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
This commit is contained in:
spiral 2021-11-17 20:41:02 -05:00
parent 45258d519e
commit e7f36eb31f
No known key found for this signature in database
GPG Key ID: A6059F0CA0E1BD31
24 changed files with 134 additions and 126 deletions

View File

@ -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<bool> TryGetGuild(ulong guildId, out Guild guild);
public Task<bool> TryGetChannel(ulong channelId, out Channel channel);
public Task<bool> TryGetDmChannel(ulong userId, out Channel channel);
public Task<bool> TryGetUser(ulong userId, out User user);
public Task<bool> TryGetRole(ulong roleId, out Role role);
public IAsyncEnumerable<Guild> GetAllGuilds();
public IEnumerable<Channel> GetGuildChannels(ulong guildId);
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId);
}
}

View File

@ -124,34 +124,34 @@ namespace Myriad.Cache
return default;
}
public bool TryGetGuild(ulong guildId, out Guild guild)
public Task<bool> 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<bool> TryGetChannel(ulong channelId, out Channel channel) =>
Task.FromResult(_channels.TryGetValue(channelId, out channel!));
public bool TryGetDmChannel(ulong userId, out Channel channel)
public Task<bool> 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<bool> 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<bool> TryGetRole(ulong roleId, out Role role) =>
Task.FromResult(_roles.TryGetValue(roleId, out role!));
public IAsyncEnumerable<Guild> GetAllGuilds()
{
@ -160,12 +160,12 @@ namespace Myriad.Cache
.ToAsyncEnumerable();
}
public IEnumerable<Channel> GetGuildChannels(ulong guildId)
public Task<IEnumerable<Channel>> 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) =>

View File

@ -9,44 +9,44 @@ namespace Myriad.Extensions
{
public static class CacheExtensions
{
public static Guild GetGuild(this IDiscordCache cache, ulong guildId)
public static async Task<Guild> 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<Channel> 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<Channel?> 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<User> 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<Role> 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<User?> 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<Channel?> 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<Channel> 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<Channel> 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;
}
}

View File

@ -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<PermissionSet> 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<PermissionSet> 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<PermissionSet> 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<PermissionSet> 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;

View File

@ -78,14 +78,14 @@ namespace PluralKit.Bot
}, null, timeTillNextWholeMinute, TimeSpan.FromMinutes(1));
}
public PermissionSet PermissionsIn(ulong channelId)
public async Task <PermissionSet> 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<HandlerQueue<T>>();
using var _ = LogContext.PushProperty("EventId", Guid.NewGuid());
using var __ = LogContext.Push(serviceScope.Resolve<SerilogGatewayEnricherFactory>().GetEnricher(shard, evt));
using var __ = LogContext.Push(await serviceScope.Resolve<SerilogGatewayEnricherFactory>().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());
}

View File

@ -71,8 +71,8 @@ namespace PluralKit.Bot
public Cluster Cluster => _cluster;
public MessageContext MessageContext => _messageContext;
public PermissionSet BotPermissions => _provider.Resolve<Bot>().PermissionsIn(_channel.Id);
public PermissionSet UserPermissions => _cache.PermissionsFor(_message);
public Task<PermissionSet> BotPermissions => _provider.Resolve<Bot>().PermissionsIn(_channel.Id);
public Task<PermissionSet> UserPermissions => _cache.PermissionsFor(_message);
public DiscordApiClient Rest => _rest;
@ -85,11 +85,13 @@ namespace PluralKit.Bot
public async Task<Message> 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

View File

@ -52,16 +52,16 @@ namespace PluralKit.Bot
return ctx;
}
public static Context CheckAuthorPermission(this Context ctx, PermissionSet neededPerms, string permissionName)
public static async Task<Context> 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<bool> 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;

View File

@ -152,19 +152,19 @@ namespace PluralKit.Bot
}
}
public static Task<Channel> MatchChannel(this Context ctx)
public static async Task<Channel> MatchChannel(this Context ctx)
{
if (!MentionUtils.TryParseChannel(ctx.PeekArgument(), out var id))
return Task.FromResult<Channel>(null);
return null;
if (!ctx.Cache.TryGetChannel(id, out var channel))
return Task.FromResult<Channel>(null);
if (!await ctx.Cache.TryGetChannel(id, out var channel))
return null;
if (!DiscordUtils.IsValidGuildChannel(channel))
return Task.FromResult<Channel>(null);
return null;
ctx.PopArgument();
return Task.FromResult(channel);
return channel;
}
public static Guild MatchGuild(this Context ctx)

View File

@ -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.");

View File

@ -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))

View File

@ -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<Channel>();
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<string> 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<Channel>();
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()));

View File

@ -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<bool> 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<bool> 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
{

View File

@ -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",

View File

@ -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);
}
}

View File

@ -42,7 +42,9 @@ namespace PluralKit.Bot
};
}).AsSelf().SingleInstance();
builder.RegisterType<Cluster>().AsSelf().SingleInstance();
builder.RegisterType<MemoryDiscordCache>().AsSelf().As<IDiscordCache>().SingleInstance();
builder.Register(c => {
return new MemoryDiscordCache();
}).AsSelf().As<IDiscordCache>().SingleInstance();
builder.Register(c =>
{

View File

@ -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<ProxyMember> 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
{

View File

@ -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<Channel?> 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

View File

@ -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?

View File

@ -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++;

View File

@ -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<Message> 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();

View File

@ -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
{

View File

@ -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<bool> 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) =>

View File

@ -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)

View File

@ -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<ILogEventEnricher> GetEnricher(Shard shard, IGatewayEvent evt)
{
var props = new List<LogEventProperty>
{
@ -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);
}