diff --git a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
index 5f24b2d..e11178c 100644
--- a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
@@ -14,6 +14,7 @@
// along with this program. If not, see .
using System.ComponentModel;
+using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries;
@@ -21,14 +22,14 @@ using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
+using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects;
using Remora.Discord.Commands.Attributes;
-using Remora.Discord.Commands.Contexts;
-using Remora.Discord.Commands.Extensions;
using Remora.Discord.Commands.Feedback.Messages;
using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Commands.Services;
+using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Interactivity;
using Remora.Discord.Interactivity.Services;
using Remora.Rest.Core;
@@ -40,16 +41,26 @@ namespace Catalogger.Backend.Bot.Commands;
public class ChannelCommands(
ILogger logger,
+ Config config,
DatabaseContext db,
GuildCache guildCache,
ChannelCache channelCache,
+ IMemberCache memberCache,
IFeedbackService feedbackService,
ContextInjectionService contextInjection,
- InMemoryDataService dataService
+ InMemoryDataService dataService,
+ PermissionResolverService permissionResolver
) : CommandGroup
{
private readonly ILogger _logger = logger.ForContext();
+ private static readonly DiscordPermission[] RequiredGuildPermissions =
+ [
+ DiscordPermission.ManageGuild,
+ DiscordPermission.ViewAuditLog,
+ ];
+
+ // TODO: i hate this
[Command("check-permissions")]
[Description(
"Check for any permission issues that would prevent Catalogger from sending logs."
@@ -57,7 +68,134 @@ public class ChannelCommands(
[DiscordDefaultMemberPermissions(DiscordPermission.ManageGuild)]
public async Task CheckPermissionsAsync()
{
- throw new NotImplementedException();
+ var (userId, guildId) = contextInjection.GetUserAndGuild();
+ if (!guildCache.TryGet(guildId, out var guild))
+ throw new CataloggerError("Guild not in cache");
+
+ var embed = new EmbedBuilder().WithTitle($"Permission check for {guild.Name}");
+
+ var botUser = await memberCache.TryGetAsync(
+ guildId,
+ 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");
+
+ // We don't want to check categories or threads
+ var guildChannels = channelCache
+ .GuildChannels(guildId)
+ .Where(c =>
+ c.Type
+ is not (
+ ChannelType.GuildCategory
+ or ChannelType.PublicThread
+ or ChannelType.PrivateThread
+ or ChannelType.AnnouncementThread
+ )
+ )
+ .ToList();
+
+ // We'll only check channels the user can see, to not leak any information.
+ var checkChannels = guildChannels
+ .Where(c =>
+ {
+ var perms = permissionResolver.GetChannelPermissions(guildId, currentUser, c);
+ return perms.HasPermission(DiscordPermission.ViewChannel)
+ || perms.HasPermission(DiscordPermission.Administrator);
+ })
+ .ToList();
+
+ var ignoredChannels = guildChannels.Count - checkChannels.Count;
+ if (ignoredChannels != 0)
+ embed = embed.WithFooter(
+ $"{ignoredChannels} channel(s) were ignored as you do not have access to them"
+ );
+
+ var guildPerms = permissionResolver.GetGuildPermissions(guildId, botUser);
+ // If the bot has admin perms, we can ignore the rest--we'll never get permission errors
+ if (guildPerms.HasPermission(DiscordPermission.Administrator))
+ {
+ return await feedbackService.ReplyAsync(
+ embeds:
+ [
+ embed
+ .WithColour(DiscordUtils.Green)
+ .WithDescription("No issues found, all channels can be logged to and from!")
+ .Build()
+ .GetOrThrow(),
+ ]
+ );
+ }
+
+ var missingGuildPerms = string.Join(
+ ", ",
+ RequiredGuildPermissions.Where(p => !guildPerms.HasPermission(p))
+ );
+ if (!string.IsNullOrWhiteSpace(missingGuildPerms))
+ embed.AddField("Server-level permissions", missingGuildPerms);
+
+ var missingManageChannel = new List();
+ var missingSendMessages = new List();
+ var missingViewChannel = new List();
+ var missingReadMessageHistory = new List();
+ var missingManageWebhooks = new List();
+
+ foreach (var channel in checkChannels)
+ {
+ var channelPerms = permissionResolver.GetChannelPermissions(guildId, botUser, channel);
+ if (!channelPerms.HasPermission(DiscordPermission.ManageChannels))
+ missingManageChannel.Add(channel.ID);
+ if (!channelPerms.HasPermission(DiscordPermission.SendMessages))
+ missingSendMessages.Add(channel.ID);
+ if (!channelPerms.HasPermission(DiscordPermission.ViewChannel))
+ missingViewChannel.Add(channel.ID);
+ if (!channelPerms.HasPermission(DiscordPermission.ReadMessageHistory))
+ missingReadMessageHistory.Add(channel.ID);
+ if (!channelPerms.HasPermission(DiscordPermission.ManageWebhooks))
+ missingManageWebhooks.Add(channel.ID);
+ }
+
+ if (missingManageChannel.Count != 0)
+ embed.AddField(
+ "Manage Channel",
+ string.Join("\n", missingManageChannel.Select(id => $"<#{id}>"))
+ );
+ if (missingSendMessages.Count != 0)
+ embed.AddField(
+ "Send Messages",
+ string.Join("\n", missingSendMessages.Select(id => $"<#{id}>"))
+ );
+ if (missingReadMessageHistory.Count != 0)
+ embed.AddField(
+ "Read Message History",
+ string.Join("\n", missingReadMessageHistory.Select(id => $"<#{id}>"))
+ );
+ if (missingViewChannel.Count != 0)
+ embed.AddField(
+ "View Channel",
+ string.Join("\n", missingViewChannel.Select(id => $"<#{id}>"))
+ );
+ if (missingManageWebhooks.Count != 0)
+ embed.AddField(
+ "Manage Webhooks",
+ string.Join("\n", missingManageWebhooks.Select(id => $"<#{id}>"))
+ );
+
+ if (embed.Fields.Count == 0)
+ {
+ embed = embed
+ .WithColour(DiscordUtils.Green)
+ .WithDescription("No issues found, all channels can be logged to and from!");
+ }
+ else
+ {
+ embed = embed
+ .WithColour(DiscordUtils.Red)
+ .WithDescription("Permission issues found, please fix them and try again.");
+ }
+
+ return await feedbackService.ReplyAsync(embeds: [embed.Build().GetOrThrow()]);
}
[Command("configure-channels")]