Skip to content

Commit 1c54c6c

Browse files
Improvements to reminders
- Shows reminder text & time in modify dropdown instead of ID & text (closes #41) - Uses modals instead of select menus to prompt user for reminder IDs if they have too many to list in a select menu, and adds slash command option for reminder ID to /reminder show (closes #38)
1 parent ab824f0 commit 1c54c6c

File tree

3 files changed

+129
-48
lines changed

3 files changed

+129
-48
lines changed

Commands/ReminderCommands.cs

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@ public static async Task DeleteReminder(SlashCommandContext ctx)
247247
if (reminderDetails is null) continue;
248248
if (reminderDetails.UserId != ctx.User.Id) continue;
249249
userHasReminders = true;
250-
options.Add(new DiscordSelectComponentOption(reminderDetails.ReminderId.ToString(),
250+
options.Add(new DiscordSelectComponentOption(reminderDetails.ReminderText.Truncate(100),
251251
reminderDetails.ReminderId.ToString(),
252-
reminderDetails.ReminderText.Truncate(100)));
252+
reminderDetails.ReminderTime.Humanize()));
253253
}
254254

255255
if (!userHasReminders)
@@ -275,42 +275,53 @@ await ctx.FollowupAsync(
275275
[Description("Modify a reminder.")]
276276
public static async Task ModifyReminder(SlashCommandContext ctx)
277277
{
278-
await ctx.DeferResponseAsync(true);
279-
280-
var options = new List<DiscordSelectComponentOption>();
278+
// We can't defer this!! Want to respond with a modal if the user has >25 reminders.
281279

282280
var reminders = await Program.Db.HashGetAllAsync("reminders");
283281

284-
var userHasReminders = false;
285-
foreach (var reminder in reminders)
286-
{
287-
var reminderDetails = JsonConvert.DeserializeObject<Reminder>(reminder.Value);
282+
var userReminders = reminders.Select(x => JsonConvert.DeserializeObject<Reminder>(x.Value)).Where(r => r is not null && r.UserId == ctx.User.Id).ToList();
288283

289-
if (reminderDetails is null) continue;
290-
if (reminderDetails.UserId != ctx.User.Id) continue;
291-
userHasReminders = true;
292-
options.Add(new DiscordSelectComponentOption(reminderDetails.ReminderId.ToString(),
293-
reminderDetails.ReminderId.ToString(),
294-
reminderDetails.ReminderText.Truncate(100)));
295-
}
296-
297-
if (!userHasReminders)
284+
if (userReminders.Count == 0)
298285
{
299286
var reminderCmd = Program.ApplicationCommands.FirstOrDefault(c => c.Name == "reminder");
300287

301-
await ctx.FollowupAsync(new DiscordFollowupMessageBuilder()
288+
await ctx.RespondAsync(new DiscordInteractionResponseBuilder()
302289
.WithContent(
303290
reminderCmd is null
304291
? "You don't have any reminders!"
305292
: $"You don't have any reminders! Set one with </{reminderCmd.Name} set:{reminderCmd.Id}>.")
306293
.AsEphemeral());
307294
return;
308295
}
309-
310-
await ctx.FollowupAsync(
311-
new DiscordFollowupMessageBuilder().WithContent("Please choose a reminder to modify.")
296+
else if (userReminders.Count <= 25)
297+
{
298+
var options = new List<DiscordSelectComponentOption>();
299+
options.AddRange(userReminders.Select(reminder =>
300+
new DiscordSelectComponentOption(reminder.ReminderText.Truncate(100),
301+
reminder.ReminderId.ToString(),
302+
reminder.ReminderTime.Humanize())));
303+
304+
await ctx.RespondAsync(
305+
new DiscordInteractionResponseBuilder().WithContent("Please choose a reminder to modify.")
312306
.AddActionRowComponent(new DiscordSelectComponent("reminder-modify-dropdown", null, options))
313307
.AsEphemeral());
308+
}
309+
else
310+
{
311+
// User has more than 25 reminders. Show a modal where they are prompted to enter the ID for the reminder they want to modify.
312+
// I wanted to paginate a select menu instead, but Discord and D#+ limitations make that really difficult for now. (cba writing my own pagination)
313+
314+
var modalText = "You have a lot of reminders! Please enter the ID of the reminder you wish to modify.";
315+
var reminderListCmd = Program.ApplicationCommands.FirstOrDefault(c => c.Name == "reminder");
316+
if (reminderListCmd is not null)
317+
modalText += $" You can see reminder IDs with </reminder list:{reminderListCmd.Id}>.";
318+
319+
await ctx.RespondWithModalAsync(new DiscordModalBuilder().WithCustomId("reminder-modify-modal").WithTitle("Modify a Reminder")
320+
.AddTextDisplay(modalText)
321+
.AddTextInput(new DiscordTextInputComponent("reminder-modify-id-input"), "Reminder ID")
322+
.AddTextInput(new DiscordTextInputComponent("reminder-modify-time-input", required: false), "(Optional) Enter the new reminder time:")
323+
.AddTextInput(new DiscordTextInputComponent("reminder-modify-text-input", required: false), "(Optional) Enter the new reminder text:"));
324+
}
314325
}
315326

316327
[Command("pushback")]
@@ -453,43 +464,71 @@ await ctx.FollowupAsync(new DiscordFollowupMessageBuilder().WithContent(
453464

454465
[Command("show")]
455466
[Description("Show the details for a reminder.")]
456-
public static async Task ReminderShow(SlashCommandContext ctx)
467+
public static async Task ReminderShow(SlashCommandContext ctx, [Parameter("id"), Description("The ID of the reminder to show.")] string id)
457468
{
458469
await ctx.DeferResponseAsync(true);
459470

460-
var options = new List<DiscordSelectComponentOption>();
461-
462-
var reminders = await Program.Db.HashGetAllAsync("reminders");
471+
var reminderCmd = Program.ApplicationCommands.FirstOrDefault(x => x.Name == "reminder");
463472

464-
var userHasReminders = false;
465-
foreach (var reminder in reminders)
473+
Regex idRegex = new("[0-9]+");
474+
if (!idRegex.IsMatch(id.ToString()))
466475
{
467-
var reminderDetails = JsonConvert.DeserializeObject<Reminder>(reminder.Value);
468-
469-
if (reminderDetails!.UserId != ctx.User.Id) continue;
470-
userHasReminders = true;
471-
options.Add(new DiscordSelectComponentOption(reminderDetails.ReminderId.ToString(),
472-
reminderDetails.ReminderId.ToString(),
473-
reminderDetails.ReminderText.Truncate(100)));
476+
await ctx.FollowupAsync(new DiscordFollowupMessageBuilder()
477+
.WithContent($"The reminder ID you provided isn't correct! It should look something like this: `1234`.{(reminderCmd is null ? "" : $" You can see your reminders and their IDs with </reminder list:{reminderCmd.Id}>.")}")
478+
.AsEphemeral());
479+
return;
474480
}
475481

476-
if (!userHasReminders)
482+
Reminder reminder;
483+
try
484+
{
485+
reminder = JsonConvert.DeserializeObject<Reminder>(await Program.Db.HashGetAsync("reminders", id));
486+
}
487+
catch
477488
{
478-
var reminderCmd = Program.ApplicationCommands.FirstOrDefault(c => c.Name == "reminder");
489+
479490

480491
await ctx.FollowupAsync(new DiscordFollowupMessageBuilder()
481-
.WithContent(
482-
reminderCmd is null
483-
? "You don't have any reminders!"
484-
: $"You don't have any reminders! Set one with </{reminderCmd.Name} set:{reminderCmd.Id}>.")
485-
.AsEphemeral());
492+
.WithContent($"I couldn't find a reminder with that ID! Make sure it's correct. It should look something like this: `1234`.{(reminderCmd is null ? "" : $" You can see your reminders and their IDs with </reminder list:{reminderCmd.Id}>.")}")
493+
.AsEphemeral());
486494
return;
487495
}
488496

489-
await ctx.FollowupAsync(
490-
new DiscordFollowupMessageBuilder().WithContent("Please choose a reminder to show details for.")
491-
.AddActionRowComponent(new DiscordSelectComponent("reminder-show-dropdown", null, options))
492-
.AsEphemeral());
497+
DiscordEmbedBuilder embed = new()
498+
{
499+
Title = $"Reminder `{id}`",
500+
Description = reminder!.ReminderText,
501+
Color = Program.BotColor
502+
};
503+
504+
if (reminder.GuildId != "@me" && reminder.ReminderTime is not null)
505+
{
506+
embed.AddField("Server",
507+
$"{(await Program.Discord.GetGuildAsync(Convert.ToUInt64(reminder.GuildId))).Name}");
508+
embed.AddField("Channel", $"<#{reminder.ChannelId}>");
509+
}
510+
511+
var jumpLink = reminder.IsPrivate
512+
? $"This reminder was set privately, so the message where it was set is unavailable. Here is a link to the surrounding context: https://discord.com/channels/{reminder.GuildId}/{reminder.ChannelId}/{reminder.MessageId}/"
513+
: $"https://discord.com/channels/{reminder.GuildId}/{reminder.ChannelId}/{reminder.MessageId}";
514+
515+
embed.AddField("Jump Link", jumpLink);
516+
517+
var setTime = ((DateTimeOffset)reminder.SetTime).ToUnixTimeSeconds();
518+
519+
long reminderTime = default;
520+
if (reminder.ReminderTime is not null)
521+
reminderTime = ((DateTimeOffset)reminder.ReminderTime).ToUnixTimeSeconds();
522+
523+
embed.AddField("Set At", $"<t:{setTime}:F> (<t:{setTime}:R>)");
524+
525+
if (reminder.ReminderTime is not null)
526+
embed.AddField("Set For", $"<t:{reminderTime}:F> (<t:{reminderTime}:R>)");
527+
else
528+
embed.WithFooter(
529+
"This reminder will not be sent automatically.");
530+
531+
await ctx.FollowupAsync(new DiscordFollowupMessageBuilder().AddEmbed(embed).AsEphemeral());
493532
}
494533

495534
[GeneratedRegex("[0-9]+")]

Events/ComponentInteractionEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
public partial class ComponentInteractionEvent
44
{
5-
// Used to pass context between /reminder modify and the modal
5+
// Used to pass context between reminder modify interactions
66
// <user ID, reminder>
77
public static Dictionary<ulong, Reminder> ReminderModifyCache = new();
88

Events/ModalEvents.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,50 @@ await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder
6363
{
6464
await e.Interaction.DeferAsync(true);
6565

66+
var reminderCmd = Program.ApplicationCommands.FirstOrDefault(x => x.Name == "reminder");
67+
6668
var time = (e.Values["reminder-modify-time-input"] as TextInputModalSubmission).Value;
6769
var text = (e.Values["reminder-modify-text-input"] as TextInputModalSubmission).Value;
70+
string id = null;
71+
if (e.Values.ContainsKey("reminder-modify-id-input"))
72+
id = (e.Values["reminder-modify-id-input"] as TextInputModalSubmission).Value;
6873

69-
var reminder = ComponentInteractionEvent.ReminderModifyCache[e.Interaction.User.Id];
74+
Reminder reminder;
75+
try
76+
{
77+
if (!ComponentInteractionEvent.ReminderModifyCache.TryGetValue(e.Interaction.User.Id, out reminder))
78+
{
79+
Regex idRegex = new("[0-9]+");
80+
if (!idRegex.IsMatch(id))
81+
{
82+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
83+
.WithContent($"The reminder ID you provided isn't correct! It should look something like this: `1234`.{(reminderCmd is null ? "" : $" You can see your reminders and their IDs with </reminder list:{reminderCmd.Id}>.")}")
84+
.AsEphemeral());
85+
return;
86+
}
87+
88+
reminder = JsonConvert.DeserializeObject<Reminder>(await Program.Db.HashGetAsync("reminders", id));
89+
}
90+
}
91+
catch (ArgumentNullException)
92+
{
93+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
94+
.WithContent($"I couldn't find a reminder with that ID! Make sure it's correct. It should look something like this: `1234`.{(reminderCmd is null ? "" : $" You can see your reminders and their IDs with </reminder list:{reminderCmd.Id}>.")}")
95+
.AsEphemeral());
96+
return;
97+
}
98+
catch
99+
{
100+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder().WithContent($"Sorry, something went wrong. Please try again or contact a bot owner for help."));
101+
return;
102+
}
103+
104+
if (reminder.UserId != e.Interaction.User.Id)
105+
{
106+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
107+
.WithContent("Only the person who set that reminder can modify it!").AsEphemeral());
108+
return;
109+
}
70110

71111
if (string.IsNullOrWhiteSpace(text) && string.IsNullOrWhiteSpace(time))
72112
{
@@ -124,6 +164,8 @@ await reminderMessage.ModifyAsync(
124164
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
125165
.WithContent("Reminder modified successfully."));
126166

167+
ComponentInteractionEvent.ReminderModifyCache.Remove(e.Interaction.User.Id);
168+
127169
break;
128170
}
129171
default:

0 commit comments

Comments
 (0)