feat: redirect commands

This commit is contained in:
sam 2024-10-16 14:53:30 +02:00
parent 03dc16b0b3
commit bacbc6db0e
Signed by: sam
GPG key ID: 5F3C3C1B3166639D
7 changed files with 212 additions and 7 deletions

View file

@ -166,7 +166,7 @@ public class IgnoreChannelCommands(
var value = string.Join("\n", visibleCategories.Select(c => $"<#{c.Id}>")); var value = string.Join("\n", visibleCategories.Select(c => $"<#{c.Id}>"));
if (nonVisibleCategories != 0) if (nonVisibleCategories != 0)
value += value +=
$"\n\n{nonVisibleCategories} channel(s) were ignored as you do not have access to them."; $"\n\n{nonVisibleCategories} channel(s) are not shown as you do not have access to them.";
embed.AddField("Categories", value); embed.AddField("Categories", value);
} }
@ -183,7 +183,7 @@ public class IgnoreChannelCommands(
var value = string.Join("\n", visibleBase.Select(c => $"<#{c.Id}>")); var value = string.Join("\n", visibleBase.Select(c => $"<#{c.Id}>"));
if (nonVisibleBase != 0) if (nonVisibleBase != 0)
value += value +=
$"\n\n{nonVisibleBase} channel(s) were ignored as you do not have access to them."; $"\n\n{nonVisibleBase} channel(s) are not shown as you do not have access to them.";
embed.AddField("Channels", value); embed.AddField("Channels", value);
} }

View file

