diff --git a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs index 2dab68f..7f2ec0d 100644 --- a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs +++ b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs @@ -43,6 +43,7 @@ public class ChannelCommands( Config config, GuildRepository guildRepository, GuildCache guildCache, + GuildFetchService guildFetchService, ChannelCache channelCache, IMemberCache memberCache, IFeedbackService feedbackService, @@ -68,8 +69,11 @@ public class ChannelCommands( public async Task CheckPermissionsAsync() { var (userId, guildId) = contextInjection.GetUserAndGuild(); + if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + { + return CataloggerError.Result($"Guild {guildId} not in cache"); + } var embed = new EmbedBuilder().WithTitle($"Permission check for {guild.Name}"); @@ -78,8 +82,18 @@ public class ChannelCommands( DiscordSnowflake.New(config.Discord.ApplicationId) ); var currentUser = await memberCache.TryGetAsync(guildId, userId); + if (botUser == null || currentUser == null) - throw new CataloggerError("Bot member or invoking member not found in cache"); + { + // If this happens, something has gone wrong when fetching members. Refetch the guild's members. + guildFetchService.EnqueueGuild(guildId); + _logger.Error( + "Either our own user {BotId} or the invoking user {UserId} is not in cache, aborting permission check", + config.Discord.ApplicationId, + userId + ); + return CataloggerError.Result("Bot member or invoking member not found in cache"); + } // We don't want to check categories or threads var guildChannels = channelCache @@ -204,7 +218,7 @@ public class ChannelCommands( { var (userId, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); diff --git a/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs b/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs index 9867e7d..1104c82 100644 --- a/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs +++ b/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs @@ -50,15 +50,15 @@ public class ChannelCommandsComponents( public async Task OnMenuSelectionAsync(IReadOnlyList values) { if (contextInjection.Context is not IInteractionCommandContext ctx) - throw new CataloggerError("No context"); + return CataloggerError.Result("No context"); if (!ctx.TryGetUserID(out var userId)) - throw new CataloggerError("No user ID in context"); + return CataloggerError.Result("No user ID in context"); if (!ctx.Interaction.Message.TryGet(out var msg)) - throw new CataloggerError("No message ID in context"); + return CataloggerError.Result("No message ID in context"); if (!ctx.TryGetGuildID(out var guildId)) - throw new CataloggerError("No guild ID in context"); + return CataloggerError.Result("No guild ID in context"); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); @@ -76,7 +76,7 @@ public class ChannelCommandsComponents( var state = values[0]; if (!Enum.TryParse(state, out var logChannelType)) - throw new CataloggerError($"Invalid config-channels state {state}"); + return CataloggerError.Result($"Invalid config-channels state {state}"); var channelId = WebhookExecutorService.GetDefaultLogChannel(guildConfig, logChannelType); string? channelMention; @@ -147,15 +147,15 @@ public class ChannelCommandsComponents( public async Task OnButtonPressedAsync(string state) { if (contextInjection.Context is not IInteractionCommandContext ctx) - throw new CataloggerError("No context"); + return CataloggerError.Result("No context"); if (!ctx.TryGetUserID(out var userId)) - throw new CataloggerError("No user ID in context"); + return CataloggerError.Result("No user ID in context"); if (!ctx.Interaction.Message.TryGet(out var msg)) - throw new CataloggerError("No message ID in context"); + return CataloggerError.Result("No message ID in context"); if (!ctx.TryGetGuildID(out var guildId)) - throw new CataloggerError("No guild ID in context"); + return CataloggerError.Result("No guild ID in context"); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); @@ -179,9 +179,9 @@ public class ChannelCommandsComponents( ); case "reset": if (lease.Data.CurrentPage == null) - throw new CataloggerError("CurrentPage was null in reset button callback"); + return CataloggerError.Result("CurrentPage was null in reset button callback"); if (!Enum.TryParse(lease.Data.CurrentPage, out var channelType)) - throw new CataloggerError( + return CataloggerError.Result( $"Invalid config-channels CurrentPage: '{lease.Data.CurrentPage}'" ); @@ -281,15 +281,15 @@ public class ChannelCommandsComponents( public async Task OnMenuSelectionAsync(IReadOnlyList channels) { if (contextInjection.Context is not IInteractionCommandContext ctx) - throw new CataloggerError("No context"); + return CataloggerError.Result("No context"); if (!ctx.TryGetUserID(out var userId)) - throw new CataloggerError("No user ID in context"); + return CataloggerError.Result("No user ID in context"); if (!ctx.Interaction.Message.TryGet(out var msg)) - throw new CataloggerError("No message ID in context"); + return CataloggerError.Result("No message ID in context"); if (!ctx.TryGetGuildID(out var guildId)) - throw new CataloggerError("No guild ID in context"); + return CataloggerError.Result("No guild ID in context"); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildConfig = await guildRepository.GetAsync(guildId); var channelId = channels[0].ID.ToUlong(); @@ -305,7 +305,7 @@ public class ChannelCommandsComponents( } if (!Enum.TryParse(lease.Data.CurrentPage, out var channelType)) - throw new CataloggerError( + return CataloggerError.Result( $"Invalid config-channels CurrentPage '{lease.Data.CurrentPage}'" ); diff --git a/Catalogger.Backend/Bot/Commands/IgnoreEntitiesCommands.cs b/Catalogger.Backend/Bot/Commands/IgnoreEntitiesCommands.cs index 5ffc7a9..7e8987c 100644 --- a/Catalogger.Backend/Bot/Commands/IgnoreEntitiesCommands.cs +++ b/Catalogger.Backend/Bot/Commands/IgnoreEntitiesCommands.cs @@ -98,7 +98,7 @@ public class IgnoreEntitiesCommands : CommandGroup { var (_, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildConfig = await guildRepository.GetAsync(guildId); @@ -201,14 +201,14 @@ public class IgnoreEntitiesCommands : CommandGroup { var (userId, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); var member = await memberCache.TryGetAsync(guildId, userId); if (member == null) - throw new CataloggerError("Executing member not found"); + return CataloggerError.Result("Executing member not found"); var ignoredChannels = guildConfig .IgnoredChannels.Select(id => diff --git a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Channels.cs b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Channels.cs index 69b225e..b61fabc 100644 --- a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Channels.cs +++ b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Channels.cs @@ -110,14 +110,14 @@ public partial class IgnoreMessageCommands : CommandGroup { var (userId, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); var member = await memberCache.TryGetAsync(guildId, userId); if (member == null) - throw new CataloggerError("Executing member not found"); + return CataloggerError.Result("Executing member not found"); var ignoredChannels = guildConfig .Messages.IgnoredChannels.Select(id => diff --git a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Roles.cs b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Roles.cs index cf3cb10..2cc46b7 100644 --- a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Roles.cs +++ b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Roles.cs @@ -90,7 +90,7 @@ public partial class IgnoreMessageCommands { var (_, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildConfig = await guildRepository.GetAsync(guildId); diff --git a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Users.cs b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Users.cs index d89f487..15ae280 100644 --- a/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Users.cs +++ b/Catalogger.Backend/Bot/Commands/IgnoreMessageCommands.Users.cs @@ -94,7 +94,7 @@ public partial class IgnoreMessageCommands { var (userId, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild was not cached"); + return CataloggerError.Result("Guild not in cache"); var guildConfig = await guildRepository.GetAsync(guildId); diff --git a/Catalogger.Backend/Bot/Commands/InviteCommands.cs b/Catalogger.Backend/Bot/Commands/InviteCommands.cs index 7ffa031..6ec6991 100644 --- a/Catalogger.Backend/Bot/Commands/InviteCommands.cs +++ b/Catalogger.Backend/Bot/Commands/InviteCommands.cs @@ -59,7 +59,7 @@ public class InviteCommands( var (userId, guildId) = contextInjection.GetUserAndGuild(); var guildInvites = await guildApi.GetGuildInvitesAsync(guildId).GetOrThrow(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var dbInvites = await inviteRepository.GetGuildInvitesAsync(guildId); diff --git a/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs b/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs index 1ebc458..dd2ff90 100644 --- a/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs +++ b/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs @@ -45,7 +45,7 @@ public class KeyRoleCommands( { var (_, guildId) = contextInjection.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild not in cache"); + return CataloggerError.Result("Guild not in cache"); var guildRoles = roleCache.GuildRoles(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); @@ -85,7 +85,7 @@ public class KeyRoleCommands( var (_, guildId) = contextInjection.GetUserAndGuild(); var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId); if (role == null) - throw new CataloggerError("Role is not cached"); + return CataloggerError.Result("Role is not cached"); var guildConfig = await guildRepository.GetAsync(guildId); if (guildConfig.KeyRoles.Any(id => role.ID.Value == id)) @@ -111,7 +111,7 @@ public class KeyRoleCommands( var (_, guildId) = contextInjection.GetUserAndGuild(); var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId); if (role == null) - throw new CataloggerError("Role is not cached"); + return CataloggerError.Result("Role is not cached"); var guildConfig = await guildRepository.GetAsync(guildId); if (guildConfig.KeyRoles.All(id => role.ID != id)) diff --git a/Catalogger.Backend/Bot/Commands/RedirectCommands.cs b/Catalogger.Backend/Bot/Commands/RedirectCommands.cs index 586deb3..3864c54 100644 --- a/Catalogger.Backend/Bot/Commands/RedirectCommands.cs +++ b/Catalogger.Backend/Bot/Commands/RedirectCommands.cs @@ -141,7 +141,7 @@ public class RedirectCommands( { var (userId, guildId) = contextInjectionService.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild was not cached"); + return CataloggerError.Result("Guild not in cache"); var guildChannels = channelCache.GuildChannels(guildId).ToList(); var guildConfig = await guildRepository.GetAsync(guildId); diff --git a/Catalogger.Backend/Bot/Commands/WatchlistCommands.cs b/Catalogger.Backend/Bot/Commands/WatchlistCommands.cs index b47a583..c92a886 100644 --- a/Catalogger.Backend/Bot/Commands/WatchlistCommands.cs +++ b/Catalogger.Backend/Bot/Commands/WatchlistCommands.cs @@ -83,7 +83,7 @@ public class WatchlistCommands( { var (userId, guildId) = contextInjectionService.GetUserAndGuild(); if (!guildCache.TryGet(guildId, out var guild)) - throw new CataloggerError("Guild was not cached"); + return CataloggerError.Result("Guild not in cache"); var watchlist = await watchlistRepository.GetGuildWatchlistAsync(guildId); if (watchlist.Count == 0) diff --git a/Catalogger.Backend/CataloggerError.cs b/Catalogger.Backend/CataloggerError.cs index 31abf6f..40322a4 100644 --- a/Catalogger.Backend/CataloggerError.cs +++ b/Catalogger.Backend/CataloggerError.cs @@ -13,6 +13,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +using Remora.Results; +using RemoraResult = Remora.Results.Result; + namespace Catalogger.Backend; -public class CataloggerError(string message) : Exception(message) { } +public class CataloggerError(string message) : Exception(message), IResultError +{ + public static RemoraResult Result(string message) => + RemoraResult.FromError(new CataloggerError(message)); +}