diff --git a/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs b/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs index eacb8e27..8a04d370 100644 --- a/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs +++ b/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs @@ -15,7 +15,7 @@ namespace PluralKit.Bot { var text = ctx.PeekArgument(); if (text.TryParseMention(out var id)) - return await ctx.Shard.GetUserAsync(id); + return await ctx.Shard.GetUser(id); return null; } @@ -116,25 +116,14 @@ namespace PluralKit.Bot public static async Task MatchChannel(this Context ctx) { - if (!MentionUtils.TryParseChannel(ctx.PeekArgument(), out var channel)) + if (!MentionUtils.TryParseChannel(ctx.PeekArgument(), out var id)) return null; - - try - { - var discordChannel = await ctx.Shard.GetChannelAsync(channel); - if (discordChannel.Type != ChannelType.Text) return null; - - ctx.PopArgument(); - return discordChannel; - } - catch (NotFoundException) - { - return null; - } - catch (UnauthorizedException) - { - return null; - } + + var channel = await ctx.Shard.GetChannel(id); + if (channel == null || channel.Type != ChannelType.Text) return null; + + ctx.PopArgument(); + return channel; } } } \ No newline at end of file diff --git a/PluralKit.Bot/Commands/CommandTree.cs b/PluralKit.Bot/Commands/CommandTree.cs index fbb69720..3d42e5f9 100644 --- a/PluralKit.Bot/Commands/CommandTree.cs +++ b/PluralKit.Bot/Commands/CommandTree.cs @@ -355,16 +355,11 @@ namespace PluralKit.Bot { // Try to resolve the user ID to find the associated account, // so we can print their username. - try - { - var user = await ctx.Rest.GetUserAsync(id); + var user = await ctx.Shard.GetUser(id); + if (user != null) return $"Account **{user.Username}#{user.Discriminator}** does not have a system registered."; - } - catch (NotFoundException) - { - // User not found, just return ID + else return $"Account with ID `{id}` not found."; - } } return $"System with ID `{input}` not found."; diff --git a/PluralKit.Bot/Commands/Misc.cs b/PluralKit.Bot/Commands/Misc.cs index 8229c576..9ca25343 100644 --- a/PluralKit.Bot/Commands/Misc.cs +++ b/PluralKit.Bot/Commands/Misc.cs @@ -106,13 +106,13 @@ namespace PluralKit.Bot { 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.GetGuildAsync(guildId); + guild = await ctx.Rest.GetGuild(guildId); if (guild == 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.GetMemberAsync(ctx.Author.Id); + 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."); diff --git a/PluralKit.Bot/Handlers/ReactionAdded.cs b/PluralKit.Bot/Handlers/ReactionAdded.cs index a7fde738..4f452ff6 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -86,7 +86,7 @@ namespace PluralKit.Bot private async ValueTask HandleQueryReaction(MessageReactionAddEventArgs evt, FullMessage msg) { // Try to DM the user info about the message - var member = await evt.Guild.GetMemberAsync(evt.User.Id); + var member = await evt.Guild.GetMember(evt.User.Id); try { await member.SendMessageAsync(embed: await _embeds.CreateMemberEmbed(msg.System, msg.Member, evt.Guild, LookupContext.ByNonOwner)); @@ -105,9 +105,9 @@ namespace PluralKit.Bot // Check if the "pinger" has permission to send messages in this channel // (if not, PK shouldn't send messages on their behalf) - var guildUser = await evt.Guild.GetMemberAsync(evt.User.Id); + var guildUser = await evt.Guild.GetMember(evt.User.Id); var requiredPerms = Permissions.AccessChannels | Permissions.SendMessages; - if ((guildUser.PermissionsIn(evt.Channel) & requiredPerms) != requiredPerms) return; + if (guildUser == null || (guildUser.PermissionsIn(evt.Channel) & requiredPerms) != requiredPerms) return; if (msg.System.PingsEnabled) { diff --git a/PluralKit.Bot/Services/EmbedService.cs b/PluralKit.Bot/Services/EmbedService.cs index 55ef3ebf..7007d7df 100644 --- a/PluralKit.Bot/Services/EmbedService.cs +++ b/PluralKit.Bot/Services/EmbedService.cs @@ -34,7 +34,7 @@ namespace PluralKit.Bot { // Fetch/render info for all accounts simultaneously var accounts = await conn.GetLinkedAccounts(system.Id); - var users = await Task.WhenAll(accounts.Select(async uid => (await client.GetUserAsync(uid))?.NameAndMention() ?? $"(deleted account {uid})")); + var users = await Task.WhenAll(accounts.Select(async uid => (await client.GetUser(uid))?.NameAndMention() ?? $"(deleted account {uid})")); var memberCount = await conn.GetSystemMemberCount(system.Id, PrivacyLevel.Public); var eb = new DiscordEmbedBuilder() @@ -159,18 +159,18 @@ namespace PluralKit.Bot { { var ctx = LookupContext.ByNonOwner; - var channel = await client.GetChannelAsync(msg.Message.Channel); - var serverMsg = channel != null ? await channel.GetMessageAsync(msg.Message.Mid) : null; + var channel = await client.GetChannel(msg.Message.Channel); + var serverMsg = channel != null ? await channel.GetMessage(msg.Message.Mid) : null; // Need this whole dance to handle cases where: // - the user is deleted (userInfo == null) // - the bot's no longer in the server we're querying (channel == null) // - the member is no longer in the server we're querying (memberInfo == null) DiscordMember memberInfo = null; - DiscordUser userInfo = null; - if (channel != null) try { memberInfo = await channel.Guild.GetMemberAsync(msg.Message.Sender); } catch (NotFoundException) { } + DiscordUser userInfo = null; + if (channel != null) memberInfo = await channel.Guild.GetMember(msg.Message.Sender); if (memberInfo != null) userInfo = memberInfo; // Don't do an extra request if we already have this info from the member lookup - else try { userInfo = await client.GetUserAsync(msg.Message.Sender); } catch (NotFoundException) { } + else userInfo = await client.GetUser(msg.Message.Sender); // Calculate string displayed under "Sent by" string userStr; diff --git a/PluralKit.Bot/Services/LogChannelService.cs b/PluralKit.Bot/Services/LogChannelService.cs index 4631a24a..c75ebb48 100644 --- a/PluralKit.Bot/Services/LogChannelService.cs +++ b/PluralKit.Bot/Services/LogChannelService.cs @@ -50,11 +50,9 @@ namespace PluralKit.Bot { private async Task FindLogChannel(ulong guild, ulong channel) { - try - { - return await _rest.GetChannelAsync(channel); - } - catch (Exception e) when (e is NotFoundException || e is UnauthorizedException) + var obj = await _rest.GetChannel(channel); + + 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); @@ -63,7 +61,7 @@ namespace PluralKit.Bot { new {Guild = guild}); } - return null; + return obj; } } } \ No newline at end of file diff --git a/PluralKit.Bot/Services/WebhookCacheService.cs b/PluralKit.Bot/Services/WebhookCacheService.cs index d1a65ca4..acf3126f 100644 --- a/PluralKit.Bot/Services/WebhookCacheService.cs +++ b/PluralKit.Bot/Services/WebhookCacheService.cs @@ -33,7 +33,7 @@ namespace PluralKit.Bot public async Task GetWebhook(DiscordClient client, ulong channelId) { - var channel = await client.GetChannelAsync(channelId); + var channel = await client.GetChannel(channelId); if (channel == null) return null; if (channel.Type == ChannelType.Text) return null; return await GetWebhook(channel); diff --git a/PluralKit.Bot/Utils/DiscordUtils.cs b/PluralKit.Bot/Utils/DiscordUtils.cs index a470c43d..1343ad3a 100644 --- a/PluralKit.Bot/Utils/DiscordUtils.cs +++ b/PluralKit.Bot/Utils/DiscordUtils.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using DSharpPlus; using DSharpPlus.Entities; +using DSharpPlus.Exceptions; using NodaTime; @@ -22,6 +23,10 @@ namespace PluralKit.Bot public static DiscordColor Gray = new DiscordColor(0x979c9f); public static Permissions DM_PERMISSIONS = (Permissions) 0b00000_1000110_1011100110000_000000; + + private static readonly Regex USER_MENTION = new Regex("<@!?(\\d{17,19})>"); + private static readonly Regex ROLE_MENTION = new Regex("<@&(\\d{17,19})>"); + private static readonly Regex EVERYONE_HERE_MENTION = new Regex("@(everyone|here)"); private static readonly FieldInfo _roleIdsField = typeof(DiscordMember).GetField("_role_ids", BindingFlags.NonPublic | BindingFlags.Instance); @@ -58,7 +63,12 @@ namespace PluralKit.Bot // Just delegates to PermissionsInSync, but handles the case of a non-member User in a guild properly // This is a separate method because it requires an async call if (channel.Guild != null && !(user is DiscordMember)) - return PermissionsInSync(channel, await channel.Guild.GetMemberAsync(user.Id)); + { + var member = await channel.Guild.GetMember(user.Id); + if (member != null) + return PermissionsInSync(channel, member); + } + return PermissionsInSync(channel, user); } @@ -128,9 +138,6 @@ namespace PluralKit.Bot return cache != null && cache.TryGetValue(id, out user); } - private static readonly Regex USER_MENTION = new Regex("<@!?(\\d{17,19})>"); - private static readonly Regex ROLE_MENTION = new Regex("<@&(\\d{17,19})>"); - private static readonly Regex EVERYONE_HERE_MENTION = new Regex("@(everyone|here)"); public static DiscordColor? ToDiscordColor(this string color) { if (int.TryParse(color, NumberStyles.HexNumber, null, out var colorInt)) @@ -184,5 +191,62 @@ namespace PluralKit.Bot if (input != null) return pattern.Replace(input, @"\$&"); else return input; } + + public static Task GetUser(this DiscordRestClient client, ulong id) => + WrapDiscordCall(client.GetUserAsync(id)); + + public static Task GetUser(this DiscordClient client, ulong id) => + WrapDiscordCall(client.GetUserAsync(id)); + + public static Task GetChannel(this DiscordRestClient client, ulong id) => + WrapDiscordCall(client.GetChannelAsync(id)); + + public static Task GetChannel(this DiscordClient client, ulong id) => + WrapDiscordCall(client.GetChannelAsync(id)); + + public static Task GetGuild(this DiscordRestClient client, ulong id) => + WrapDiscordCall(client.GetGuildAsync(id)); + + public static Task GetGuild(this DiscordClient client, ulong id) => + WrapDiscordCall(client.GetGuildAsync(id)); + + public static Task GetMember(this DiscordRestClient client, ulong guild, ulong user) + { + async Task Inner() => + await (await client.GetGuildAsync(guild)).GetMemberAsync(user); + return WrapDiscordCall(Inner()); + } + public static Task GetMember(this DiscordClient client, ulong guild, ulong user) + { + async Task Inner() => + await (await client.GetGuildAsync(guild)).GetMemberAsync(user); + return WrapDiscordCall(Inner()); + } + + public static Task GetMember(this DiscordGuild guild, ulong user) => + WrapDiscordCall(guild.GetMemberAsync(user)); + + public static Task GetMessage(this DiscordChannel channel, ulong id) => + WrapDiscordCall(channel.GetMessageAsync(id)); + + public static Task GetMessage(this DiscordRestClient client, ulong channel, ulong message) => + WrapDiscordCall(client.GetMessageAsync(channel, message)); + + private static async Task WrapDiscordCall(Task t) + where T: class + { + try + { + return await t; + } + catch (NotFoundException) + { + return null; + } + catch (UnauthorizedException) + { + return null; + } + } } } \ No newline at end of file