@ -90,6 +90,12 @@ public class InviteCommands(
)) ))
.ToList(); .ToList();
if (fields.Count == 0)
return await feedbackService.ReplyAsync(
"No invites found for this server.",
isEphemeral: true
);
return await feedbackService.SendContextualPaginatedMessageAsync( return await feedbackService.SendContextualPaginatedMessageAsync(
userId, userId,
DiscordUtils.PaginateFields( DiscordUtils.PaginateFields(

View file

@ -46,6 +46,7 @@ public class MetaCommands(
IFeedbackService feedbackService, IFeedbackService feedbackService,
ContextInjectionService contextInjection, ContextInjectionService contextInjection,
GuildCache guildCache, GuildCache guildCache,
RoleCache roleCache,
ChannelCache channelCache, ChannelCache channelCache,
EmojiCache emojiCache, EmojiCache emojiCache,
IDiscordRestChannelAPI channelApi IDiscordRestChannelAPI channelApi
@ -109,7 +110,8 @@ public class MetaCommands(
embed.AddField( embed.AddField(
"Numbers", "Numbers",
$"{CataloggerMetrics.MessagesStored.Value:N0} messages " $"{CataloggerMetrics.MessagesStored.Value:N0} messages "
+ $"from {guildCache.Size:N0} servers\nCached {channelCache.Size:N0} channels, {emojiCache.Size:N0}", + $"from {guildCache.Size:N0} servers\n"
+ $"Cached {channelCache.Size:N0} channels, {roleCache.Size:N0} roles, {emojiCache.Size:N0} emojis",
false false
); );

View file

@ -0,0 +1,196 @@
// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// 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/>.
using System.ComponentModel;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Extensions;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects;
using Remora.Discord.Commands.Attributes;
using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Commands.Services;
using Remora.Discord.Pagination.Extensions;
using IResult = Remora.Results.IResult;
namespace Catalogger.Backend.Bot.Commands;
[Group("redirects")]
[Description("Commands for configuring log redirects.")]
[DiscordDefaultMemberPermissions(DiscordPermission.ManageGuild)]
public class RedirectCommands(
DatabaseContext db,
GuildCache guildCache,
ChannelCache channelCache,
ContextInjectionService contextInjectionService,
FeedbackService feedbackService
) : CommandGroup
{
[Command("add")]
[Description("Redirect message logs from a channel or category to another channel.")]
public async Task<IResult> AddRedirectAsync(
[ChannelTypes(
ChannelType.GuildCategory,
ChannelType.GuildText,
ChannelType.GuildAnnouncement,
ChannelType.GuildForum,
ChannelType.GuildMedia,
ChannelType.GuildVoice
)]
[Description("The channel to redirect logs from")]
IChannel source,
[ChannelTypes(ChannelType.GuildText)]
[Description("The channel to redirect logs to")]
IChannel target
)
{
var (_, guildId) = contextInjectionService.GetUserAndGuild();
var guildConfig = await db.GetGuildAsync(guildId);
guildConfig.Channels.Redirects[source.ID.Value] = target.ID.Value;
db.Update(guildConfig);
await db.SaveChangesAsync();
var output =
$"Success! Edited and deleted messages from {FormatChannel(source)} will now be redirected to <#{target.ID}>.";
// Notify the user if the channel they started redirecting from was already covered by a category-level redirect.
if (
source.ParentID.IsDefined(out var parentId)
&& guildConfig.Channels.Redirects.TryGetValue(
parentId.Value.Value,
out var parentRedirect
)
&& parentRedirect != 0
)
{
output +=
$"\n**Note:** channels from the category {FormatChannel(source)} is in were already being redirected to <#{parentRedirect}>.";
}
return await feedbackService.ReplyAsync(output);
}
[Command("remove")]
[Description("Stop redirecting message logs from a channel.")]
public async Task<IResult> RemoveRedirectAsync(
[ChannelTypes(
ChannelType.GuildCategory,
ChannelType.GuildText,
ChannelType.GuildAnnouncement,
ChannelType.GuildForum,
ChannelType.GuildMedia,
ChannelType.GuildVoice
)]
IChannel source
)
{
var (_, guildId) = contextInjectionService.GetUserAndGuild();
var guildConfig = await db.GetGuildAsync(guildId);
var wasSet = guildConfig.Channels.Redirects.Remove(source.ID.Value);
var output = wasSet
? $"Removed the redirect for {FormatChannel(source)}! Message logs from"
+ $"{(source.Type == ChannelType.GuildCategory ? "that category's channels" : "that channel")}"
+ "will now be logged to the default channel, if any."
: $"Message logs from {FormatChannel(source)} were already not being redirected to another channel.";
// Warn the user if this is a non-category channel and *all* of this category's channels are being redirected.
if (
source.ParentID.IsDefined(out var parentId)
&& guildConfig.Channels.Redirects.TryGetValue(
parentId.Value.Value,
out var parentRedirect
)
&& parentRedirect != 0
)
{
var parentChannelName = $"<#{parentId}>";
if (channelCache.TryGet(parentId.Value, out var parentChannel))
parentChannelName = parentChannel.Name.Value!;
if (wasSet)
output +=
$"\nHowever, all channels in the {parentChannelName} category are being redirected to <#{parentRedirect}>, "
+ $"so removing the redirect for {FormatChannel(source)} will not take effect until that is removed as well.";
else
output +=
$"\nHowever, all channels in the {parentChannelName} category are being redirected to <#{parentRedirect}>.";
}
db.Update(guildConfig);
await db.SaveChangesAsync();
return await feedbackService.ReplyAsync(output);
}
[Command("list")]
[Description("List currently redirected channels.")]
public async Task<IResult> ListRedirectsAsync()
{
var (userId, guildId) = contextInjectionService.GetUserAndGuild();
if (!guildCache.TryGet(guildId, out var guild))
throw new CataloggerError("Guild was not cached");
var guildChannels = channelCache.GuildChannels(guildId).ToList();
var guildConfig = await db.GetGuildAsync(guildId);
var fields = new List<IEmbedField>();
foreach (var (source, target) in guildConfig.Channels.Redirects)
{
fields.Add(
new EmbedField(
Name: FormatChannelHeader(
source,
guildChannels.FirstOrDefault(c => c.ID.Value == source)
),
Value: FormatChannelText(
target,
guildChannels.FirstOrDefault(c => c.ID.Value == target)
),
IsInline: false
)
);
}
if (fields.Count == 0)
return await feedbackService.ReplyAsync(
"No channels are being redirected right now.",
isEphemeral: true
);
return await feedbackService.SendContextualPaginatedMessageAsync(
userId,
DiscordUtils.PaginateFields(fields, $"Channel redirects for {guild.Name}")
);
}
private static string FormatChannel(IChannel channel) =>
channel.Type == ChannelType.GuildCategory ? channel.Name.Value! : $"<#{channel.ID}>";
private static string FormatChannelHeader(ulong id, IChannel? channel) =>
channel != null
? channel.Type == ChannelType.GuildCategory
? $"Category {channel.Name}"
: $"#{channel.Name}"
: $"*unknown channel {id}*";
private static string FormatChannelText(ulong id, IChannel? channel) =>
channel != null
? $"#{channel.Name} (<#{channel.ID}>)"
: $"*unknown channel {id}* (<#{id}>)";
}

View file

@ -137,7 +137,7 @@ public static class DiscordExtensions
permissionSet.GetPermissions().Select(p => p.Humanize(LetterCasing.Title)) permissionSet.GetPermissions().Select(p => p.Humanize(LetterCasing.Title))
); );
public static (Snowflake, Snowflake) GetUserAndGuild( public static (Snowflake UserId, Snowflake GuildId) GetUserAndGuild(
this ContextInjectionService contextInjectionService this ContextInjectionService contextInjectionService
) )
{ {

View file

@ -88,6 +88,7 @@ builder
.WithCommandGroup<KeyRoleCommands>() .WithCommandGroup<KeyRoleCommands>()
.WithCommandGroup<InviteCommands>() .WithCommandGroup<InviteCommands>()
.WithCommandGroup<IgnoreChannelCommands>() .WithCommandGroup<IgnoreChannelCommands>()
.WithCommandGroup<RedirectCommands>()
// End command tree // End command tree
.Finish() .Finish()
.AddPagination() .AddPagination()

View file

@ -321,9 +321,9 @@ public class WebhookExecutorService(
guild.Channels.Redirects.TryGetValue(channelId.Value.Value, out var channelRedirect) guild.Channels.Redirects.TryGetValue(channelId.Value.Value, out var channelRedirect)
) )
return channelRedirect; return channelRedirect;
if (categoryRedirect != 0) return categoryRedirect != 0
return categoryRedirect; ? categoryRedirect
return GetDefaultLogChannel(guild, logChannelType); : GetDefaultLogChannel(guild, logChannelType);
} }
return GetDefaultLogChannel(guild, logChannelType); return GetDefaultLogChannel(guild, logChannelType);