From e7f36eb31fcb92079fdecfc8f277f88d08578029 Mon Sep 17 00:00:00 2001 From: spiral Date: Wed, 17 Nov 2021 20:41:02 -0500 Subject: [PATCH] feat: async cache this breaks logging bot permissions to Sentry. we haven't had a need to check those recently (permissions issues were because of broken cache), so this is fine for now this should be re-added in the future though --- Myriad/Cache/IDiscordCache.cs | 12 +++---- Myriad/Cache/MemoryDiscordCache.cs | 26 +++++++-------- Myriad/Extensions/CacheExtensions.cs | 32 +++++++++---------- Myriad/Extensions/PermissionExtensions.cs | 17 +++++----- PluralKit.Bot/Bot.cs | 10 +++--- PluralKit.Bot/CommandSystem/Context.cs | 10 +++--- .../CommandSystem/ContextChecksExt.cs | 6 ++-- .../ContextEntityArgumentsExt.cs | 12 +++---- PluralKit.Bot/Commands/Checks.cs | 10 +++--- PluralKit.Bot/Commands/Message.cs | 6 ++-- PluralKit.Bot/Commands/ServerConfig.cs | 32 +++++++++---------- PluralKit.Bot/Handlers/MessageCreated.cs | 10 +++--- PluralKit.Bot/Handlers/MessageEdited.cs | 8 ++--- PluralKit.Bot/Handlers/ReactionAdded.cs | 14 ++++---- PluralKit.Bot/Modules.cs | 4 ++- PluralKit.Bot/Proxy/ProxyService.cs | 8 ++--- PluralKit.Bot/Services/LogChannelService.cs | 6 ++-- PluralKit.Bot/Services/LoggerCleanService.cs | 4 +-- .../Services/PeriodicStatCollector.cs | 2 +- .../Services/WebhookExecutorService.cs | 4 +-- PluralKit.Bot/Utils/ContextUtils.cs | 6 ++-- PluralKit.Bot/Utils/DiscordUtils.cs | 4 +-- PluralKit.Bot/Utils/SentryUtils.cs | 8 +++-- .../Utils/SerilogGatewayEnricherFactory.cs | 9 +++--- 24 files changed, 134 insertions(+), 126 deletions(-) diff --git a/Myriad/Cache/IDiscordCache.cs b/Myriad/Cache/IDiscordCache.cs index 5ba90e9b..42a5a45b 100644 --- a/Myriad/Cache/IDiscordCache.cs +++ b/Myriad/Cache/IDiscordCache.cs @@ -18,13 +18,13 @@ namespace Myriad.Cache public ValueTask RemoveUser(ulong userId); public ValueTask RemoveRole(ulong guildId, ulong roleId); - public bool TryGetGuild(ulong guildId, out Guild guild); - public bool TryGetChannel(ulong channelId, out Channel channel); - public bool TryGetDmChannel(ulong userId, out Channel channel); - public bool TryGetUser(ulong userId, out User user); - public bool TryGetRole(ulong roleId, out Role role); + public Task TryGetGuild(ulong guildId, out Guild guild); + public Task TryGetChannel(ulong channelId, out Channel channel); + public Task TryGetDmChannel(ulong userId, out Channel channel); + public Task TryGetUser(ulong userId, out User user); + public Task TryGetRole(ulong roleId, out Role role); public IAsyncEnumerable GetAllGuilds(); - public IEnumerable GetGuildChannels(ulong guildId); + public Task> GetGuildChannels(ulong guildId); } } \ No newline at end of file diff --git a/Myriad/Cache/MemoryDiscordCache.cs b/Myriad/Cache/MemoryDiscordCache.cs index 4369b72c..06f920c5 100644 --- a/Myriad/Cache/MemoryDiscordCache.cs +++ b/Myriad/Cache/MemoryDiscordCache.cs @@ -124,34 +124,34 @@ namespace Myriad.Cache return default; } - public bool TryGetGuild(ulong guildId, out Guild guild) + public Task TryGetGuild(ulong guildId, out Guild guild) { if (_guilds.TryGetValue(guildId, out var cg)) { guild = cg.Guild; - return true; + return Task.FromResult(true); } guild = null!; - return false; + return Task.FromResult(false); } - public bool TryGetChannel(ulong channelId, out Channel channel) => - _channels.TryGetValue(channelId, out channel!); + public Task TryGetChannel(ulong channelId, out Channel channel) => + Task.FromResult(_channels.TryGetValue(channelId, out channel!)); - public bool TryGetDmChannel(ulong userId, out Channel channel) + public Task TryGetDmChannel(ulong userId, out Channel channel) { channel = default!; if (!_dmChannels.TryGetValue(userId, out var channelId)) - return false; + return Task.FromResult(false); return TryGetChannel(channelId, out channel); } - public bool TryGetUser(ulong userId, out User user) => - _users.TryGetValue(userId, out user!); + public Task TryGetUser(ulong userId, out User user) => + Task.FromResult(_users.TryGetValue(userId, out user!)); - public bool TryGetRole(ulong roleId, out Role role) => - _roles.TryGetValue(roleId, out role!); + public Task TryGetRole(ulong roleId, out Role role) => + Task.FromResult(_roles.TryGetValue(roleId, out role!)); public IAsyncEnumerable GetAllGuilds() { @@ -160,12 +160,12 @@ namespace Myriad.Cache .ToAsyncEnumerable(); } - public IEnumerable GetGuildChannels(ulong guildId) + public Task> GetGuildChannels(ulong guildId) { if (!_guilds.TryGetValue(guildId, out var guild)) throw new ArgumentException("Guild not found", nameof(guildId)); - return guild.Channels.Keys.Select(c => _channels[c]); + return Task.FromResult(guild.Channels.Keys.Select(c => _channels[c])); } private CachedGuild SaveGuildRaw(Guild guild) => diff --git a/Myriad/Extensions/CacheExtensions.cs b/Myriad/Extensions/CacheExtensions.cs index 02802172..a5c7343c 100644 --- a/Myriad/Extensions/CacheExtensions.cs +++ b/Myriad/Extensions/CacheExtensions.cs @@ -9,44 +9,44 @@ namespace Myriad.Extensions { public static class CacheExtensions { - public static Guild GetGuild(this IDiscordCache cache, ulong guildId) + public static async Task GetGuild(this IDiscordCache cache, ulong guildId) { - if (!cache.TryGetGuild(guildId, out var guild)) + if (!await cache.TryGetGuild(guildId, out var guild)) throw new KeyNotFoundException($"Guild {guildId} not found in cache"); return guild; } - public static Channel GetChannel(this IDiscordCache cache, ulong channelId) + public static async Task GetChannel(this IDiscordCache cache, ulong channelId) { - if (!cache.TryGetChannel(channelId, out var channel)) + if (!await cache.TryGetChannel(channelId, out var channel)) throw new KeyNotFoundException($"Channel {channelId} not found in cache"); return channel; } - public static Channel? GetChannelOrNull(this IDiscordCache cache, ulong channelId) + public static async Task GetChannelOrNull(this IDiscordCache cache, ulong channelId) { - if (cache.TryGetChannel(channelId, out var channel)) + if (await cache.TryGetChannel(channelId, out var channel)) return channel; return null; } - public static User GetUser(this IDiscordCache cache, ulong userId) + public static async Task GetUser(this IDiscordCache cache, ulong userId) { - if (!cache.TryGetUser(userId, out var user)) + if (!await cache.TryGetUser(userId, out var user)) throw new KeyNotFoundException($"User {userId} not found in cache"); return user; } - public static Role GetRole(this IDiscordCache cache, ulong roleId) + public static async Task GetRole(this IDiscordCache cache, ulong roleId) { - if (!cache.TryGetRole(roleId, out var role)) + if (!await cache.TryGetRole(roleId, out var role)) throw new KeyNotFoundException($"Role {roleId} not found in cache"); return role; } public static async ValueTask GetOrFetchUser(this IDiscordCache cache, DiscordApiClient rest, ulong userId) { - if (cache.TryGetUser(userId, out var cacheUser)) + if (await cache.TryGetUser(userId, out var cacheUser)) return cacheUser; var restUser = await rest.GetUser(userId); @@ -57,7 +57,7 @@ namespace Myriad.Extensions public static async ValueTask GetOrFetchChannel(this IDiscordCache cache, DiscordApiClient rest, ulong channelId) { - if (cache.TryGetChannel(channelId, out var cacheChannel)) + if (await cache.TryGetChannel(channelId, out var cacheChannel)) return cacheChannel; var restChannel = await rest.GetChannel(channelId); @@ -68,7 +68,7 @@ namespace Myriad.Extensions public static async Task GetOrCreateDmChannel(this IDiscordCache cache, DiscordApiClient rest, ulong recipientId) { - if (cache.TryGetDmChannel(recipientId, out var cacheChannel)) + if (await cache.TryGetDmChannel(recipientId, out var cacheChannel)) return cacheChannel; var restChannel = await rest.CreateDm(recipientId); @@ -76,13 +76,13 @@ namespace Myriad.Extensions return restChannel; } - public static Channel GetRootChannel(this IDiscordCache cache, ulong channelOrThread) + public static async Task GetRootChannel(this IDiscordCache cache, ulong channelOrThread) { - var channel = cache.GetChannel(channelOrThread); + var channel = await cache.GetChannel(channelOrThread); if (!channel.IsThread()) return channel; - var parent = cache.GetChannel(channel.ParentId!.Value); + var parent = await cache.GetChannel(channel.ParentId!.Value); return parent; } } diff --git a/Myriad/Extensions/PermissionExtensions.cs b/Myriad/Extensions/PermissionExtensions.cs index fa614ba3..abfc2b7a 100644 --- a/Myriad/Extensions/PermissionExtensions.cs +++ b/Myriad/Extensions/PermissionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Myriad.Cache; using Myriad.Gateway; @@ -10,24 +11,24 @@ namespace Myriad.Extensions { public static class PermissionExtensions { - public static PermissionSet PermissionsFor(this IDiscordCache cache, MessageCreateEvent message) => + public static Task PermissionsFor(this IDiscordCache cache, MessageCreateEvent message) => PermissionsFor(cache, message.ChannelId, message.Author.Id, message.Member, isWebhook: message.WebhookId != null); - public static PermissionSet PermissionsFor(this IDiscordCache cache, ulong channelId, GuildMember member) => + public static Task PermissionsFor(this IDiscordCache cache, ulong channelId, GuildMember member) => PermissionsFor(cache, channelId, member.User.Id, member); - public static PermissionSet PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId, GuildMemberPartial? member, bool isWebhook = false) + public static async Task PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId, GuildMemberPartial? member, bool isWebhook = false) { - if (!cache.TryGetChannel(channelId, out var channel)) + if (!await cache.TryGetChannel(channelId, out var channel)) // todo: handle channel not found better return PermissionSet.Dm; if (channel.GuildId == null) return PermissionSet.Dm; - var rootChannel = cache.GetRootChannel(channelId); + var rootChannel = await cache.GetRootChannel(channelId); - var guild = cache.GetGuild(channel.GuildId.Value); + var guild = await cache.GetGuild(channel.GuildId.Value); if (isWebhook) return EveryonePermissions(guild); @@ -38,12 +39,12 @@ namespace Myriad.Extensions public static PermissionSet EveryonePermissions(this Guild guild) => guild.Roles.FirstOrDefault(r => r.Id == guild.Id)?.Permissions ?? PermissionSet.Dm; - public static PermissionSet EveryonePermissions(this IDiscordCache cache, Channel channel) + public static async Task EveryonePermissions(this IDiscordCache cache, Channel channel) { if (channel.Type == Channel.ChannelType.Dm) return PermissionSet.Dm; - var defaultPermissions = cache.GetGuild(channel.GuildId!.Value).EveryonePermissions(); + var defaultPermissions = (await cache.GetGuild(channel.GuildId!.Value)).EveryonePermissions(); var overwrite = channel.PermissionOverwrites?.FirstOrDefault(r => r.Id == channel.GuildId); if (overwrite == null) return defaultPermissions; diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index c3345eb1..303e6df9 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -78,14 +78,14 @@ namespace PluralKit.Bot }, null, timeTillNextWholeMinute, TimeSpan.FromMinutes(1)); } - public PermissionSet PermissionsIn(ulong channelId) + public async Task PermissionsIn(ulong channelId) { - var channel = _cache.GetRootChannel(channelId); + var channel = await _cache.GetRootChannel(channelId); if (channel.GuildId != null) { var member = _guildMembers.GetValueOrDefault(channel.GuildId.Value); - return _cache.PermissionsFor(channelId, _cluster.User?.Id ?? default, member); + return await _cache.PermissionsFor(channelId, _cluster.User?.Id ?? default, member); } return PermissionSet.Dm; @@ -198,7 +198,7 @@ namespace PluralKit.Bot var queue = serviceScope.ResolveOptional>(); using var _ = LogContext.PushProperty("EventId", Guid.NewGuid()); - using var __ = LogContext.Push(serviceScope.Resolve().GetEnricher(shard, evt)); + using var __ = LogContext.Push(await serviceScope.Resolve().GetEnricher(shard, evt)); _logger.Verbose("Received gateway event: {@Event}", evt); // Also, find a Sentry enricher for the event type (if one is present), and ask it to put some event data in the Sentry scope @@ -263,7 +263,7 @@ namespace PluralKit.Bot if (reportChannel == null) return; - var botPerms = PermissionsIn(reportChannel.Value); + var botPerms = await PermissionsIn(reportChannel.Value); if (botPerms.HasFlag(PermissionSet.SendMessages | PermissionSet.EmbedLinks)) await _errorMessageService.SendErrorMessage(reportChannel.Value, sentryEvent.EventId.ToString()); } diff --git a/PluralKit.Bot/CommandSystem/Context.cs b/PluralKit.Bot/CommandSystem/Context.cs index db1b6975..2fc43f76 100644 --- a/PluralKit.Bot/CommandSystem/Context.cs +++ b/PluralKit.Bot/CommandSystem/Context.cs @@ -71,8 +71,8 @@ namespace PluralKit.Bot public Cluster Cluster => _cluster; public MessageContext MessageContext => _messageContext; - public PermissionSet BotPermissions => _provider.Resolve().PermissionsIn(_channel.Id); - public PermissionSet UserPermissions => _cache.PermissionsFor(_message); + public Task BotPermissions => _provider.Resolve().PermissionsIn(_channel.Id); + public Task UserPermissions => _cache.PermissionsFor(_message); public DiscordApiClient Rest => _rest; @@ -85,11 +85,13 @@ namespace PluralKit.Bot public async Task Reply(string text = null, Embed embed = null, AllowedMentions? mentions = null) { - if (!BotPermissions.HasFlag(PermissionSet.SendMessages)) + var botPerms = await BotPermissions; + + if (!botPerms.HasFlag(PermissionSet.SendMessages)) // Will be "swallowed" during the error handler anyway, this message is never shown. throw new PKError("PluralKit does not have permission to send messages in this channel."); - if (embed != null && !BotPermissions.HasFlag(PermissionSet.EmbedLinks)) + if (embed != null && !botPerms.HasFlag(PermissionSet.EmbedLinks)) throw new PKError("PluralKit does not have permission to send embeds in this channel. Please ensure I have the **Embed Links** permission enabled."); var msg = await _rest.CreateMessage(_channel.Id, new MessageRequest diff --git a/PluralKit.Bot/CommandSystem/ContextChecksExt.cs b/PluralKit.Bot/CommandSystem/ContextChecksExt.cs index 0a300e58..dacfaa76 100644 --- a/PluralKit.Bot/CommandSystem/ContextChecksExt.cs +++ b/PluralKit.Bot/CommandSystem/ContextChecksExt.cs @@ -52,16 +52,16 @@ namespace PluralKit.Bot return ctx; } - public static Context CheckAuthorPermission(this Context ctx, PermissionSet neededPerms, string permissionName) + public static async Task CheckAuthorPermission(this Context ctx, PermissionSet neededPerms, string permissionName) { - if ((ctx.UserPermissions & neededPerms) != neededPerms) + if ((await ctx.UserPermissions & neededPerms) != neededPerms) throw new PKError($"You must have the \"{permissionName}\" permission in this server to use this command."); return ctx; } public static async Task CheckPermissionsInGuildChannel(this Context ctx, Channel channel, PermissionSet neededPerms) { - var guild = ctx.Cache.GetGuild(channel.GuildId.Value); + var guild = await ctx.Cache.GetGuild(channel.GuildId.Value); if (guild == null) return false; diff --git a/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs b/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs index 3c0e8e45..ebfd187c 100644 --- a/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs +++ b/PluralKit.Bot/CommandSystem/ContextEntityArgumentsExt.cs @@ -152,19 +152,19 @@ namespace PluralKit.Bot } } - public static Task MatchChannel(this Context ctx) + public static async Task MatchChannel(this Context ctx) { if (!MentionUtils.TryParseChannel(ctx.PeekArgument(), out var id)) - return Task.FromResult(null); + return null; - if (!ctx.Cache.TryGetChannel(id, out var channel)) - return Task.FromResult(null); + if (!await ctx.Cache.TryGetChannel(id, out var channel)) + return null; if (!DiscordUtils.IsValidGuildChannel(channel)) - return Task.FromResult(null); + return null; ctx.PopArgument(); - return Task.FromResult(channel); + return channel; } public static Guild MatchGuild(this Context ctx) diff --git a/PluralKit.Bot/Commands/Checks.cs b/PluralKit.Bot/Commands/Checks.cs index 0d8bd6c4..86e7cedf 100644 --- a/PluralKit.Bot/Commands/Checks.cs +++ b/PluralKit.Bot/Commands/Checks.cs @@ -87,8 +87,8 @@ namespace PluralKit.Bot var missingEmojiPermissions = false; foreach (var channel in await _rest.GetGuildChannels(guild.Id)) { - var botPermissions = _bot.PermissionsIn(channel.Id); - var webhookPermissions = _cache.EveryonePermissions(channel); + var botPermissions = await _bot.PermissionsIn(channel.Id); + var webhookPermissions = await _cache.EveryonePermissions(channel); var userPermissions = PermissionExtensions.PermissionsFor(guild, channel, ctx.Author.Id, senderGuildUser); if ((userPermissions & PermissionSet.ViewChannel) == 0) @@ -176,8 +176,8 @@ namespace PluralKit.Bot if (!await ctx.CheckPermissionsInGuildChannel(channel, PermissionSet.ViewChannel)) throw new PKError(error); - var botPermissions = _bot.PermissionsIn(channel.Id); - var webhookPermissions = _cache.EveryonePermissions(channel); + var botPermissions = await _bot.PermissionsIn(channel.Id); + var webhookPermissions = await _cache.EveryonePermissions(channel); // We use a bitfield so we can set individual permission bits ulong missingPermissions = 0; @@ -249,7 +249,7 @@ namespace PluralKit.Bot throw new PKError("You can only check your own messages."); // get the channel info - var channel = _cache.GetChannel(channelId.Value); + var channel = await _cache.GetChannel(channelId.Value); if (channel == null) throw new PKError("Unable to get the channel associated with this message."); diff --git a/PluralKit.Bot/Commands/Message.cs b/PluralKit.Bot/Commands/Message.cs index 47d77e8d..99a07be8 100644 --- a/PluralKit.Bot/Commands/Message.cs +++ b/PluralKit.Bot/Commands/Message.cs @@ -67,7 +67,7 @@ namespace PluralKit.Bot if (ctx.Guild == null) await _rest.CreateReaction(ctx.Channel.Id, ctx.Message.Id, new() { Name = Emojis.Success }); - if (ctx.BotPermissions.HasFlag(PermissionSet.ManageMessages)) + if ((await ctx.BotPermissions).HasFlag(PermissionSet.ManageMessages)) await _rest.DeleteMessage(ctx.Channel.Id, ctx.Message.Id); await _logChannel.LogMessage(ctx.MessageContext, msg.Message, ctx.Message, editedMsg, originalMsg!.Content!); @@ -109,7 +109,7 @@ namespace PluralKit.Bot { var error = "The channel where the message was sent does not exist anymore, or you are missing permissions to access it."; - var channel = _cache.GetChannel(msg.Message.Channel); + var channel = await _cache.GetChannel(msg.Message.Channel); if (channel == null) throw new PKError(error); @@ -162,7 +162,7 @@ namespace PluralKit.Bot var showContent = true; var noShowContentError = "Message deleted or inaccessible."; - var channel = _cache.GetChannel(message.Message.Channel); + var channel = await _cache.GetChannel(message.Message.Channel); if (channel == null) showContent = false; else if (!await ctx.CheckPermissionsInGuildChannel(channel, PermissionSet.ViewChannel)) diff --git a/PluralKit.Bot/Commands/ServerConfig.cs b/PluralKit.Bot/Commands/ServerConfig.cs index 3640d4dd..847c445d 100644 --- a/PluralKit.Bot/Commands/ServerConfig.cs +++ b/PluralKit.Bot/Commands/ServerConfig.cs @@ -30,7 +30,7 @@ namespace PluralKit.Bot public async Task SetLogChannel(Context ctx) { - ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); + await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); var settings = await _repo.GetGuild(ctx.Guild.Id); if (await ctx.MatchClear("the server log channel")) @@ -59,7 +59,7 @@ namespace PluralKit.Bot if (channel.Type != Channel.ChannelType.GuildText) throw new PKError("PluralKit cannot log messages to this type of channel."); - var perms = _bot.PermissionsIn(channel.Id); + var perms = await _bot.PermissionsIn(channel.Id); if (!perms.HasFlag(PermissionSet.SendMessages)) throw new PKError("PluralKit is missing **Send Messages** permissions in the new log channel."); if (!perms.HasFlag(PermissionSet.EmbedLinks)) @@ -71,11 +71,11 @@ namespace PluralKit.Bot public async Task SetLogEnabled(Context ctx, bool enable) { - ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); + await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); var affectedChannels = new List(); if (ctx.Match("all")) - affectedChannels = _cache.GetGuildChannels(ctx.Guild.Id).Where(x => x.Type == Channel.ChannelType.GuildText).ToList(); + affectedChannels = (await _cache.GetGuildChannels(ctx.Guild.Id)).Where(x => x.Type == Channel.ChannelType.GuildText).ToList(); else if (!ctx.HasNext()) throw new PKSyntaxError("You must pass one or more #channels."); else while (ctx.HasNext()) { @@ -104,13 +104,13 @@ namespace PluralKit.Bot public async Task ShowBlacklisted(Context ctx) { - ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); + await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); var blacklist = await _repo.GetGuild(ctx.Guild.Id); // Resolve all channels from the cache and order by position - var channels = blacklist.Blacklist - .Select(id => _cache.GetChannelOrNull(id)) + var channels = (await Task.WhenAll(blacklist.Blacklist + .Select(id => _cache.GetChannelOrNull(id)))) .Where(c => c != null) .OrderBy(c => c.Position) .ToList(); @@ -124,10 +124,10 @@ namespace PluralKit.Bot await ctx.Paginate(channels.ToAsyncEnumerable(), channels.Count, 25, $"Blacklisted channels for {ctx.Guild.Name}", null, - (eb, l) => + async (eb, l) => { - string CategoryName(ulong? id) => - id != null ? _cache.GetChannel(id.Value).Name : "(no category)"; + async Task CategoryName(ulong? id) => + id != null ? (await _cache.GetChannel(id.Value)).Name : "(no category)"; ulong? lastCategory = null; @@ -136,7 +136,7 @@ namespace PluralKit.Bot { if (lastCategory != channel!.ParentId && fieldValue.Length > 0) { - eb.Field(new(CategoryName(lastCategory), fieldValue.ToString())); + eb.Field(new(await CategoryName(lastCategory), fieldValue.ToString())); fieldValue.Clear(); } else fieldValue.Append("\n"); @@ -145,19 +145,17 @@ namespace PluralKit.Bot lastCategory = channel.ParentId; } - eb.Field(new(CategoryName(lastCategory), fieldValue.ToString())); - - return Task.CompletedTask; + eb.Field(new(await CategoryName(lastCategory), fieldValue.ToString())); }); } public async Task SetBlacklisted(Context ctx, bool shouldAdd) { - ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); + await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); var affectedChannels = new List(); if (ctx.Match("all")) - affectedChannels = _cache.GetGuildChannels(ctx.Guild.Id).Where(x => x.Type == Channel.ChannelType.GuildText).ToList(); + affectedChannels = (await _cache.GetGuildChannels(ctx.Guild.Id)).Where(x => x.Type == Channel.ChannelType.GuildText).ToList(); else if (!ctx.HasNext()) throw new PKSyntaxError("You must pass one or more #channels."); else while (ctx.HasNext()) { @@ -182,7 +180,7 @@ namespace PluralKit.Bot public async Task SetLogCleanup(Context ctx) { - ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); + await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); var botList = string.Join(", ", _cleanService.Bots.Select(b => b.Name).OrderBy(x => x.ToLowerInvariant())); diff --git a/PluralKit.Bot/Handlers/MessageCreated.cs b/PluralKit.Bot/Handlers/MessageCreated.cs index d4be0744..717ef97e 100644 --- a/PluralKit.Bot/Handlers/MessageCreated.cs +++ b/PluralKit.Bot/Handlers/MessageCreated.cs @@ -70,9 +70,9 @@ namespace PluralKit.Bot if (evt.Type != Message.MessageType.Default && evt.Type != Message.MessageType.Reply) return; if (IsDuplicateMessage(evt)) return; - var guild = evt.GuildId != null ? _cache.GetGuild(evt.GuildId.Value) : null; - var channel = _cache.GetChannel(evt.ChannelId); - var rootChannel = _cache.GetRootChannel(evt.ChannelId); + var guild = evt.GuildId != null ? await _cache.GetGuild(evt.GuildId.Value) : null; + var channel = await _cache.GetChannel(evt.ChannelId); + var rootChannel = await _cache.GetRootChannel(evt.ChannelId); // Log metrics and message info _metrics.Measure.Meter.Mark(BotMetrics.MessagesReceived); @@ -98,7 +98,7 @@ namespace PluralKit.Bot private async ValueTask TryHandleLogClean(MessageCreateEvent evt, MessageContext ctx) { - var channel = _cache.GetChannel(evt.ChannelId); + var channel = await _cache.GetChannel(evt.ChannelId); if (!evt.Author.Bot || channel.Type != Channel.ChannelType.GuildText || !ctx.LogCleanupEnabled) return false; @@ -156,7 +156,7 @@ namespace PluralKit.Bot private async ValueTask TryHandleProxy(Shard shard, MessageCreateEvent evt, Guild guild, Channel channel, MessageContext ctx) { - var botPermissions = _bot.PermissionsIn(channel.Id); + var botPermissions = await _bot.PermissionsIn(channel.Id); try { diff --git a/PluralKit.Bot/Handlers/MessageEdited.cs b/PluralKit.Bot/Handlers/MessageEdited.cs index 32760e0b..6a55301d 100644 --- a/PluralKit.Bot/Handlers/MessageEdited.cs +++ b/PluralKit.Bot/Handlers/MessageEdited.cs @@ -51,10 +51,10 @@ namespace PluralKit.Bot if (!evt.Content.HasValue || !evt.Author.HasValue || !evt.Member.HasValue) return; - var channel = _cache.GetChannel(evt.ChannelId); + var channel = await _cache.GetChannel(evt.ChannelId); if (!DiscordUtils.IsValidGuildChannel(channel)) return; - var guild = _cache.GetGuild(channel.GuildId!.Value); + var guild = await _cache.GetGuild(channel.GuildId!.Value); var lastMessage = _lastMessageCache.GetLastMessage(evt.ChannelId)?.Current; // Only react to the last message in the channel @@ -67,7 +67,7 @@ namespace PluralKit.Bot ctx = await _repo.GetMessageContext(evt.Author.Value!.Id, channel.GuildId!.Value, evt.ChannelId); var equivalentEvt = await GetMessageCreateEvent(evt, lastMessage, channel); - var botPermissions = _bot.PermissionsIn(channel.Id); + var botPermissions = await _bot.PermissionsIn(channel.Id); try { @@ -112,7 +112,7 @@ namespace PluralKit.Bot if (referencedMessageId == null) return null; - var botPermissions = _bot.PermissionsIn(channelId); + var botPermissions = await _bot.PermissionsIn(channelId); if (!botPermissions.HasFlag(PermissionSet.ReadMessageHistory)) { _logger.Warning("Tried to get referenced message in channel {ChannelId} to reply but bot does not have Read Message History", diff --git a/PluralKit.Bot/Handlers/ReactionAdded.cs b/PluralKit.Bot/Handlers/ReactionAdded.cs index 9b9d0dc2..761cd149 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -50,7 +50,7 @@ namespace PluralKit.Bot { // Sometimes we get events from users that aren't in the user cache // We just ignore all of those for now, should be quite rare... - if (!_cache.TryGetUser(evt.UserId, out var user)) + if (!await _cache.TryGetUser(evt.UserId, out var user)) return; // ignore any reactions added by *us* @@ -60,7 +60,7 @@ namespace PluralKit.Bot // Ignore reactions from bots (we can't DM them anyway) if (user.Bot) return; - var channel = _cache.GetChannel(evt.ChannelId); + var channel = await _cache.GetChannel(evt.ChannelId); // check if it's a command message first // since this can happen in DMs as well @@ -121,7 +121,7 @@ namespace PluralKit.Bot private async ValueTask HandleProxyDeleteReaction(MessageReactionAddEvent evt, FullMessage msg) { - if (!_bot.PermissionsIn(evt.ChannelId).HasFlag(PermissionSet.ManageMessages)) + if (!(await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) return; var system = await _repo.GetSystemByAccount(evt.UserId); @@ -162,7 +162,7 @@ namespace PluralKit.Bot private async ValueTask HandleQueryReaction(MessageReactionAddEvent evt, FullMessage msg) { - var guild = _cache.GetGuild(evt.GuildId!.Value); + var guild = await _cache.GetGuild(evt.GuildId!.Value); // Try to DM the user info about the message try @@ -185,14 +185,14 @@ namespace PluralKit.Bot private async ValueTask HandlePingReaction(MessageReactionAddEvent evt, FullMessage msg) { - if (!_bot.PermissionsIn(evt.ChannelId).HasFlag(PermissionSet.ManageMessages)) + if (!(await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) return; // Check if the "pinger" has permission to send messages in this channel // (if not, PK shouldn't send messages on their behalf) var member = await _rest.GetGuildMember(evt.GuildId!.Value, evt.UserId); var requiredPerms = PermissionSet.ViewChannel | PermissionSet.SendMessages; - if (member == null || !_cache.PermissionsFor(evt.ChannelId, member).HasFlag(requiredPerms)) return; + if (member == null || !(await _cache.PermissionsFor(evt.ChannelId, member)).HasFlag(requiredPerms)) return; if (msg.System.PingsEnabled) { @@ -240,7 +240,7 @@ namespace PluralKit.Bot private async Task TryRemoveOriginalReaction(MessageReactionAddEvent evt) { - if (_bot.PermissionsIn(evt.ChannelId).HasFlag(PermissionSet.ManageMessages)) + if ((await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) await _rest.DeleteUserReaction(evt.ChannelId, evt.MessageId, evt.Emoji, evt.UserId); } } diff --git a/PluralKit.Bot/Modules.cs b/PluralKit.Bot/Modules.cs index c348c1ad..396b8b79 100644 --- a/PluralKit.Bot/Modules.cs +++ b/PluralKit.Bot/Modules.cs @@ -42,7 +42,9 @@ namespace PluralKit.Bot }; }).AsSelf().SingleInstance(); builder.RegisterType().AsSelf().SingleInstance(); - builder.RegisterType().AsSelf().As().SingleInstance(); + builder.Register(c => { + return new MemoryDiscordCache(); + }).AsSelf().As().SingleInstance(); builder.Register(c => { diff --git a/PluralKit.Bot/Proxy/ProxyService.cs b/PluralKit.Bot/Proxy/ProxyService.cs index 978a8455..3b13e95f 100644 --- a/PluralKit.Bot/Proxy/ProxyService.cs +++ b/PluralKit.Bot/Proxy/ProxyService.cs @@ -56,7 +56,7 @@ namespace PluralKit.Bot if (!ShouldProxy(channel, message, ctx)) return false; - var rootChannel = _cache.GetRootChannel(message.ChannelId); + var rootChannel = await _cache.GetRootChannel(message.ChannelId); List members; // Fetch members and try to match to a specific member @@ -143,10 +143,10 @@ namespace PluralKit.Bot var content = match.ProxyContent; if (!allowEmbeds) content = content.BreakLinkEmbeds(); - var messageChannel = _cache.GetChannel(trigger.ChannelId); - var rootChannel = _cache.GetRootChannel(trigger.ChannelId); + var messageChannel = await _cache.GetChannel(trigger.ChannelId); + var rootChannel = await _cache.GetRootChannel(trigger.ChannelId); var threadId = messageChannel.IsThread() ? messageChannel.Id : (ulong?)null; - var guild = _cache.GetGuild(trigger.GuildId.Value); + var guild = await _cache.GetGuild(trigger.GuildId.Value); var proxyMessage = await _webhookExecutor.ExecuteWebhook(new ProxyRequest { diff --git a/PluralKit.Bot/Services/LogChannelService.cs b/PluralKit.Bot/Services/LogChannelService.cs index 52774a6f..b7a7e522 100644 --- a/PluralKit.Bot/Services/LogChannelService.cs +++ b/PluralKit.Bot/Services/LogChannelService.cs @@ -41,7 +41,7 @@ namespace PluralKit.Bot if (logChannel == null) return; - var triggerChannel = _cache.GetChannel(proxiedMessage.Channel); + var triggerChannel = await _cache.GetChannel(proxiedMessage.Channel); var system = await _repo.GetSystem(ctx.SystemId.Value); var member = await _repo.GetMember(proxiedMessage.Member); @@ -78,7 +78,7 @@ namespace PluralKit.Bot if (logChannel == null || logChannel.Type != Channel.ChannelType.GuildText) return null; // Check bot permissions - var perms = _bot.PermissionsIn(logChannel.Id); + var perms = await _bot.PermissionsIn(logChannel.Id); if (!perms.HasFlag(PermissionSet.SendMessages | PermissionSet.EmbedLinks)) { _logger.Information( @@ -93,7 +93,7 @@ namespace PluralKit.Bot private async Task FindLogChannel(ulong guildId, ulong channelId) { // TODO: fetch it directly on cache miss? - if (_cache.TryGetChannel(channelId, out var channel)) + if (await _cache.TryGetChannel(channelId, out var channel)) return channel; // Channel doesn't exist or we don't have permission to access it, let's remove it from the database too diff --git a/PluralKit.Bot/Services/LoggerCleanService.cs b/PluralKit.Bot/Services/LoggerCleanService.cs index bb4d310a..f7fcfb63 100644 --- a/PluralKit.Bot/Services/LoggerCleanService.cs +++ b/PluralKit.Bot/Services/LoggerCleanService.cs @@ -86,10 +86,10 @@ namespace PluralKit.Bot public async ValueTask HandleLoggerBotCleanup(Message msg) { - var channel = _cache.GetChannel(msg.ChannelId); + var channel = await _cache.GetChannel(msg.ChannelId); if (channel.Type != Channel.ChannelType.GuildText) return; - if (!_bot.PermissionsIn(channel.Id).HasFlag(PermissionSet.ManageMessages)) return; + if (!(await _bot.PermissionsIn(channel.Id)).HasFlag(PermissionSet.ManageMessages)) return; // If this message is from a *webhook*, check if the name matches one of the bots we know // TODO: do we need to do a deeper webhook origin check, or would that be too hard on the rate limit? diff --git a/PluralKit.Bot/Services/PeriodicStatCollector.cs b/PluralKit.Bot/Services/PeriodicStatCollector.cs index b4533e7c..94433e81 100644 --- a/PluralKit.Bot/Services/PeriodicStatCollector.cs +++ b/PluralKit.Bot/Services/PeriodicStatCollector.cs @@ -52,7 +52,7 @@ namespace PluralKit.Bot await foreach (var guild in _cache.GetAllGuilds()) { guildCount++; - foreach (var channel in _cache.GetGuildChannels(guild.Id)) + foreach (var channel in await _cache.GetGuildChannels(guild.Id)) { if (DiscordUtils.IsValidGuildChannel(channel)) channelCount++; diff --git a/PluralKit.Bot/Services/WebhookExecutorService.cs b/PluralKit.Bot/Services/WebhookExecutorService.cs index da5ebe23..4ab560f8 100644 --- a/PluralKit.Bot/Services/WebhookExecutorService.cs +++ b/PluralKit.Bot/Services/WebhookExecutorService.cs @@ -89,7 +89,7 @@ namespace PluralKit.Bot }; ulong? threadId = null; - var root = _cache.GetRootChannel(channelId); + var root = await _cache.GetRootChannel(channelId); if (root.Id != channelId) threadId = channelId; @@ -102,7 +102,7 @@ namespace PluralKit.Bot private async Task ExecuteWebhookInner(Webhook webhook, ProxyRequest req, bool hasRetried = false) { - var guild = _cache.GetGuild(req.GuildId); + var guild = await _cache.GetGuild(req.GuildId); var content = req.Content.Truncate(2000); var allowedMentions = content.ParseMentions(); diff --git a/PluralKit.Bot/Utils/ContextUtils.cs b/PluralKit.Bot/Utils/ContextUtils.cs index 8e1c8615..0c2d8f52 100644 --- a/PluralKit.Bot/Utils/ContextUtils.cs +++ b/PluralKit.Bot/Utils/ContextUtils.cs @@ -165,7 +165,7 @@ namespace PluralKit.Bot if (currentPage < 0) currentPage += pageCount; // If we can, remove the user's reaction (so they can press again quickly) - if (ctx.BotPermissions.HasFlag(PermissionSet.ManageMessages)) + if ((await ctx.BotPermissions).HasFlag(PermissionSet.ManageMessages)) await ctx.Rest.DeleteUserReaction(msg.ChannelId, msg.Id, reaction.Emoji, reaction.UserId); // Edit the embed with the new page @@ -179,7 +179,7 @@ namespace PluralKit.Bot } // todo: re-check - if (ctx.BotPermissions.HasFlag(PermissionSet.ManageMessages)) + if ((await ctx.BotPermissions).HasFlag(PermissionSet.ManageMessages)) await ctx.Rest.DeleteAllReactions(msg.ChannelId, msg.Id); } // If we get a "NotFound" error, the message has been deleted and thus not our problem @@ -292,7 +292,7 @@ namespace PluralKit.Bot var task = f(); // If we don't have permission to add reactions, don't bother, and just await the task normally. - if (!DiscordUtils.HasReactionPermissions(ctx)) return await task; + if (!await DiscordUtils.HasReactionPermissions(ctx)) return await task; try { diff --git a/PluralKit.Bot/Utils/DiscordUtils.cs b/PluralKit.Bot/Utils/DiscordUtils.cs index b10f0ce7..21312db6 100644 --- a/PluralKit.Bot/Utils/DiscordUtils.cs +++ b/PluralKit.Bot/Utils/DiscordUtils.cs @@ -196,10 +196,10 @@ namespace PluralKit.Bot public static string EventType(this IGatewayEvent evt) => evt.GetType().Name.Replace("Event", ""); - public static bool HasReactionPermissions(Context ctx) + public static async Task HasReactionPermissions(Context ctx) { var neededPermissions = PermissionSet.AddReactions | PermissionSet.ReadMessageHistory; - return ((ctx.BotPermissions & neededPermissions) == neededPermissions); + return ((await ctx.BotPermissions & neededPermissions) == neededPermissions); } public static bool IsValidGuildChannel(Channel channel) => diff --git a/PluralKit.Bot/Utils/SentryUtils.cs b/PluralKit.Bot/Utils/SentryUtils.cs index ac63b3d7..0244959e 100644 --- a/PluralKit.Bot/Utils/SentryUtils.cs +++ b/PluralKit.Bot/Utils/SentryUtils.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Myriad.Extensions; using Myriad.Gateway; +using Myriad.Types; using Sentry; @@ -42,8 +44,10 @@ namespace PluralKit.Bot // Also report information about the bot's permissions in the channel // We get a lot of permission errors so this'll be useful for determining problems - var perms = _bot.PermissionsIn(evt.ChannelId); - scope.AddBreadcrumb(perms.ToPermissionString(), "permissions"); + + // todo: re-add this + // var perms = _bot.PermissionsIn(evt.ChannelId); + // scope.AddBreadcrumb(perms.ToPermissionString(), "permissions"); } public void Enrich(Scope scope, Shard shard, MessageDeleteEvent evt) diff --git a/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs b/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs index a3845505..9c78b6eb 100644 --- a/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs +++ b/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Myriad.Cache; using Myriad.Extensions; @@ -20,7 +21,7 @@ namespace PluralKit.Bot _cache = cache; } - public ILogEventEnricher GetEnricher(Shard shard, IGatewayEvent evt) + public async Task GetEnricher(Shard shard, IGatewayEvent evt) { var props = new List { @@ -38,9 +39,9 @@ namespace PluralKit.Bot { props.Add(new("ChannelId", new ScalarValue(channel.Value))); - if (_cache.TryGetChannel(channel.Value, out _)) + if (await _cache.TryGetChannel(channel.Value, out _)) { - var botPermissions = _bot.PermissionsIn(channel.Value); + var botPermissions = await _bot.PermissionsIn(channel.Value); props.Add(new("BotPermissions", new ScalarValue(botPermissions))); } } @@ -52,7 +53,7 @@ namespace PluralKit.Bot props.Add(new("UserId", new ScalarValue(user.Value))); if (evt is MessageCreateEvent mce) - props.Add(new("UserPermissions", new ScalarValue(_cache.PermissionsFor(mce)))); + props.Add(new("UserPermissions", new ScalarValue(await _cache.PermissionsFor(mce)))); return new Inner(props); }