diff --git a/Myriad/Cache/DiscordCacheExtensions.cs b/Myriad/Cache/DiscordCacheExtensions.cs index c5659e4a..8e463914 100644 --- a/Myriad/Cache/DiscordCacheExtensions.cs +++ b/Myriad/Cache/DiscordCacheExtensions.cs @@ -1,7 +1,9 @@ using System.Linq; using System.Threading.Tasks; +using Myriad.Extensions; using Myriad.Gateway; +using Myriad.Types; namespace Myriad.Cache { @@ -11,6 +13,8 @@ namespace Myriad.Cache { switch (evt) { + case ReadyEvent ready: + return cache.SaveOwnUser(ready.User.Id); case GuildCreateEvent gc: return cache.SaveGuildCreate(gc); case GuildUpdateEvent gu: @@ -102,5 +106,20 @@ namespace Myriad.Cache foreach (var thread in evt.Threads) await cache.SaveChannel(thread); } + + public static async Task PermissionsIn(this IDiscordCache cache, ulong channelId) + { + var channel = await cache.GetRootChannel(channelId); + + if (channel.GuildId != null) + { + var userId = await cache.GetOwnUser(); + var member = await cache.TryGetSelfMember(channel.GuildId.Value); + return await cache.PermissionsFor(channelId, userId, member); + } + + return PermissionSet.Dm; + } + } } \ No newline at end of file diff --git a/Myriad/Cache/IDiscordCache.cs b/Myriad/Cache/IDiscordCache.cs index ebae23f8..1f94d1bc 100644 --- a/Myriad/Cache/IDiscordCache.cs +++ b/Myriad/Cache/IDiscordCache.cs @@ -7,6 +7,7 @@ namespace Myriad.Cache { public interface IDiscordCache { + public ValueTask SaveOwnUser(ulong userId); public ValueTask SaveGuild(Guild guild); public ValueTask SaveChannel(Channel channel); public ValueTask SaveUser(User user); @@ -19,6 +20,7 @@ namespace Myriad.Cache public ValueTask RemoveUser(ulong userId); public ValueTask RemoveRole(ulong guildId, ulong roleId); + public Task GetOwnUser(); public Task TryGetGuild(ulong guildId); public Task TryGetChannel(ulong channelId); public Task TryGetDmChannel(ulong userId); diff --git a/Myriad/Cache/MemoryDiscordCache.cs b/Myriad/Cache/MemoryDiscordCache.cs index 3898a7f2..e1f27cb5 100644 --- a/Myriad/Cache/MemoryDiscordCache.cs +++ b/Myriad/Cache/MemoryDiscordCache.cs @@ -16,6 +16,7 @@ namespace Myriad.Cache private readonly ConcurrentDictionary _roles = new(); private readonly ConcurrentDictionary _users = new(); private readonly ConcurrentDictionary _guildMembers = new(); + private ulong? _ownUserId { get; set; } public ValueTask SaveGuild(Guild guild) { @@ -46,6 +47,15 @@ namespace Myriad.Cache } } + public ValueTask SaveOwnUser(ulong userId) + { + // this (hopefully) never changes at runtime, so we skip out on re-assigning it + if (_ownUserId == null) + _ownUserId = userId; + + return default; + } + public ValueTask SaveUser(User user) { _users[user.Id] = user; @@ -125,6 +135,8 @@ namespace Myriad.Cache return default; } + public Task GetOwnUser() => Task.FromResult(_ownUserId!.Value); + public ValueTask RemoveRole(ulong guildId, ulong roleId) { _roles.TryRemove(roleId, out _); diff --git a/Myriad/Gateway/Cluster.cs b/Myriad/Gateway/Cluster.cs index 91ef1b0b..709c7ab3 100644 --- a/Myriad/Gateway/Cluster.cs +++ b/Myriad/Gateway/Cluster.cs @@ -28,8 +28,6 @@ namespace Myriad.Gateway public event Action? ShardCreated; public IReadOnlyDictionary Shards => _shards; - public User? User => _shards.Values.Select(s => s.User).FirstOrDefault(s => s != null); - public ApplicationPartial? Application => _shards.Values.Select(s => s.Application).FirstOrDefault(s => s != null); public async Task Start(GatewayInfo.Bot info) { diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index f084053e..b6f47e68 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -77,19 +77,6 @@ namespace PluralKit.Bot }, null, timeTillNextWholeMinute, TimeSpan.FromMinutes(1)); } - public async Task PermissionsIn(ulong channelId) - { - var channel = await _cache.GetRootChannel(channelId); - - if (channel.GuildId != null) - { - var member = await _cache.TryGetSelfMember(channel.GuildId.Value); - return await _cache.PermissionsFor(channelId, _cluster.User?.Id ?? default, member); - } - - return PermissionSet.Dm; - } - private async Task OnEventReceived(Shard shard, IGatewayEvent evt) { await _cache.TryUpdateSelfMember(shard, evt); @@ -249,7 +236,7 @@ namespace PluralKit.Bot if (reportChannel == null) return; - var botPerms = await PermissionsIn(reportChannel.Value); + var botPerms = await _cache.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 2fc43f76..e651c7f9 100644 --- a/PluralKit.Bot/CommandSystem/Context.cs +++ b/PluralKit.Bot/CommandSystem/Context.cs @@ -71,7 +71,7 @@ namespace PluralKit.Bot public Cluster Cluster => _cluster; public MessageContext MessageContext => _messageContext; - public Task BotPermissions => _provider.Resolve().PermissionsIn(_channel.Id); + public Task BotPermissions => _provider.Resolve().PermissionsIn(_channel.Id); public Task UserPermissions => _cache.PermissionsFor(_message); public DiscordApiClient Rest => _rest; diff --git a/PluralKit.Bot/Commands/Checks.cs b/PluralKit.Bot/Commands/Checks.cs index 86e7cedf..73f1db88 100644 --- a/PluralKit.Bot/Commands/Checks.cs +++ b/PluralKit.Bot/Commands/Checks.cs @@ -87,7 +87,7 @@ namespace PluralKit.Bot var missingEmojiPermissions = false; foreach (var channel in await _rest.GetGuildChannels(guild.Id)) { - var botPermissions = await _bot.PermissionsIn(channel.Id); + var botPermissions = await _cache.PermissionsIn(channel.Id); var webhookPermissions = await _cache.EveryonePermissions(channel); var userPermissions = PermissionExtensions.PermissionsFor(guild, channel, ctx.Author.Id, senderGuildUser); @@ -176,7 +176,7 @@ namespace PluralKit.Bot if (!await ctx.CheckPermissionsInGuildChannel(channel, PermissionSet.ViewChannel)) throw new PKError(error); - var botPermissions = await _bot.PermissionsIn(channel.Id); + var botPermissions = await _cache.PermissionsIn(channel.Id); var webhookPermissions = await _cache.EveryonePermissions(channel); // We use a bitfield so we can set individual permission bits diff --git a/PluralKit.Bot/Commands/Lists/ContextListExt.cs b/PluralKit.Bot/Commands/Lists/ContextListExt.cs index 6b043790..18526feb 100644 --- a/PluralKit.Bot/Commands/Lists/ContextListExt.cs +++ b/PluralKit.Bot/Commands/Lists/ContextListExt.cs @@ -61,7 +61,7 @@ namespace PluralKit.Bot p.IncludeLastSwitch = true; if (ctx.MatchFlag("with-last-message", "with-last-proxy", "wlm", "wlp")) throw new PKError("Sorting by last message is temporarily disabled due to database issues, sorry."); - // p.IncludeLastMessage = true; + // p.IncludeLastMessage = true; if (ctx.MatchFlag("with-message-count", "wmc")) p.IncludeMessageCount = true; if (ctx.MatchFlag("with-created", "wc")) diff --git a/PluralKit.Bot/Commands/Misc.cs b/PluralKit.Bot/Commands/Misc.cs index 004ed2cc..20613399 100644 --- a/PluralKit.Bot/Commands/Misc.cs +++ b/PluralKit.Bot/Commands/Misc.cs @@ -59,7 +59,7 @@ namespace PluralKit.Bot public async Task Invite(Context ctx) { - var clientId = _botConfig.ClientId ?? _cluster.Application?.Id; + var clientId = _botConfig.ClientId ?? await _cache.GetOwnUser(); var permissions = PermissionSet.AddReactions | diff --git a/PluralKit.Bot/Commands/ServerConfig.cs b/PluralKit.Bot/Commands/ServerConfig.cs index b9e08a83..3ec6e4ad 100644 --- a/PluralKit.Bot/Commands/ServerConfig.cs +++ b/PluralKit.Bot/Commands/ServerConfig.cs @@ -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 = await _bot.PermissionsIn(channel.Id); + var perms = await _cache.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)) diff --git a/PluralKit.Bot/Handlers/MessageCreated.cs b/PluralKit.Bot/Handlers/MessageCreated.cs index 717ef97e..04a52de2 100644 --- a/PluralKit.Bot/Handlers/MessageCreated.cs +++ b/PluralKit.Bot/Handlers/MessageCreated.cs @@ -54,7 +54,9 @@ namespace PluralKit.Bot // for now, only return error messages for explicit commands public ulong? ErrorChannelFor(MessageCreateEvent evt) { - if (!HasCommandPrefix(evt.Content, _cluster.User?.Id ?? default, out var cmdStart) || cmdStart == evt.Content.Length) + // todo: fix @mention prefix + // it only breaks error reporting so I'm not *too* worried about it, but should be fixed eventually + if (!HasCommandPrefix(evt.Content, default, out var cmdStart) || cmdStart == evt.Content.Length) return null; return evt.ChannelId; @@ -156,7 +158,7 @@ namespace PluralKit.Bot private async ValueTask TryHandleProxy(Shard shard, MessageCreateEvent evt, Guild guild, Channel channel, MessageContext ctx) { - var botPermissions = await _bot.PermissionsIn(channel.Id); + var botPermissions = await _cache.PermissionsIn(channel.Id); try { diff --git a/PluralKit.Bot/Handlers/MessageEdited.cs b/PluralKit.Bot/Handlers/MessageEdited.cs index 6a55301d..8ee024f5 100644 --- a/PluralKit.Bot/Handlers/MessageEdited.cs +++ b/PluralKit.Bot/Handlers/MessageEdited.cs @@ -45,7 +45,7 @@ namespace PluralKit.Bot public async Task Handle(Shard shard, MessageUpdateEvent evt) { - if (evt.Author.Value?.Id == _client.User?.Id) return; + if (evt.Author.Value?.Id == await _cache.GetOwnUser()) return; // Edit message events sometimes arrive with missing data; double-check it's all there if (!evt.Content.HasValue || !evt.Author.HasValue || !evt.Member.HasValue) @@ -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 = await _bot.PermissionsIn(channel.Id); + var botPermissions = await _cache.PermissionsIn(channel.Id); try { @@ -112,7 +112,7 @@ namespace PluralKit.Bot if (referencedMessageId == null) return null; - var botPermissions = await _bot.PermissionsIn(channelId); + var botPermissions = await _cache.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 6e7faa47..4fffe4d6 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -54,7 +54,7 @@ namespace PluralKit.Bot return; // ignore any reactions added by *us* - if (evt.UserId == _cluster.User?.Id) + if (evt.UserId == await _cache.GetOwnUser()) return; // Ignore reactions from bots (we can't DM them anyway) @@ -121,7 +121,7 @@ namespace PluralKit.Bot private async ValueTask HandleProxyDeleteReaction(MessageReactionAddEvent evt, FullMessage msg) { - if (!(await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) + if (!(await _cache.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) return; var system = await _repo.GetSystemByAccount(evt.UserId); @@ -185,7 +185,7 @@ namespace PluralKit.Bot private async ValueTask HandlePingReaction(MessageReactionAddEvent evt, FullMessage msg) { - if (!(await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) + if (!(await _cache.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) return; // Check if the "pinger" has permission to send messages in this channel @@ -240,7 +240,7 @@ namespace PluralKit.Bot private async Task TryRemoveOriginalReaction(MessageReactionAddEvent evt) { - if ((await _bot.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) + if ((await _cache.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.ManageMessages)) await _rest.DeleteUserReaction(evt.ChannelId, evt.MessageId, evt.Emoji, evt.UserId); } } diff --git a/PluralKit.Bot/Services/LogChannelService.cs b/PluralKit.Bot/Services/LogChannelService.cs index ef23c18e..92823e49 100644 --- a/PluralKit.Bot/Services/LogChannelService.cs +++ b/PluralKit.Bot/Services/LogChannelService.cs @@ -78,7 +78,7 @@ namespace PluralKit.Bot if (logChannel == null || logChannel.Type != Channel.ChannelType.GuildText) return null; // Check bot permissions - var perms = await _bot.PermissionsIn(logChannel.Id); + var perms = await _cache.PermissionsIn(logChannel.Id); if (!perms.HasFlag(PermissionSet.SendMessages | PermissionSet.EmbedLinks)) { _logger.Information( diff --git a/PluralKit.Bot/Services/LoggerCleanService.cs b/PluralKit.Bot/Services/LoggerCleanService.cs index f7fcfb63..0cfd1db3 100644 --- a/PluralKit.Bot/Services/LoggerCleanService.cs +++ b/PluralKit.Bot/Services/LoggerCleanService.cs @@ -89,7 +89,7 @@ namespace PluralKit.Bot var channel = await _cache.GetChannel(msg.ChannelId); if (channel.Type != Channel.ChannelType.GuildText) return; - if (!(await _bot.PermissionsIn(channel.Id)).HasFlag(PermissionSet.ManageMessages)) return; + if (!(await _cache.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/WebhookCacheService.cs b/PluralKit.Bot/Services/WebhookCacheService.cs index e8c26714..3f22ceb0 100644 --- a/PluralKit.Bot/Services/WebhookCacheService.cs +++ b/PluralKit.Bot/Services/WebhookCacheService.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using App.Metrics; -using Myriad.Gateway; +using Myriad.Cache; using Myriad.Rest; using Myriad.Rest.Types.Requests; using Myriad.Types; @@ -24,13 +24,13 @@ namespace PluralKit.Bot private readonly IMetrics _metrics; private readonly ILogger _logger; - private readonly Cluster _cluster; + private readonly IDiscordCache _cache; - public WebhookCacheService(ILogger logger, IMetrics metrics, DiscordApiClient rest, Cluster cluster) + public WebhookCacheService(ILogger logger, IMetrics metrics, DiscordApiClient rest, IDiscordCache cache) { _metrics = metrics; _rest = rest; - _cluster = cluster; + _cache = cache; _logger = logger.ForContext(); _webhooks = new ConcurrentDictionary>>(); } @@ -86,7 +86,8 @@ namespace PluralKit.Bot var webhooks = await FetchChannelWebhooks(channelId); // If the channel has a webhook created by PK, just return that one - var ourWebhook = webhooks.FirstOrDefault(IsWebhookMine); + var ourUserId = await _cache.GetOwnUser(); + var ourWebhook = webhooks.FirstOrDefault(hook => IsWebhookMine(ourUserId, hook)); if (ourWebhook != null) return ourWebhook; @@ -120,7 +121,7 @@ namespace PluralKit.Bot return await _rest.CreateWebhook(channelId, new CreateWebhookRequest(WebhookName)); } - private bool IsWebhookMine(Webhook arg) => arg.User?.Id == _cluster.User?.Id && arg.Name == WebhookName; + private bool IsWebhookMine(ulong userId, Webhook arg) => arg.User?.Id == userId && arg.Name == WebhookName; public int CacheSize => _webhooks.Count; } diff --git a/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs b/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs index d99e0b63..fb540a52 100644 --- a/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs +++ b/PluralKit.Bot/Utils/SerilogGatewayEnricherFactory.cs @@ -42,7 +42,7 @@ namespace PluralKit.Bot if (await _cache.TryGetChannel(channel.Value) != null) { - var botPermissions = await _bot.PermissionsIn(channel.Value); + var botPermissions = await _cache.PermissionsIn(channel.Value); props.Add(new("BotPermissions", new ScalarValue(botPermissions))); } }