2024-10-14 14:56:40 +02:00
|
|
|
// 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/>.
|
|
|
|
|
|
2024-08-19 16:12:28 +02:00
|
|
|
using Catalogger.Backend.Cache.InMemoryCache;
|
2024-08-14 16:05:43 +02:00
|
|
|
using Catalogger.Backend.Database;
|
|
|
|
|
using Catalogger.Backend.Database.Queries;
|
|
|
|
|
using Catalogger.Backend.Extensions;
|
|
|
|
|
using Catalogger.Backend.Services;
|
|
|
|
|
using Remora.Discord.API.Abstractions.Objects;
|
|
|
|
|
using Remora.Discord.API.Abstractions.Rest;
|
|
|
|
|
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.Interactivity;
|
|
|
|
|
using Remora.Discord.Interactivity.Services;
|
|
|
|
|
using Remora.Rest.Core;
|
|
|
|
|
using Remora.Results;
|
|
|
|
|
|
|
|
|
|
namespace Catalogger.Backend.Bot.Commands;
|
|
|
|
|
|
|
|
|
|
public class ChannelCommandsComponents(
|
|
|
|
|
ILogger logger,
|
|
|
|
|
DatabaseContext db,
|
2024-08-19 16:12:28 +02:00
|
|
|
GuildCache guildCache,
|
|
|
|
|
ChannelCache channelCache,
|
2024-08-14 16:05:43 +02:00
|
|
|
ContextInjectionService contextInjection,
|
|
|
|
|
IFeedbackService feedbackService,
|
|
|
|
|
IDiscordRestInteractionAPI interactionApi,
|
2024-10-09 17:35:11 +02:00
|
|
|
InMemoryDataService<Snowflake, ChannelCommandData> dataService
|
|
|
|
|
) : InteractionGroup
|
2024-08-14 16:05:43 +02:00
|
|
|
{
|
|
|
|
|
private readonly ILogger _logger = logger.ForContext<ChannelCommandsComponents>();
|
|
|
|
|
|
|
|
|
|
[Button("config-channels")]
|
|
|
|
|
[SuppressInteractionResponse(true)]
|
|
|
|
|
public async Task<Result> OnButtonPressedAsync(string state)
|
|
|
|
|
{
|
2024-10-09 17:35:11 +02:00
|
|
|
if (contextInjection.Context is not IInteractionCommandContext ctx)
|
|
|
|
|
throw new CataloggerError("No context");
|
|
|
|
|
if (!ctx.TryGetUserID(out var userId))
|
|
|
|
|
throw new CataloggerError("No user ID in context");
|
|
|
|
|
if (!ctx.Interaction.Message.TryGet(out var msg))
|
|
|
|
|
throw new CataloggerError("No message ID in context");
|
|
|
|
|
if (!ctx.TryGetGuildID(out var guildId))
|
|
|
|
|
throw new CataloggerError("No guild ID in context");
|
|
|
|
|
if (!guildCache.TryGet(guildId, out var guild))
|
|
|
|
|
throw new CataloggerError("Guild not in cache");
|
2024-08-14 16:05:43 +02:00
|
|
|
var guildChannels = channelCache.GuildChannels(guildId).ToList();
|
|
|
|
|
var guildConfig = await db.GetGuildAsync(guildId);
|
|
|
|
|
|
|
|
|
|
var result = await dataService.LeaseDataAsync(msg.ID);
|
|
|
|
|
await using var lease = result.GetOrThrow();
|
|
|
|
|
if (lease.Data.UserId != userId)
|
|
|
|
|
{
|
2024-10-09 17:35:11 +02:00
|
|
|
return (Result)
|
2024-10-14 00:26:17 +02:00
|
|
|
await feedbackService.ReplyAsync(
|
2024-10-09 17:35:11 +02:00
|
|
|
"This is not your configuration menu.",
|
2024-10-14 00:26:17 +02:00
|
|
|
isEphemeral: true
|
2024-10-09 17:35:11 +02:00
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
|
{
|
|
|
|
|
case "close":
|
2024-10-09 17:35:11 +02:00
|
|
|
return await interactionApi.UpdateMessageAsync(
|
|
|
|
|
ctx.Interaction,
|
|
|
|
|
new InteractionMessageCallbackData(Components: Array.Empty<IMessageComponent>())
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
case "reset":
|
|
|
|
|
if (lease.Data.CurrentPage == null)
|
|
|
|
|
throw new CataloggerError("CurrentPage was null in reset button callback");
|
|
|
|
|
if (!Enum.TryParse<LogChannelType>(lease.Data.CurrentPage, out var channelType))
|
2024-10-09 17:35:11 +02:00
|
|
|
throw new CataloggerError(
|
|
|
|
|
$"Invalid config-channels CurrentPage: '{lease.Data.CurrentPage}'"
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
|
|
|
|
|
// TODO: figure out some way to make this less verbose?
|
|
|
|
|
switch (channelType)
|
|
|
|
|
{
|
|
|
|
|
case LogChannelType.GuildUpdate:
|
|
|
|
|
guildConfig.Channels.GuildUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildEmojisUpdate:
|
|
|
|
|
guildConfig.Channels.GuildEmojisUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleCreate:
|
|
|
|
|
guildConfig.Channels.GuildRoleCreate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleUpdate:
|
|
|
|
|
guildConfig.Channels.GuildRoleUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleDelete:
|
|
|
|
|
guildConfig.Channels.GuildRoleDelete = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelCreate:
|
|
|
|
|
guildConfig.Channels.ChannelCreate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelUpdate:
|
|
|
|
|
guildConfig.Channels.ChannelUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelDelete:
|
|
|
|
|
guildConfig.Channels.ChannelDelete = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberAdd:
|
|
|
|
|
guildConfig.Channels.GuildMemberAdd = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildKeyRoleUpdate:
|
|
|
|
|
guildConfig.Channels.GuildKeyRoleUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberNickUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberNickUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberAvatarUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberAvatarUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberRemove:
|
|
|
|
|
guildConfig.Channels.GuildMemberRemove = 0;
|
|
|
|
|
break;
|
2024-10-11 20:38:53 +02:00
|
|
|
case LogChannelType.GuildMemberTimeout:
|
|
|
|
|
guildConfig.Channels.GuildMemberTimeout = 0;
|
|
|
|
|
break;
|
2024-08-14 16:05:43 +02:00
|
|
|
case LogChannelType.GuildMemberKick:
|
|
|
|
|
guildConfig.Channels.GuildMemberKick = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildBanAdd:
|
|
|
|
|
guildConfig.Channels.GuildBanAdd = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildBanRemove:
|
|
|
|
|
guildConfig.Channels.GuildBanRemove = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.InviteCreate:
|
|
|
|
|
guildConfig.Channels.InviteCreate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.InviteDelete:
|
|
|
|
|
guildConfig.Channels.InviteDelete = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageUpdate:
|
|
|
|
|
guildConfig.Channels.MessageUpdate = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageDelete:
|
|
|
|
|
guildConfig.Channels.MessageDelete = 0;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageDeleteBulk:
|
|
|
|
|
guildConfig.Channels.MessageDeleteBulk = 0;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.Update(guildConfig);
|
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
|
goto case "return";
|
|
|
|
|
case "return":
|
|
|
|
|
var (e, c) = ChannelCommands.BuildRootMenu(guildChannels, guild, guildConfig);
|
2024-10-09 17:35:11 +02:00
|
|
|
await interactionApi.UpdateMessageAsync(
|
|
|
|
|
ctx.Interaction,
|
|
|
|
|
new InteractionMessageCallbackData(Embeds: e, Components: c)
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
lease.Data = new ChannelCommandData(userId, CurrentPage: null);
|
|
|
|
|
return Result.Success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Enum.TryParse<LogChannelType>(state, out var logChannelType))
|
|
|
|
|
throw new CataloggerError($"Invalid config-channels state {state}");
|
|
|
|
|
|
|
|
|
|
var channelId = WebhookExecutorService.GetDefaultLogChannel(guildConfig, logChannelType);
|
|
|
|
|
string? channelMention;
|
2024-10-09 17:35:11 +02:00
|
|
|
if (channelId is 0)
|
|
|
|
|
channelMention = null;
|
|
|
|
|
else if (guildChannels.All(c => c.ID != channelId))
|
|
|
|
|
channelMention = $"unknown channel {channelId}";
|
|
|
|
|
else
|
|
|
|
|
channelMention = $"<#{channelId}>";
|
2024-08-14 16:05:43 +02:00
|
|
|
|
|
|
|
|
List<IEmbed> embeds =
|
|
|
|
|
[
|
|
|
|
|
new Embed(
|
|
|
|
|
Title: ChannelCommands.PrettyLogTypeName(logChannelType),
|
|
|
|
|
Description: channelMention == null
|
|
|
|
|
? "This event is not currently logged.\nTo start logging it somewhere, select a channel below."
|
2024-10-09 17:35:11 +02:00
|
|
|
: $"This event is currently set to log to {channelMention}."
|
|
|
|
|
+ "\nTo change where it is logged, select a channel below."
|
|
|
|
|
+ "\nTo disable logging this event entirely, select \"Stop logging\" below.",
|
|
|
|
|
Colour: DiscordUtils.Purple
|
|
|
|
|
),
|
2024-08-14 16:05:43 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
List<IMessageComponent> components =
|
|
|
|
|
[
|
2024-10-09 17:35:11 +02:00
|
|
|
new ActionRowComponent(
|
|
|
|
|
new[]
|
|
|
|
|
{
|
|
|
|
|
new ChannelSelectComponent(
|
|
|
|
|
CustomID: CustomIDHelpers.CreateSelectMenuID("config-channels"),
|
|
|
|
|
ChannelTypes: new[] { ChannelType.GuildText }
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
new ActionRowComponent(
|
|
|
|
|
new[]
|
|
|
|
|
{
|
|
|
|
|
new ButtonComponent(
|
|
|
|
|
ButtonComponentStyle.Danger,
|
|
|
|
|
Label: "Stop logging",
|
|
|
|
|
CustomID: CustomIDHelpers.CreateButtonIDWithState(
|
|
|
|
|
"config-channels",
|
|
|
|
|
"reset"
|
|
|
|
|
),
|
|
|
|
|
IsDisabled: channelMention == null
|
|
|
|
|
),
|
|
|
|
|
new ButtonComponent(
|
|
|
|
|
ButtonComponentStyle.Secondary,
|
|
|
|
|
Label: "Return to menu",
|
|
|
|
|
CustomID: CustomIDHelpers.CreateButtonIDWithState(
|
|
|
|
|
"config-channels",
|
|
|
|
|
"return"
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
),
|
2024-08-14 16:05:43 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
lease.Data = new ChannelCommandData(userId, CurrentPage: state);
|
2024-10-09 17:35:11 +02:00
|
|
|
return await interactionApi.UpdateMessageAsync(
|
|
|
|
|
ctx.Interaction,
|
|
|
|
|
new InteractionMessageCallbackData(Embeds: embeds, Components: components)
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SelectMenu("config-channels")]
|
|
|
|
|
[SuppressInteractionResponse(true)]
|
|
|
|
|
public async Task<Result> OnMenuSelectionAsync(IReadOnlyList<IPartialChannel> channels)
|
|
|
|
|
{
|
2024-10-09 17:35:11 +02:00
|
|
|
if (contextInjection.Context is not IInteractionCommandContext ctx)
|
|
|
|
|
throw new CataloggerError("No context");
|
|
|
|
|
if (!ctx.TryGetUserID(out var userId))
|
|
|
|
|
throw new CataloggerError("No user ID in context");
|
|
|
|
|
if (!ctx.Interaction.Message.TryGet(out var msg))
|
|
|
|
|
throw new CataloggerError("No message ID in context");
|
|
|
|
|
if (!ctx.TryGetGuildID(out var guildId))
|
|
|
|
|
throw new CataloggerError("No guild ID in context");
|
|
|
|
|
if (!guildCache.TryGet(guildId, out var guild))
|
|
|
|
|
throw new CataloggerError("Guild not in cache");
|
2024-08-14 16:05:43 +02:00
|
|
|
var guildConfig = await db.GetGuildAsync(guildId);
|
|
|
|
|
var channelId = channels[0].ID.ToUlong();
|
|
|
|
|
|
|
|
|
|
var result = await dataService.LeaseDataAsync(msg.ID);
|
|
|
|
|
await using var lease = result.GetOrThrow();
|
|
|
|
|
if (lease.Data.UserId != userId)
|
|
|
|
|
{
|
2024-10-09 17:35:11 +02:00
|
|
|
return (Result)
|
2024-10-14 00:26:17 +02:00
|
|
|
await feedbackService.ReplyAsync(
|
2024-10-09 17:35:11 +02:00
|
|
|
"This is not your configuration menu.",
|
2024-10-14 00:26:17 +02:00
|
|
|
isEphemeral: true
|
2024-10-09 17:35:11 +02:00
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Enum.TryParse<LogChannelType>(lease.Data.CurrentPage, out var channelType))
|
2024-10-09 17:35:11 +02:00
|
|
|
throw new CataloggerError(
|
|
|
|
|
$"Invalid config-channels CurrentPage '{lease.Data.CurrentPage}'"
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
|
|
|
|
|
switch (channelType)
|
|
|
|
|
{
|
|
|
|
|
case LogChannelType.GuildUpdate:
|
|
|
|
|
guildConfig.Channels.GuildUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildEmojisUpdate:
|
|
|
|
|
guildConfig.Channels.GuildEmojisUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleCreate:
|
|
|
|
|
guildConfig.Channels.GuildRoleCreate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleUpdate:
|
|
|
|
|
guildConfig.Channels.GuildRoleUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildRoleDelete:
|
|
|
|
|
guildConfig.Channels.GuildRoleDelete = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelCreate:
|
|
|
|
|
guildConfig.Channels.ChannelCreate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelUpdate:
|
|
|
|
|
guildConfig.Channels.ChannelUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.ChannelDelete:
|
|
|
|
|
guildConfig.Channels.ChannelDelete = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberAdd:
|
|
|
|
|
guildConfig.Channels.GuildMemberAdd = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildKeyRoleUpdate:
|
|
|
|
|
guildConfig.Channels.GuildKeyRoleUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberNickUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberNickUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberAvatarUpdate:
|
|
|
|
|
guildConfig.Channels.GuildMemberAvatarUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildMemberRemove:
|
|
|
|
|
guildConfig.Channels.GuildMemberRemove = channelId;
|
|
|
|
|
break;
|
2024-10-11 20:38:53 +02:00
|
|
|
case LogChannelType.GuildMemberTimeout:
|
|
|
|
|
guildConfig.Channels.GuildMemberTimeout = channelId;
|
|
|
|
|
break;
|
2024-08-14 16:05:43 +02:00
|
|
|
case LogChannelType.GuildMemberKick:
|
|
|
|
|
guildConfig.Channels.GuildMemberKick = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildBanAdd:
|
|
|
|
|
guildConfig.Channels.GuildBanAdd = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.GuildBanRemove:
|
|
|
|
|
guildConfig.Channels.GuildBanRemove = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.InviteCreate:
|
|
|
|
|
guildConfig.Channels.InviteCreate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.InviteDelete:
|
|
|
|
|
guildConfig.Channels.InviteDelete = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageUpdate:
|
|
|
|
|
guildConfig.Channels.MessageUpdate = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageDelete:
|
|
|
|
|
guildConfig.Channels.MessageDelete = channelId;
|
|
|
|
|
break;
|
|
|
|
|
case LogChannelType.MessageDeleteBulk:
|
|
|
|
|
guildConfig.Channels.MessageDeleteBulk = channelId;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.Update(guildConfig);
|
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
List<IEmbed> embeds =
|
|
|
|
|
[
|
|
|
|
|
new Embed(
|
|
|
|
|
Title: ChannelCommands.PrettyLogTypeName(channelType),
|
2024-10-09 17:35:11 +02:00
|
|
|
Description: $"This event is currently set to log to <#{channelId}>."
|
|
|
|
|
+ "\nTo change where it is logged, select a channel below."
|
|
|
|
|
+ "\nTo disable logging this event entirely, select \"Stop logging\" below.",
|
|
|
|
|
Colour: DiscordUtils.Purple
|
|
|
|
|
),
|
2024-08-14 16:05:43 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
List<IMessageComponent> components =
|
|
|
|
|
[
|
2024-10-09 17:35:11 +02:00
|
|
|
new ActionRowComponent(
|
|
|
|
|
new[]
|
|
|
|
|
{
|
|
|
|
|
new ChannelSelectComponent(
|
|
|
|
|
CustomID: CustomIDHelpers.CreateSelectMenuID("config-channels"),
|
|
|
|
|
ChannelTypes: new[] { ChannelType.GuildText }
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
new ActionRowComponent(
|
|
|
|
|
new[]
|
|
|
|
|
{
|
|
|
|
|
new ButtonComponent(
|
|
|
|
|
ButtonComponentStyle.Danger,
|
|
|
|
|
Label: "Stop logging",
|
|
|
|
|
CustomID: CustomIDHelpers.CreateButtonIDWithState(
|
|
|
|
|
"config-channels",
|
|
|
|
|
"reset"
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
new ButtonComponent(
|
|
|
|
|
ButtonComponentStyle.Secondary,
|
|
|
|
|
Label: "Return to menu",
|
|
|
|
|
CustomID: CustomIDHelpers.CreateButtonIDWithState(
|
|
|
|
|
"config-channels",
|
|
|
|
|
"return"
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
),
|
2024-08-14 16:05:43 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
lease.Data = lease.Data with { UserId = userId };
|
2024-10-09 17:35:11 +02:00
|
|
|
return await interactionApi.UpdateMessageAsync(
|
|
|
|
|
ctx.Interaction,
|
|
|
|
|
new InteractionMessageCallbackData(Embeds: embeds, Components: components)
|
|
|
|
|
);
|
2024-08-14 16:05:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 17:35:11 +02:00
|
|
|
public record ChannelCommandData(Snowflake UserId, string? CurrentPage);
|