refactor: return CataloggerError as Results instead of throwing in commands

This commit is contained in:
sam 2025-03-20 15:24:23 +01:00
parent 84c3b42874
commit 8a4e3ff184
Signed by: sam
GPG key ID: 5F3C3C1B3166639D
11 changed files with 57 additions and 36 deletions

View file

@ -43,6 +43,7 @@ public class ChannelCommands(
Config config, Config config,
GuildRepository guildRepository, GuildRepository guildRepository,
GuildCache guildCache, GuildCache guildCache,
GuildFetchService guildFetchService,
ChannelCache channelCache, ChannelCache channelCache,
IMemberCache memberCache, IMemberCache memberCache,
IFeedbackService feedbackService, IFeedbackService feedbackService,
@ -68,8 +69,11 @@ public class ChannelCommands(
public async Task<IResult> CheckPermissionsAsync() public async Task<IResult> CheckPermissionsAsync()
{ {
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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}"); var embed = new EmbedBuilder().WithTitle($"Permission check for {guild.Name}");
@ -78,8 +82,18 @@ public class ChannelCommands(
DiscordSnowflake.New(config.Discord.ApplicationId) DiscordSnowflake.New(config.Discord.ApplicationId)
); );
var currentUser = await memberCache.TryGetAsync(guildId, userId); var currentUser = await memberCache.TryGetAsync(guildId, userId);
if (botUser == null || currentUser == null) 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 // We don't want to check categories or threads
var guildChannels = channelCache var guildChannels = channelCache
@ -204,7 +218,7 @@ public class ChannelCommands(
{ {
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);

View file

@ -50,15 +50,15 @@ public class ChannelCommandsComponents(
public async Task<Result> OnMenuSelectionAsync(IReadOnlyList<string> values) public async Task<Result> OnMenuSelectionAsync(IReadOnlyList<string> values)
{ {
if (contextInjection.Context is not IInteractionCommandContext ctx) if (contextInjection.Context is not IInteractionCommandContext ctx)
throw new CataloggerError("No context"); return CataloggerError.Result("No context");
if (!ctx.TryGetUserID(out var userId)) 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)) 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)) 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)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
@ -76,7 +76,7 @@ public class ChannelCommandsComponents(
var state = values[0]; var state = values[0];
if (!Enum.TryParse<LogChannelType>(state, out var logChannelType)) if (!Enum.TryParse<LogChannelType>(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); var channelId = WebhookExecutorService.GetDefaultLogChannel(guildConfig, logChannelType);
string? channelMention; string? channelMention;
@ -147,15 +147,15 @@ public class ChannelCommandsComponents(
public async Task<Result> OnButtonPressedAsync(string state) public async Task<Result> OnButtonPressedAsync(string state)
{ {
if (contextInjection.Context is not IInteractionCommandContext ctx) if (contextInjection.Context is not IInteractionCommandContext ctx)
throw new CataloggerError("No context"); return CataloggerError.Result("No context");
if (!ctx.TryGetUserID(out var userId)) 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)) 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)) 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)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
@ -179,9 +179,9 @@ public class ChannelCommandsComponents(
); );
case "reset": case "reset":
if (lease.Data.CurrentPage == null) 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<LogChannelType>(lease.Data.CurrentPage, out var channelType)) if (!Enum.TryParse<LogChannelType>(lease.Data.CurrentPage, out var channelType))
throw new CataloggerError( return CataloggerError.Result(
$"Invalid config-channels CurrentPage: '{lease.Data.CurrentPage}'" $"Invalid config-channels CurrentPage: '{lease.Data.CurrentPage}'"
); );
@ -281,15 +281,15 @@ public class ChannelCommandsComponents(
public async Task<Result> OnMenuSelectionAsync(IReadOnlyList<IPartialChannel> channels) public async Task<Result> OnMenuSelectionAsync(IReadOnlyList<IPartialChannel> channels)
{ {
if (contextInjection.Context is not IInteractionCommandContext ctx) if (contextInjection.Context is not IInteractionCommandContext ctx)
throw new CataloggerError("No context"); return CataloggerError.Result("No context");
if (!ctx.TryGetUserID(out var userId)) 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)) 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)) 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)) 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 guildConfig = await guildRepository.GetAsync(guildId);
var channelId = channels[0].ID.ToUlong(); var channelId = channels[0].ID.ToUlong();
@ -305,7 +305,7 @@ public class ChannelCommandsComponents(
} }
if (!Enum.TryParse<LogChannelType>(lease.Data.CurrentPage, out var channelType)) if (!Enum.TryParse<LogChannelType>(lease.Data.CurrentPage, out var channelType))
throw new CataloggerError( return CataloggerError.Result(
$"Invalid config-channels CurrentPage '{lease.Data.CurrentPage}'" $"Invalid config-channels CurrentPage '{lease.Data.CurrentPage}'"
); );

