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/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.ComponentModel; using System.ComponentModel;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache; using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database; using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries; using Catalogger.Backend.Database.Queries;
@ -21,14 +22,14 @@ using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services; using Catalogger.Backend.Services;
using Remora.Commands.Attributes; using Remora.Commands.Attributes;
using Remora.Commands.Groups; using Remora.Commands.Groups;
using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects; using Remora.Discord.API.Objects;
using Remora.Discord.Commands.Attributes; 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.Messages;
using Remora.Discord.Commands.Feedback.Services; using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Commands.Services; using Remora.Discord.Commands.Services;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Interactivity; using Remora.Discord.Interactivity;
using Remora.Discord.Interactivity.Services; using Remora.Discord.Interactivity.Services;
using Remora.Rest.Core; using Remora.Rest.Core;
@ -40,16 +41,26 @@ namespace Catalogger.Backend.Bot.Commands;
public class ChannelCommands( public class ChannelCommands(
ILogger logger, ILogger logger,
Config config,
DatabaseContext db, DatabaseContext db,
GuildCache guildCache, GuildCache guildCache,
ChannelCache channelCache, ChannelCache channelCache,
IMemberCache memberCache,
IFeedbackService feedbackService, IFeedbackService feedbackService,
ContextInjectionService contextInjection, ContextInjectionService contextInjection,
InMemoryDataService<Snowflake, ChannelCommandData> dataService InMemoryDataService<Snowflake, ChannelCommandData> dataService,
PermissionResolverService permissionResolver
) : CommandGroup ) : CommandGroup
{ {
private readonly ILogger _logger = logger.ForContext<ChannelCommands>(); private readonly ILogger _logger = logger.ForContext<ChannelCommands>();
private static readonly DiscordPermission[] RequiredGuildPermissions =
[
DiscordPermission.ManageGuild,
DiscordPermission.ViewAuditLog,
];
// TODO: i hate this
[Command("check-permissions")] [Command("check-permissions")]
[Description( [Description(
"Check for any permission issues that would prevent Catalogger from sending logs." "Check for any permission issues that would prevent Catalogger from sending logs."
@ -57,7 +68,134 @@ public class ChannelCommands(
[DiscordDefaultMemberPermissions(DiscordPermission.ManageGuild)] [DiscordDefaultMemberPermissions(DiscordPermission.ManageGuild)]
public async Task<IResult> CheckPermissionsAsync() 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")] [Command("configure-channels")]