Use a modal to configure announcement messages

In addition, wrote a new static class just for receiving ModalSubmitted events in case any more modals may be handled in the future
This commit is contained in:
Noi 2022-03-13 21:24:17 -07:00
parent d700cd8ce9
commit c8d6a87966
3 changed files with 88 additions and 6 deletions

View file

@ -23,6 +23,10 @@ public class ConfigModule : BotModuleBase {
private const string HelpSubCmdMessage = "Modify the announcement message.";
private const string HelpSubCmdPing = "Set whether to ping users mentioned in the announcement.";
internal const string ModalCidAnnounce = "edit-announce";
private const string ModalComCidSingle = "msg-single";
private const string ModalComCidMulti = "msg-multi";
[SlashCommand("help", "Show information regarding announcement messages.")]
public async Task CmdAnnounceHelp() {
const string subcommands =
@ -62,11 +66,49 @@ public class ConfigModule : BotModuleBase {
[SlashCommand("set-message", HelpPfxModOnly + HelpSubCmdMessage)]
public async Task CmdSetMessage() {
// TODO implement this
var pfx = TextCommands.CommandsCommon.CommandPrefix;
await RespondAsync(":x: Sorry, changing the announcement message via slash commands is not yet available. " +
"Please use the corresponding text command: " +
$"`{pfx}config message` for single, `{pfx}config message-pl` for multi.", ephemeral: true);
var gconf = await Context.Guild.GetConfigAsync().ConfigureAwait(false);
var txtSingle = new TextInputBuilder() {
Label = "Single - Message for one birthday",
CustomId = ModalComCidSingle,
Style = TextInputStyle.Paragraph,
MaxLength = 1500,
Required = false,
Placeholder = BackgroundServices.BirthdayRoleUpdate.DefaultAnnounce,
Value = gconf.AnnounceMessages.Item1 ?? ""
};
var txtMulti = new TextInputBuilder() {
Label = "Multi - Message for multiple birthdays",
CustomId = ModalComCidMulti,
Style = TextInputStyle.Paragraph,
MaxLength = 1500,
Required = false,
Placeholder = BackgroundServices.BirthdayRoleUpdate.DefaultAnnouncePl,
Value = gconf.AnnounceMessages.Item2 ?? ""
};
var form = new ModalBuilder()
.WithTitle("Edit announcement message")
.WithCustomId(ModalCidAnnounce)
.AddTextInput(txtSingle)
.AddTextInput(txtMulti)
.Build();
await RespondWithModalAsync(form).ConfigureAwait(false);
}
internal static async Task CmdSetMessageResponse(SocketModal modal, SocketGuildChannel channel,
Dictionary<string, SocketMessageComponentData> data) {
string? newSingle = data[ModalComCidSingle].Value;
string? newMulti = data[ModalComCidMulti].Value;
if (string.IsNullOrWhiteSpace(newSingle)) newSingle = null;
if (string.IsNullOrWhiteSpace(newMulti)) newMulti = null;
var gconf = await channel.Guild.GetConfigAsync().ConfigureAwait(false);
gconf.AnnounceMessages = (newSingle, newMulti);
await gconf.UpdateAsync().ConfigureAwait(false);
await modal.RespondAsync(":white_check_mark: Announcement messages have been updated.");
}
[SlashCommand("set-ping", HelpPfxModOnly + HelpSubCmdPing)]

View file

@ -0,0 +1,39 @@
namespace BirthdayBot.ApplicationCommands;
/// <summary>
/// An instance-less class meant to handle incoming submitted modals.
/// </summary>
static class ModalResponder {
private delegate Task Responder(SocketModal modal, SocketGuildChannel channel,
Dictionary<string, SocketMessageComponentData> data);
internal static async Task DiscordClient_ModalSubmitted(ShardInstance inst, SocketModal arg) {
Responder handler = arg.Data.CustomId switch {
ConfigModule.SubCmdsConfigAnnounce.ModalCidAnnounce => ConfigModule.SubCmdsConfigAnnounce.CmdSetMessageResponse,
_ => DefaultHandler
};
var data = arg.Data.Components.ToDictionary(k => k.CustomId);
if (arg.Channel is not SocketGuildChannel channel) {
inst.Log(nameof(ModalResponder), $"Modal of type `{arg.Data.CustomId}` but channel data unavailable. " +
$"Sender ID {arg.User.Id}, name {arg.User}.");
await arg.RespondAsync(":x: Invalid request. Are you trying this command from a channel the bot can't see?")
.ConfigureAwait(false);
return;
}
try {
inst.Log(nameof(ModalResponder), $"Modal of type `{arg.Data.CustomId}` at {channel.Guild}!{arg.User}.");
await handler(arg, channel, data).ConfigureAwait(false);
} catch (Exception e) {
inst.Log(nameof(ModalResponder), $"Unhandled exception. {e}");
// TODO when implementing proper application error logging, see here
await arg.RespondAsync(ShardInstance.InternalError);
}
}
private static async Task DefaultHandler(SocketModal modal, SocketGuildChannel channel,
Dictionary<string, SocketMessageComponentData> data)
=> await modal.RespondAsync(":x: ...???");
}

View file

@ -49,6 +49,7 @@ public sealed class ShardInstance : IDisposable {
_interactionService = _services.GetRequiredService<InteractionService>();
DiscordClient.InteractionCreated += DiscordClient_InteractionCreated;
_interactionService.SlashCommandExecuted += InteractionService_SlashCommandExecuted;
DiscordClient.ModalSubmitted += modal => { return ModalResponder.DiscordClient_ModalSubmitted(this, modal); };
// Background task constructor begins background processing immediately.
_background = new ShardBackgroundWorker(this);
@ -75,7 +76,7 @@ public sealed class ShardInstance : IDisposable {
Log(nameof(ShardInstance), "Instance disposed.");
}
public void Log(string source, string message) => Program.Log($"Shard {ShardId:00}] [{source}", message);
internal void Log(string source, string message) => Program.Log($"Shard {ShardId:00}] [{source}", message);
private Task Client_Log(LogMessage arg) {
// Suppress certain messages