feat: check permissions command

This commit is contained in:
sam 2024-10-15 01:32:59 +02:00
parent af437ff88c
commit a2b09969d3
Signed by: sam
GPG key ID: 5F3C3C1B3166639D

View file

@ -14,6 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<Snowflake, ChannelCommandData> dataService
InMemoryDataService<Snowflake, ChannelCommandData> dataService,
PermissionResolverService permissionResolver
) : CommandGroup
{
private readonly ILogger _logger = logger.ForContext<ChannelCommands>();
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<IResult> 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<Snowflake>();
var missingSendMessages = new List<Snowflake>();
var missingViewChannel = new List<Snowflake>();
var missingReadMessageHistory = new List<Snowflake>();
var missingManageWebhooks = new List<Snowflake>();
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")]