Skip to content

Commit d1203e4

Browse files
Merge branch 'main' into floatingmilkshake/dsp-command-migration
2 parents 15f5e94 + 4dbd562 commit d1203e4

15 files changed

+330
-42
lines changed

Commands/AnnouncementCmds.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ public async Task AnnounceBuildSlashCommand(SlashCommandContext ctx,
2929
[Parameter("lockdown"), Description("Set 0 to not lock. Lock the channel for a certain period of time after announcing the build.")] string lockdownTime = "auto"
3030
)
3131
{
32-
if (Program.cfgjson.InsiderCommandLockedToChannel != 0 && ctx.Channel.Id != Program.cfgjson.InsiderCommandLockedToChannel)
32+
if (Program.cfgjson.InsidersChannel != 0 && ctx.Channel.Id != Program.cfgjson.InsidersChannel)
3333
{
34-
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} This command only works in <#{Program.cfgjson.InsiderCommandLockedToChannel}>!", ephemeral: true);
34+
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} This command only works in <#{Program.cfgjson.InsidersChannel}>!", ephemeral: true);
3535
return;
3636
}
3737

Commands/DebugCmds.cs

+26-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public async Task MuteDebug(TextCommandContext ctx, DiscordUser targetUser = def
3232
string strOut = "";
3333
if (targetUser == default)
3434
{
35-
var muteList = Program.db.HashGetAll("mutes").ToDictionary();
35+
var muteList = (await Program.db.HashGetAllAsync("mutes")).ToDictionary();
3636
if (muteList is null | muteList.Keys.Count == 0)
3737
{
3838
await ctx.RespondAsync("No mutes found in database!");
@@ -71,7 +71,7 @@ public async Task BanDebug(TextCommandContext ctx, DiscordUser targetUser = defa
7171
string strOut = "";
7272
if (targetUser == default)
7373
{
74-
var banList = Program.db.HashGetAll("bans").ToDictionary();
74+
var banList = (await Program.db.HashGetAllAsync("bans")).ToDictionary();
7575
if (banList is null | banList.Keys.Count == 0)
7676
{
7777
await ctx.RespondAsync("No bans found in database!");
@@ -189,7 +189,7 @@ public async Task MostWarningsCmd(TextCommandContext ctx)
189189
{
190190
if (ulong.TryParse(key.ToString(), out ulong number))
191191
{
192-
var warnings = Program.db.HashGetAll(key);
192+
var warnings = await Program.db.HashGetAllAsync(key);
193193
Dictionary<long, MemberPunishment> warningdict = new();
194194
foreach (var warning in warnings)
195195
{
@@ -625,6 +625,29 @@ public async Task SearchMembersCmd(TextCommandContext ctx, string regex)
625625
await ctx.Channel.SendMessageAsync(await StringHelpers.CodeOrHasteBinAsync(JsonConvert.SerializeObject(memberIdsTonames, Formatting.Indented), "json"));
626626
}
627627

628+
[Command("testnre")]
629+
[Description("throw a System.NullReferenceException error. dont spam this please.")]
630+
[IsBotOwner]
631+
public async Task ThrowNRE(TextCommandContext ctx, bool catchAsWarning = false)
632+
{
633+
if (catchAsWarning)
634+
{
635+
try
636+
{
637+
throw new NullReferenceException();
638+
}
639+
catch (NullReferenceException e)
640+
{
641+
ctx.Client.Logger.LogWarning(e, "logging test NRE as warning");
642+
await ctx.RespondAsync("thrown NRE and logged as warning, check logs");
643+
}
644+
}
645+
else
646+
{
647+
throw new NullReferenceException();
648+
}
649+
}
650+
628651
private static async Task<(bool success, ulong failedOverwrite)> ImportOverridesFromChannelAsync(DiscordChannel channel)
629652
{
630653
// Imports overrides from the specified channel to the database. See 'debug overrides import' and 'debug overrides importall'

Commands/InsidersInteractions.cs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace Cliptok.Commands.InteractionCommands
2+
{
3+
public class InsidersInteractions
4+
{
5+
[Command("send-insiders-info-buttons"), Description("Sends a message with buttons to get Insider roles for #insiders-info.")]
6+
[RequireHomeserverPerm(ServerPermLevel.Admin, ownerOverride: true), RequirePermissions(permissions: DiscordPermission.ModerateMembers)]
7+
public static async Task SendInsidersInfoButtonMessage(SlashCommandContext ctx)
8+
{
9+
if (Program.cfgjson.InsiderInfoChannel != 0 && ctx.Channel.Id != Program.cfgjson.InsiderInfoChannel)
10+
{
11+
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} This command only works in <#{Program.cfgjson.InsiderInfoChannel}>!", ephemeral: true);
12+
return;
13+
}
14+
15+
DiscordComponent[] buttons =
16+
[
17+
new DiscordButtonComponent(DiscordButtonStyle.Primary, "insiders-info-roles-menu-callback", "Choose your Insider roles"),
18+
new DiscordButtonComponent(DiscordButtonStyle.Secondary, "insiders-info-chat-btn-callback", "I just want to chat for now")
19+
];
20+
21+
string insidersChannelMention;
22+
if (Program.cfgjson.InsidersChannel == 0)
23+
{
24+
insidersChannelMention = "#insiders";
25+
Program.discord.Logger.LogWarning("#insiders-info message sent with hardcoded #insiders mention! Is insidersChannel set in config.json?");
26+
}
27+
else
28+
{
29+
insidersChannelMention = $"<#{Program.cfgjson.InsidersChannel}>";
30+
}
31+
32+
var builder = new DiscordInteractionResponseBuilder()
33+
.WithContent($"{Program.cfgjson.Emoji.Insider} Choose your Insider roles here! Or, you can choose to chat in {insidersChannelMention} without being notified about new builds.")
34+
.AddComponents(buttons);
35+
36+
await ctx.RespondAsync(builder);
37+
}
38+
}
39+
}

Commands/UserNoteCmds.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync
222222

223223
var user = await ctx.Client.GetUserAsync((ulong)useroption.Value);
224224

225-
var notes = Program.db.HashGetAll(user.Id.ToString())
225+
var notes = (await Program.db.HashGetAllAsync(user.Id.ToString()))
226226
.Where(x => JsonConvert.DeserializeObject<UserNote>(x.Value).Type == WarningType.Note).ToDictionary(
227227
x => x.Name.ToString(),
228228
x => JsonConvert.DeserializeObject<UserNote>(x.Value)

Commands/WarningCmds.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync
165165

166166
var user = await ctx.Client.GetUserAsync((ulong)useroption.Value);
167167

168-
var warnings = Program.db.HashGetAll(user.Id.ToString())
168+
var warnings = (await Program.db.HashGetAllAsync(user.Id.ToString()))
169169
.Where(x => JsonConvert.DeserializeObject<UserWarning>(x.Value).Type == WarningType.Warning).ToDictionary(
170170
x => x.Name.ToString(),
171171
x => JsonConvert.DeserializeObject<UserWarning>(x.Value)
@@ -618,7 +618,7 @@ public async Task MostWarningsCmd(TextCommandContext ctx)
618618
{
619619
if (ulong.TryParse(key.ToString(), out ulong number))
620620
{
621-
counts[key.ToString()] = Program.db.HashGetAll(key).Count(x => JsonConvert.DeserializeObject<UserWarning>(x.Value.ToString()).Type == WarningType.Warning);
621+
counts[key.ToString()] = (await Program.db.HashGetAllAsync(key)).Count(x => JsonConvert.DeserializeObject<UserWarning>(x.Value.ToString()).Type == WarningType.Warning);
622622
}
623623
}
624624

@@ -654,7 +654,7 @@ public async Task MostWarningsDayCmd(TextCommandContext ctx)
654654
{
655655
if (ulong.TryParse(key.ToString(), out ulong number))
656656
{
657-
var warningsOutput = Program.db.HashGetAll(key.ToString()).ToDictionary(
657+
var warningsOutput = (await Program.db.HashGetAllAsync(key.ToString())).ToDictionary(
658658
x => x.Name.ToString(),
659659
x => JsonConvert.DeserializeObject<UserWarning>(x.Value)
660660
);

Events/InteractionEvents.cs

+177
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,183 @@ await LogChannelHelper.LogDeletedMessagesAsync(
200200
// Respond
201201
await e.Message.ModifyAsync(new DiscordMessageBuilder().WithContent($"{cfgjson.Emoji.Success} Override successfully added. <@{newOverwrite.Id}> already had an override in <#{pendingOverride.ChannelId}>, so here are their new permissions:\n**Allowed:** {newOverwrite.Allowed}\n**Denied:** {newOverwrite.Denied}"));
202202
}
203+
else if (e.Id == "insiders-info-roles-menu-callback")
204+
{
205+
// Shows a menu in #insider-info that allows a user to toggle their Insider roles
206+
207+
// Defer interaction
208+
await e.Interaction.DeferAsync(ephemeral: true);
209+
210+
// Fetch member
211+
var member = await e.Guild.GetMemberAsync(e.User.Id);
212+
213+
// Fetch Insider roles to check whether member already has them
214+
var insiderCanaryRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderCanary);
215+
var insiderDevRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderDev);
216+
var insiderBetaRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderBeta);
217+
var insiderRPRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderRP);
218+
var insider10RPRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.Insider10RP);
219+
var patchTuesdayRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.PatchTuesday);
220+
221+
// Show menu with current Insider roles, apply new roles based on user selection
222+
var menu = new DiscordSelectComponent("insiders-info-roles-menu-response-callback", "Choose your Insider roles",
223+
new List<DiscordSelectComponentOption>()
224+
{
225+
new("Windows 11 Canary channel", "insiders-info-w11-canary", isDefault: member.Roles.Contains(insiderCanaryRole)),
226+
new("Windows 11 Dev channel", "insiders-info-w11-dev", isDefault: member.Roles.Contains(insiderDevRole)),
227+
new("Windows 11 Beta channel", "insiders-info-w11-beta", isDefault: member.Roles.Contains(insiderBetaRole)),
228+
new("Windows 11 Release Preview channel", "insiders-info-w11-rp", isDefault: member.Roles.Contains(insiderRPRole)),
229+
new("Windows 10 Release Preview channel", "insiders-info-w10-rp", isDefault: member.Roles.Contains(insider10RPRole)),
230+
new("Patch Tuesday", "insiders-info-pt", isDefault: member.Roles.Contains(patchTuesdayRole)),
231+
}, minOptions: 0, maxOptions: 6);
232+
233+
var builder = new DiscordFollowupMessageBuilder()
234+
.WithContent($"{cfgjson.Emoji.Insider} Use the menu below to toggle your Insider roles!")
235+
.AddComponents(menu)
236+
.AsEphemeral(true);
237+
238+
await e.Interaction.CreateFollowupMessageAsync(builder);
239+
}
240+
else if (e.Id == "insiders-info-roles-menu-response-callback")
241+
{
242+
// User has selected new Insider roles w/ menu above
243+
// Compare selection against current roles; add or remove roles as necessary to match selection
244+
245+
// Defer
246+
await e.Interaction.DeferAsync(ephemeral: true);
247+
248+
// Get member
249+
var member = await e.Guild.GetMemberAsync(e.User.Id);
250+
251+
// Map role select options to role IDs
252+
var insiderRoles = new Dictionary<string, ulong>
253+
{
254+
{ "insiders-info-w11-canary", cfgjson.UserRoles.InsiderCanary },
255+
{ "insiders-info-w11-dev", cfgjson.UserRoles.InsiderDev },
256+
{ "insiders-info-w11-beta", cfgjson.UserRoles.InsiderBeta },
257+
{ "insiders-info-w11-rp", cfgjson.UserRoles.InsiderRP },
258+
{ "insiders-info-w10-rp", cfgjson.UserRoles.Insider10RP },
259+
{ "insiders-info-pt", cfgjson.UserRoles.PatchTuesday }
260+
};
261+
262+
// Get a list of the member's current roles that we can add to or remove from
263+
// Then we can apply this in a single request with member.ModifyAsync to avoid making repeated member update requests
264+
List<DiscordRole> memberRoles = member.Roles.ToList();
265+
266+
var selection = e.Values.Select(x => insiderRoles[x]).ToList();
267+
268+
foreach (var roleId in insiderRoles.Values)
269+
{
270+
var role = await e.Guild.GetRoleAsync(roleId);
271+
272+
if (selection.Contains(roleId))
273+
{
274+
// Member should have the role
275+
if (!memberRoles.Contains(role))
276+
memberRoles.Add(role);
277+
}
278+
else
279+
{
280+
// Member should not have the role
281+
if (memberRoles.Contains(role))
282+
memberRoles.Remove(role);
283+
}
284+
}
285+
286+
// Apply roles
287+
await member.ModifyAsync(x => x.Roles = memberRoles);
288+
289+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder().WithContent($"{cfgjson.Emoji.Success} Your Insider roles have been updated!").AsEphemeral(true));
290+
}
291+
else if (e.Id == "insiders-info-chat-btn-callback")
292+
{
293+
// Button in #insiders-info that checks whether user has 'insiderChat' role and asks them to confirm granting/revoking it
294+
295+
// Defer
296+
await e.Interaction.DeferAsync(ephemeral: true);
297+
298+
// Get member
299+
var member = await e.Guild.GetMemberAsync(e.User.Id);
300+
301+
// Get insider chat role
302+
var insiderChatRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderChat);
303+
304+
// Check whether member already has any insider roles
305+
var insiderRoles = new List<ulong>()
306+
{
307+
cfgjson.UserRoles.InsiderCanary,
308+
cfgjson.UserRoles.InsiderDev,
309+
cfgjson.UserRoles.InsiderBeta,
310+
cfgjson.UserRoles.InsiderRP,
311+
cfgjson.UserRoles.Insider10RP,
312+
cfgjson.UserRoles.PatchTuesday
313+
};
314+
if (member.Roles.Any(x => insiderRoles.Contains(x.Id)))
315+
{
316+
// Member already has an insider role, thus already has access to #insiders
317+
// No need for the chat role too
318+
319+
string insidersMention;
320+
if (cfgjson.InsidersChannel == 0)
321+
insidersMention = "#insiders";
322+
else
323+
insidersMention = $"<#{cfgjson.InsidersChannel}>";
324+
325+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
326+
.WithContent($"You already have Insider roles, so you already have access to chat in {insidersMention}!")
327+
.AsEphemeral(true));
328+
329+
return;
330+
}
331+
332+
if (member.Roles.Contains(insiderChatRole))
333+
{
334+
// Member already has the role
335+
// Ask them if they'd like to remove it
336+
var confirmResponse = new DiscordFollowupMessageBuilder()
337+
.WithContent($"{cfgjson.Emoji.Warning} You already have the {insiderChatRole.Mention} role! Would you like to remove it?")
338+
.AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Danger, "insiders-info-chat-btn-remove-confirm-callback", "Remove"));
339+
340+
await e.Interaction.CreateFollowupMessageAsync(confirmResponse);
341+
}
342+
else
343+
{
344+
// Member does not have the role; show a confirmation message with a button that will give it to them
345+
await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
346+
.WithContent($"{cfgjson.Emoji.Warning} Please note that <#{cfgjson.InsidersChannel}> is **not for tech support**! If you need tech support, please ask in the appropriate channels instead. Press the button to acknowledge this and get the {insiderChatRole.Mention} role.")
347+
.AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Secondary, "insiders-info-chat-btn-confirm-callback", "I understand")));
348+
}
349+
}
350+
else if (e.Id == "insiders-info-chat-btn-confirm-callback")
351+
{
352+
// Confirmation for granting insiderChat role, see above
353+
354+
// Defer
355+
await e.Interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
356+
357+
// Give member insider chat role
358+
var member = await e.Guild.GetMemberAsync(e.User.Id);
359+
var insiderChatRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderChat);
360+
await member.GrantRoleAsync(insiderChatRole);
361+
362+
// Respond
363+
await e.Interaction.EditFollowupMessageAsync(e.Message.Id, new DiscordWebhookBuilder().WithContent($"{cfgjson.Emoji.Success} You have been given the {insiderChatRole.Mention} role!"));
364+
}
365+
else if (e.Id == "insiders-info-chat-btn-remove-confirm-callback")
366+
{
367+
// Confirmation for revoking insiderChat role, see above
368+
369+
// Defer
370+
await e.Interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
371+
372+
// Get member
373+
var member = await e.Guild.GetMemberAsync(e.User.Id);
374+
375+
var insiderChatRole = await e.Guild.GetRoleAsync(cfgjson.UserRoles.InsiderChat);
376+
await member.RevokeRoleAsync(insiderChatRole);
377+
378+
await e.Interaction.EditFollowupMessageAsync(e.Message.Id, new DiscordWebhookBuilder().WithContent($"{cfgjson.Emoji.Success} You have been removed from the {insiderChatRole.Mention} role!"));
379+
}
203380
else
204381
{
205382
await e.Interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Unknown interaction. I don't know what you are asking me for.").AsEphemeral(true));

0 commit comments

Comments
 (0)