Merge branch 'main' into feature/groups
This commit is contained in:
@@ -90,7 +90,7 @@ namespace PluralKit.Bot
|
||||
// We're not actually properly disconnecting from the gateway (lol) so it'll linger for a few minutes
|
||||
// Should be plenty of time for the bot to connect again next startup and set the real status
|
||||
if (_hasReceivedReady)
|
||||
await _client.UpdateStatusAsync(new DiscordActivity("Restarting... (please wait)"));
|
||||
await _client.UpdateStatusAsync(new DiscordActivity("Restarting... (please wait)"), UserStatus.Idle);
|
||||
}
|
||||
|
||||
private Task HandleEvent<T>(T evt) where T: DiscordEventArgs
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace PluralKit.Bot
|
||||
await ctx.Execute<System>(SystemInfo, m => m.Query(ctx, ctx.System));
|
||||
|
||||
// First, we match own-system-only commands (ie. no target system parameter)
|
||||
else if (ctx.Match("new", "create", "make", "add", "register", "init"))
|
||||
else if (ctx.Match("new", "create", "make", "add", "register", "init", "n"))
|
||||
await ctx.Execute<System>(SystemNew, m => m.New(ctx));
|
||||
else if (ctx.Match("name", "rename", "changename"))
|
||||
await ctx.Execute<SystemEdit>(SystemRename, m => m.Name(ctx));
|
||||
|
||||
@@ -40,7 +40,8 @@ namespace PluralKit.Bot
|
||||
if (ctx.MatchFlag("by-last-fronted", "by-last-front", "by-last-switch", "blf", "bls")) p.SortProperty = SortProperty.LastSwitch;
|
||||
if (ctx.MatchFlag("by-last-message", "blm", "blp")) p.SortProperty = SortProperty.LastMessage;
|
||||
if (ctx.MatchFlag("by-birthday", "by-birthdate", "bbd")) p.SortProperty = SortProperty.Birthdate;
|
||||
|
||||
if (ctx.MatchFlag("random")) p.SortProperty = SortProperty.Random;
|
||||
|
||||
// Sort reverse?
|
||||
if (ctx.MatchFlag("r", "rev", "reverse"))
|
||||
p.Reverse = true;
|
||||
@@ -136,7 +137,7 @@ namespace PluralKit.Bot
|
||||
profile.Append($"\n**Birthdate**: {m.BirthdayString}");
|
||||
|
||||
if (m.ProxyTags.Count > 0)
|
||||
profile.Append($"\n**Proxy tags:** {m.ProxyTagsString()}");
|
||||
profile.Append($"\n**Proxy tags**: {m.ProxyTagsString()}");
|
||||
|
||||
if (opts.IncludeMessageCount && m.MessageCountFor(lookupCtx) is {} count && count > 0)
|
||||
profile.Append($"\n**Message count:** {count}");
|
||||
@@ -161,4 +162,4 @@ namespace PluralKit.Bot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace PluralKit.Bot
|
||||
public string CreateFilterString()
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
str.Append("Sorting by ");
|
||||
str.Append("Sorting ");
|
||||
if (SortProperty != SortProperty.Random) str.Append("by ");
|
||||
str.Append(SortProperty switch
|
||||
{
|
||||
SortProperty.Name => "member name",
|
||||
@@ -40,6 +41,7 @@ namespace PluralKit.Bot
|
||||
SortProperty.LastSwitch => "last switch",
|
||||
SortProperty.MessageCount => "message count",
|
||||
SortProperty.Birthdate => "birthday",
|
||||
SortProperty.Random => "randomly",
|
||||
_ => new ArgumentOutOfRangeException($"Couldn't find readable string for sort property {SortProperty}")
|
||||
});
|
||||
|
||||
@@ -77,6 +79,8 @@ namespace PluralKit.Bot
|
||||
IComparer<T> ReverseMaybe<T>(IComparer<T> c) =>
|
||||
opts.Reverse ? Comparer<T>.Create((a, b) => c.Compare(b, a)) : c;
|
||||
|
||||
var randGen = new global::System.Random();
|
||||
|
||||
var culture = StringComparer.InvariantCultureIgnoreCase;
|
||||
return (opts.SortProperty switch
|
||||
{
|
||||
@@ -98,6 +102,8 @@ namespace PluralKit.Bot
|
||||
SortProperty.LastSwitch => input
|
||||
.OrderByDescending(m => m.LastSwitchTime.HasValue)
|
||||
.ThenByDescending(m => m.LastSwitchTime, ReverseMaybe(Comparer<Instant?>.Default)),
|
||||
SortProperty.Random => input
|
||||
.OrderBy(m => randGen.Next()),
|
||||
_ => throw new ArgumentOutOfRangeException($"Unknown sort property {opts.SortProperty}")
|
||||
})
|
||||
// Lastly, add a by-name fallback order for collisions (generally hits w/ lots of null values)
|
||||
@@ -114,7 +120,8 @@ namespace PluralKit.Bot
|
||||
CreationDate,
|
||||
LastSwitch,
|
||||
LastMessage,
|
||||
Birthdate
|
||||
Birthdate,
|
||||
Random
|
||||
}
|
||||
|
||||
public enum ListType
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
@@ -61,7 +62,11 @@ namespace PluralKit.Bot
|
||||
//Maybe move this somewhere else in the file structure since it doesn't need to get created at every command
|
||||
|
||||
// TODO: don't buffer these, find something else to do ig
|
||||
var members = await _data.GetSystemMembers(ctx.System).Where(m => m.MemberVisibility == PrivacyLevel.Public).ToListAsync();
|
||||
|
||||
List<PKMember> members;
|
||||
if (ctx.MatchFlag("all", "a")) members = await _data.GetSystemMembers(ctx.System).ToListAsync();
|
||||
else members = await _data.GetSystemMembers(ctx.System).Where(m => m.MemberVisibility == PrivacyLevel.Public).ToListAsync();
|
||||
|
||||
if (members == null || !members.Any())
|
||||
throw Errors.NoMembersError;
|
||||
var randInt = randGen.Next(members.Count);
|
||||
|
||||
@@ -36,8 +36,8 @@ namespace PluralKit.Bot
|
||||
|
||||
// Warn if there's already a member by this name
|
||||
var existingMember = await _data.GetMemberByName(ctx.System, newName);
|
||||
if (existingMember != null)
|
||||
if (existingMember.Id != target.Id) {
|
||||
if (existingMember != null && existingMember.Id != target.Id)
|
||||
{
|
||||
var msg = $"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (`{existingMember.Hid}`). Do you want to rename this member to that name too?";
|
||||
if (!await ctx.PromptYesNo(msg)) throw new PKError("Member renaming cancelled.");
|
||||
}
|
||||
@@ -187,7 +187,7 @@ namespace PluralKit.Bot
|
||||
await ctx.Reply(embed: new DiscordEmbedBuilder()
|
||||
.WithTitle($"{Emojis.Success} Member color changed.")
|
||||
.WithColor(color.ToDiscordColor().Value)
|
||||
.WithThumbnail($"https://fakeimg.pl/256x256/{target.Color}/?text=%20")
|
||||
.WithThumbnail($"https://fakeimg.pl/256x256/{color}/?text=%20")
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,27 +94,23 @@ namespace PluralKit.Bot {
|
||||
public async Task PermCheckGuild(Context ctx)
|
||||
{
|
||||
DiscordGuild guild;
|
||||
DiscordMember senderGuildUser = null;
|
||||
|
||||
if (ctx.Guild != null && !ctx.HasNext())
|
||||
{
|
||||
guild = ctx.Guild;
|
||||
senderGuildUser = (DiscordMember)ctx.Author;
|
||||
}
|
||||
else
|
||||
{
|
||||
var guildIdStr = ctx.RemainderOrNull() ?? throw new PKSyntaxError("You must pass a server ID or run this command as .");
|
||||
var guildIdStr = ctx.RemainderOrNull() ?? throw new PKSyntaxError("You must pass a server ID or run this command in a server.");
|
||||
if (!ulong.TryParse(guildIdStr, out var guildId))
|
||||
throw new PKSyntaxError($"Could not parse `{guildIdStr}` as an ID.");
|
||||
|
||||
// TODO: will this call break for sharding if you try to request a guild on a different bot instance?
|
||||
guild = await ctx.Rest.GetGuild(guildId);
|
||||
if (guild == null)
|
||||
throw Errors.GuildNotFound(guildId);
|
||||
guild = ctx.Client.GetGuild(guildId);
|
||||
if (guild != null) senderGuildUser = await guild.GetMember(ctx.Author.Id);
|
||||
if (guild == null || senderGuildUser == null) throw Errors.GuildNotFound(guildId);
|
||||
}
|
||||
|
||||
// Ensure people can't query guilds they're not in + get their own permissions (for view access checking)
|
||||
var senderGuildUser = await guild.GetMember(ctx.Author.Id);
|
||||
if (senderGuildUser == null)
|
||||
throw new PKError("You must be a member of the guild you are querying.");
|
||||
|
||||
var requiredPermissions = new []
|
||||
{
|
||||
|
||||
@@ -25,9 +25,11 @@ namespace PluralKit.Bot
|
||||
ctx.CheckGuildContext().CheckAuthorPermission(Permissions.ManageGuild, "Manage Server");
|
||||
|
||||
DiscordChannel channel = null;
|
||||
if (ctx.HasNext())
|
||||
channel = await ctx.MatchChannel() ?? throw new PKSyntaxError("You must pass a #channel to set.");
|
||||
if (channel != null && channel.GuildId != ctx.Guild.Id) throw new PKError("That channel is not in this server!");
|
||||
if (!ctx.HasNext())
|
||||
throw new PKSyntaxError("You must pass a #channel to set.");
|
||||
var channelString = ctx.PeekArgument();
|
||||
channel = await ctx.MatchChannel();
|
||||
if (channel == null || channel.GuildId != ctx.Guild.Id) throw Errors.ChannelNotFound(channelString);
|
||||
|
||||
var patch = new GuildPatch {LogChannel = channel?.Id};
|
||||
await _db.Execute(conn => conn.UpsertGuild(ctx.Guild.Id, patch));
|
||||
@@ -48,8 +50,9 @@ namespace PluralKit.Bot
|
||||
else if (!ctx.HasNext()) throw new PKSyntaxError("You must pass one or more #channels.");
|
||||
else while (ctx.HasNext())
|
||||
{
|
||||
var channel = await ctx.MatchChannel() ?? throw new PKSyntaxError($"Channel \"{ctx.PopArgument()}\" not found.");
|
||||
if (channel.GuildId != ctx.Guild.Id) throw new PKError($"Channel {ctx.Guild.Id} is not in this server.");
|
||||
var channelString = ctx.PeekArgument();
|
||||
var channel = await ctx.MatchChannel();
|
||||
if (channel == null || channel.GuildId != ctx.Guild.Id) throw Errors.ChannelNotFound(channelString);
|
||||
affectedChannels.Add(channel);
|
||||
}
|
||||
|
||||
@@ -127,8 +130,9 @@ namespace PluralKit.Bot
|
||||
else if (!ctx.HasNext()) throw new PKSyntaxError("You must pass one or more #channels.");
|
||||
else while (ctx.HasNext())
|
||||
{
|
||||
var channel = await ctx.MatchChannel() ?? throw new PKSyntaxError($"Channel \"{ctx.PopArgument()}\" not found.");
|
||||
if (channel.GuildId != ctx.Guild.Id) throw new PKError($"Channel {ctx.Guild.Id} is not in this server.");
|
||||
var channelString = ctx.PeekArgument();
|
||||
var channel = await ctx.MatchChannel();
|
||||
if (channel == null || channel.GuildId != ctx.Guild.Id) throw Errors.ChannelNotFound(channelString);
|
||||
affectedChannels.Add(channel);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ namespace PluralKit.Bot
|
||||
{
|
||||
ctx.CheckNoSystem();
|
||||
|
||||
var system = await _data.CreateSystem(ctx.RemainderOrNull());
|
||||
var systemName = ctx.RemainderOrNull();
|
||||
if (systemName != null && systemName.Length > Limits.MaxSystemNameLength)
|
||||
throw Errors.SystemNameTooLongError(systemName.Length);
|
||||
|
||||
var system = await _data.CreateSystem(systemName);
|
||||
await _data.AddAccount(system, ctx.Author.Id);
|
||||
await ctx.Reply($"{Emojis.Success} Your system has been created. Type `pk;system` to view it, and type `pk;system help` for more information about commands you can use now. Now that you have that set up, check out the getting started guide on setting up members and proxies: <https://pluralkit.me/start>");
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace PluralKit.Bot {
|
||||
public static PKError DurationParseError(string durationStr) => new PKError($"Could not parse '{durationStr}' as a valid duration. Try a format such as `30d`, `1d3h` or `20m30s`.");
|
||||
public static PKError FrontPercentTimeInFuture => new PKError("Cannot get the front percent between now and a time in the future.");
|
||||
|
||||
public static PKError GuildNotFound(ulong guildId) => new PKError($"Guild with ID {guildId} not found.");
|
||||
public static PKError GuildNotFound(ulong guildId) => new PKError($"Guild with ID {guildId} not found. Note that you must be a member of the guild you are querying.");
|
||||
|
||||
public static PKError DisplayNameTooLong(string displayName, int maxLength) => new PKError(
|
||||
$"Display name too long ({displayName.Length} > {maxLength} characters). Use a shorter display name, or shorten your system tag.");
|
||||
@@ -115,5 +115,6 @@ namespace PluralKit.Bot {
|
||||
|
||||
public static PKError AttachmentTooLarge => new PKError("PluralKit cannot proxy attachments over 8 megabytes (as webhooks aren't considered as having Discord Nitro) :(");
|
||||
public static PKError LookupNotAllowed => new PKError("You do not have permission to access this information.");
|
||||
public static PKError ChannelNotFound(string channelString) => new PKError($"Channel \"{channelString}\" not found or is not in this server.");
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,14 @@ namespace PluralKit.Bot {
|
||||
if (logChannel == null || logChannel.Type != ChannelType.Text) return;
|
||||
|
||||
// Check bot permissions
|
||||
if (!trigger.Channel.BotHasAllPermissions(Permissions.SendMessages | Permissions.EmbedLinks)) return;
|
||||
|
||||
if (!trigger.Channel.BotHasAllPermissions(Permissions.SendMessages | Permissions.EmbedLinks))
|
||||
{
|
||||
_logger.Information(
|
||||
"Does not have permission to proxy log, ignoring (channel: {ChannelId}, guild: {GuildId}, bot permissions: {BotPermissions})",
|
||||
ctx.LogChannel.Value, trigger.Channel.GuildId, trigger.Channel.BotPermissions());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send embed!
|
||||
await using var conn = await _db.Obtain();
|
||||
var embed = _embed.CreateLoggedMessageEmbed(await conn.QuerySystem(ctx.SystemId.Value),
|
||||
@@ -55,7 +61,7 @@ namespace PluralKit.Bot {
|
||||
if (obj == null)
|
||||
{
|
||||
// Channel doesn't exist or we don't have permission to access it, let's remove it from the database too
|
||||
_logger.Warning("Attempted to fetch missing log channel {LogChannel}, removing from database", channel);
|
||||
_logger.Warning("Attempted to fetch missing log channel {LogChannel} for guild {Guild}, removing from database", channel, guild);
|
||||
await using var conn = await _db.Obtain();
|
||||
await conn.ExecuteAsync("update servers set log_channel = null where id = @Guild",
|
||||
new {Guild = guild});
|
||||
|
||||
@@ -67,7 +67,8 @@ namespace PluralKit.Bot
|
||||
dwb.WithUsername(FixClyde(name).Truncate(80));
|
||||
dwb.WithContent(content);
|
||||
dwb.AddMentions(content.ParseAllMentions(allowEveryone, channel.Guild));
|
||||
if (avatarUrl != null) dwb.WithAvatarUrl(avatarUrl);
|
||||
if (!string.IsNullOrWhiteSpace(avatarUrl))
|
||||
dwb.WithAvatarUrl(avatarUrl);
|
||||
|
||||
var attachmentChunks = ChunkAttachmentsOrThrow(attachments, 8 * 1024 * 1024);
|
||||
if (attachmentChunks.Count > 0)
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace PluralKit.Bot
|
||||
public static string EscapeBacktickPair(this string input){
|
||||
Regex doubleBacktick = new Regex(@"``", RegexOptions.Multiline);
|
||||
//Run twice to catch any pairs that are created from the first pass, pairs shouldn't be created in the second as they are created from odd numbers of backticks, even numbers are all caught on the first pass
|
||||
if(input != null) return doubleBacktick.Replace(doubleBacktick.Replace(input, @"` `"),@"` `");
|
||||
if(input != null) return doubleBacktick.Replace(doubleBacktick.Replace(input, "`\ufeff`"),"`\ufeff`");
|
||||
else return input;
|
||||
}
|
||||
|
||||
@@ -271,8 +271,8 @@ namespace PluralKit.Bot
|
||||
{
|
||||
// we need to know the channel's guild ID to get the cached guild object, so we grab it from the API
|
||||
if (guildId == null) {
|
||||
var guild = await WrapDiscordCall(client.ShardClients.Values.FirstOrDefault().GetChannelAsync(id));
|
||||
if (guild != null) guildId = guild.Id;
|
||||
var channel = await WrapDiscordCall(client.ShardClients.Values.FirstOrDefault().GetChannelAsync(id));
|
||||
if (channel != null) guildId = channel.GuildId;
|
||||
else return null; // we probably don't have the guild in cache if the API doesn't give it to us
|
||||
}
|
||||
return client.GetGuild(guildId.Value).GetChannel(id);
|
||||
@@ -326,4 +326,4 @@ namespace PluralKit.Bot
|
||||
return $"<{match.Value}>";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user