View file

@ -98,7 +98,7 @@ public class IgnoreEntitiesCommands : CommandGroup
{ {
var (_, guildId) = contextInjection.GetUserAndGuild(); var (_, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildConfig = await guildRepository.GetAsync(guildId);
@ -201,14 +201,14 @@ public class IgnoreEntitiesCommands : CommandGroup
{ {
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
var member = await memberCache.TryGetAsync(guildId, userId); var member = await memberCache.TryGetAsync(guildId, userId);
if (member == null) if (member == null)
throw new CataloggerError("Executing member not found"); return CataloggerError.Result("Executing member not found");
var ignoredChannels = guildConfig var ignoredChannels = guildConfig
.IgnoredChannels.Select(id => .IgnoredChannels.Select(id =>

View file

@ -110,14 +110,14 @@ public partial class IgnoreMessageCommands : CommandGroup
{ {
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
var member = await memberCache.TryGetAsync(guildId, userId); var member = await memberCache.TryGetAsync(guildId, userId);
if (member == null) if (member == null)
throw new CataloggerError("Executing member not found"); return CataloggerError.Result("Executing member not found");
var ignoredChannels = guildConfig var ignoredChannels = guildConfig
.Messages.IgnoredChannels.Select(id => .Messages.IgnoredChannels.Select(id =>

View file

@ -90,7 +90,7 @@ public partial class IgnoreMessageCommands
{ {
var (_, guildId) = contextInjection.GetUserAndGuild(); var (_, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildConfig = await guildRepository.GetAsync(guildId);

View file

@ -94,7 +94,7 @@ public partial class IgnoreMessageCommands
{ {
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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); var guildConfig = await guildRepository.GetAsync(guildId);

View file

@ -59,7 +59,7 @@ public class InviteCommands(
var (userId, guildId) = contextInjection.GetUserAndGuild(); var (userId, guildId) = contextInjection.GetUserAndGuild();
var guildInvites = await guildApi.GetGuildInvitesAsync(guildId).GetOrThrow(); var guildInvites = await guildApi.GetGuildInvitesAsync(guildId).GetOrThrow();
if (!guildCache.TryGet(guildId, out var guild)) 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); var dbInvites = await inviteRepository.GetGuildInvitesAsync(guildId);

View file

@ -45,7 +45,7 @@ public class KeyRoleCommands(
{ {
var (_, guildId) = contextInjection.GetUserAndGuild(); var (_, guildId) = contextInjection.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildRoles = roleCache.GuildRoles(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
@ -85,7 +85,7 @@ public class KeyRoleCommands(
var (_, guildId) = contextInjection.GetUserAndGuild(); var (_, guildId) = contextInjection.GetUserAndGuild();
var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId); var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId);
if (role == null) if (role == null)
throw new CataloggerError("Role is not cached"); return CataloggerError.Result("Role is not cached");
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
if (guildConfig.KeyRoles.Any(id => role.ID.Value == id)) if (guildConfig.KeyRoles.Any(id => role.ID.Value == id))
@ -111,7 +111,7 @@ public class KeyRoleCommands(
var (_, guildId) = contextInjection.GetUserAndGuild(); var (_, guildId) = contextInjection.GetUserAndGuild();
var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId); var role = roleCache.GuildRoles(guildId).FirstOrDefault(r => r.ID == roleId);
if (role == null) if (role == null)
throw new CataloggerError("Role is not cached"); return CataloggerError.Result("Role is not cached");
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);
if (guildConfig.KeyRoles.All(id => role.ID != id)) if (guildConfig.KeyRoles.All(id => role.ID != id))

View file

@ -141,7 +141,7 @@ public class RedirectCommands(
{ {
var (userId, guildId) = contextInjectionService.GetUserAndGuild(); var (userId, guildId) = contextInjectionService.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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 guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await guildRepository.GetAsync(guildId); var guildConfig = await guildRepository.GetAsync(guildId);

View file

@ -83,7 +83,7 @@ public class WatchlistCommands(
{ {
var (userId, guildId) = contextInjectionService.GetUserAndGuild(); var (userId, guildId) = contextInjectionService.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild)) 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); var watchlist = await watchlistRepository.GetGuildWatchlistAsync(guildId);
if (watchlist.Count == 0) if (watchlist.Count == 0)

View file

@ -13,6 +13,13 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
using Remora.Results;
using RemoraResult = Remora.Results.Result;
namespace Catalogger.Backend; 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));
}