From a629d43d7eb317a5c521ec566597ca8c0327a934 Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Fri, 14 Jun 2024 22:26:42 -0400 Subject: [PATCH 1/3] Add the ability to pause & unpause DMs --- .../SecurityActionInteractions.cs | 94 +++++++++++++++ Commands/SecurityActions.cs | 111 ++++++++++++++++++ GlobalUsings.cs | 1 + Helpers/SecurityActionHelpers.cs | 25 ++++ 4 files changed, 231 insertions(+) create mode 100644 Commands/InteractionCommands/SecurityActionInteractions.cs create mode 100644 Commands/SecurityActions.cs create mode 100644 Helpers/SecurityActionHelpers.cs diff --git a/Commands/InteractionCommands/SecurityActionInteractions.cs b/Commands/InteractionCommands/SecurityActionInteractions.cs new file mode 100644 index 00000000..e8b9e537 --- /dev/null +++ b/Commands/InteractionCommands/SecurityActionInteractions.cs @@ -0,0 +1,94 @@ +namespace Cliptok.Commands.InteractionCommands +{ + public class SecurityActionInteractions : ApplicationCommandModule + { + [SlashCommand("pausedms", "Temporarily pause DMs between server members.", defaultPermission: false)] + [HomeServer, SlashRequireHomeserverPerm(ServerPermLevel.Moderator), SlashCommandPermissions(Permissions.ModerateMembers)] + public async Task SlashPauseDMs(InteractionContext ctx, [Option("time", "The amount of time to pause DMs for. Cannot be greater than 24 hours.")] string time) + { + // need to make our own api calls because D#+ can't do this natively? + + // parse time from message + DateTime t = HumanDateParser.HumanDateParser.Parse(time); + if (t <= DateTime.Now) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Time can't be in the past!"); + return; + } + if (t > DateTime.Now.AddHours(24)) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Time can't be greater than 24 hours!"); + return; + } + var dmsDisabledUntil = t.ToUniversalTime().ToString("o"); + + // get current security actions to avoid unintentionally resetting invites_disabled_until + var currentActions = await SecurityActionHelpers.GetCurrentSecurityActions(ctx.Guild.Id); + JToken invitesDisabledUntil; + if (currentActions is null || !currentActions.HasValues) + invitesDisabledUntil = null; + else + invitesDisabledUntil = currentActions["invites_disabled_until"]; + + // create json body + var newSecurityActions = JsonConvert.SerializeObject(new + { + invites_disabled_until = invitesDisabledUntil, + dms_disabled_until = dmsDisabledUntil, + }); + + // set actions + var setActionsResponse = await SecurityActionHelpers.SetCurrentSecurityActions(ctx.Guild.Id, newSecurityActions); + + // respond + if (setActionsResponse.IsSuccessStatusCode) + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully paused DMs for **{TimeHelpers.TimeToPrettyFormat(t.Subtract(ctx.Interaction.CreationTimestamp.DateTime.ToLocalTime()), false)}**!"); + else + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } + + [SlashCommand("unpausedms", "Unpause DMs between server members.", defaultPermission: false)] + [HomeServer, SlashRequireHomeserverPerm(ServerPermLevel.Moderator), SlashCommandPermissions(Permissions.ModerateMembers)] + public async Task SlashUnpauseDMs(InteractionContext ctx) + { + // need to make our own api calls because D#+ can't do this natively? + + // get current security actions to avoid unintentionally resetting invites_disabled_until + var currentActions = await SecurityActionHelpers.GetCurrentSecurityActions(ctx.Guild.Id); + JToken dmsDisabledUntil, invitesDisabledUntil; + if (currentActions is null || !currentActions.HasValues) + { + dmsDisabledUntil = null; + invitesDisabledUntil = null; + } + else + { + dmsDisabledUntil = currentActions["dms_disabled_until"]; + invitesDisabledUntil = currentActions["invites_disabled_until"]; + } + + // if dms are already unpaused, return error + if (dmsDisabledUntil is null) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} DMs are already unpaused!"); + return; + } + + // create json body + var newSecurityActions = JsonConvert.SerializeObject(new + { + invites_disabled_until = invitesDisabledUntil, + dms_disabled_until = (object)null, + }); + + // set actions + var setActionsResponse = await SecurityActionHelpers.SetCurrentSecurityActions(ctx.Guild.Id, newSecurityActions); + + // respond + if (setActionsResponse.IsSuccessStatusCode) + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully unpaused DMs!"); + else + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } + } +} \ No newline at end of file diff --git a/Commands/SecurityActions.cs b/Commands/SecurityActions.cs new file mode 100644 index 00000000..181809d8 --- /dev/null +++ b/Commands/SecurityActions.cs @@ -0,0 +1,111 @@ +namespace Cliptok.Commands +{ + public class SecurityActions : BaseCommandModule + { + [Command("pausedms")] + [Description("Temporarily pause DMs between server members.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + public async Task PauseDMs(CommandContext ctx, [Description("The amount of time to pause DMs for."), RemainingText] string time) + { + if (string.IsNullOrWhiteSpace(time)) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} You must provide an amount of time to pause DMs for!"); + return; + } + + // need to make our own api calls because D#+ can't do this natively? + + // parse time from message + DateTime t; + try + { + t = HumanDateParser.HumanDateParser.Parse(time); + } + catch (HumanDateParser.ParseException) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} I didn't understand that time! Please try again."); + return; + } + if (t <= DateTime.Now) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Time can't be in the past!"); + return; + } + if (t > DateTime.Now.AddHours(24)) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Time can't be greater than 24 hours!"); + return; + } + var dmsDisabledUntil = t.ToUniversalTime().ToString("o"); + + // get current security actions to avoid unintentionally resetting invites_disabled_until + var currentActions = await SecurityActionHelpers.GetCurrentSecurityActions(ctx.Guild.Id); + JToken invitesDisabledUntil; + if (currentActions is null || !currentActions.HasValues) + invitesDisabledUntil = null; + else + invitesDisabledUntil = currentActions["invites_disabled_until"]; + + // create json body + var newSecurityActions = JsonConvert.SerializeObject(new + { + invites_disabled_until = invitesDisabledUntil, + dms_disabled_until = dmsDisabledUntil, + }); + + // set actions + var setActionsResponse = await SecurityActionHelpers.SetCurrentSecurityActions(ctx.Guild.Id, newSecurityActions); + + // respond + if (setActionsResponse.IsSuccessStatusCode) + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully paused DMs for **{TimeHelpers.TimeToPrettyFormat(t.Subtract(ctx.Message.Timestamp.DateTime), false)}**!"); + else + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } + + [Command("unpausedms")] + [Description("Unpause DMs between server members.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + public async Task UnpauseDMs(CommandContext ctx) + { + // need to make our own api calls because D#+ can't do this natively? + + // get current security actions to avoid unintentionally resetting invites_disabled_until + var currentActions = await SecurityActionHelpers.GetCurrentSecurityActions(ctx.Guild.Id); + JToken dmsDisabledUntil, invitesDisabledUntil; + if (currentActions is null || !currentActions.HasValues) + { + dmsDisabledUntil = null; + invitesDisabledUntil = null; + } + else + { + dmsDisabledUntil = currentActions["dms_disabled_until"]; + invitesDisabledUntil = currentActions["invites_disabled_until"]; + } + + // if dms are already unpaused, return error + if (dmsDisabledUntil is null) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} DMs are already unpaused!"); + return; + } + + // create json body + var newSecurityActions = JsonConvert.SerializeObject(new + { + invites_disabled_until = invitesDisabledUntil, + dms_disabled_until = (object)null, + }); + + // set actions + var setActionsResponse = await SecurityActionHelpers.SetCurrentSecurityActions(ctx.Guild.Id, newSecurityActions); + + // respond + if (setActionsResponse.IsSuccessStatusCode) + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully unpaused DMs!"); + else + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } + } +} \ No newline at end of file diff --git a/GlobalUsings.cs b/GlobalUsings.cs index 6d870565..d8a9ff6c 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -10,6 +10,7 @@ global using DSharpPlus.SlashCommands; global using Microsoft.Extensions.Logging; global using Newtonsoft.Json; +global using Newtonsoft.Json.Linq; global using Serilog; global using Serilog.Sinks.SystemConsole.Themes; global using StackExchange.Redis; diff --git a/Helpers/SecurityActionHelpers.cs b/Helpers/SecurityActionHelpers.cs new file mode 100644 index 00000000..9137283e --- /dev/null +++ b/Helpers/SecurityActionHelpers.cs @@ -0,0 +1,25 @@ +namespace Cliptok.Helpers +{ + public class SecurityActionHelpers + { + public static async Task GetCurrentSecurityActions(ulong guildId) + { + using HttpRequestMessage getActionsRequest = new(HttpMethod.Get, $"https://discord.com/api/v9/guilds/{guildId}"); + getActionsRequest.Headers.Authorization = new AuthenticationHeaderValue("Bot", Environment.GetEnvironmentVariable("CLIPTOK_TOKEN") ?? Program.cfgjson.Core.Token); + + var getActionsResponse = await Program.httpClient.SendAsync(getActionsRequest); + return ((JObject)JsonConvert.DeserializeObject(await getActionsResponse.Content.ReadAsStringAsync()))["incidents_data"]; + } + + public static async Task SetCurrentSecurityActions(ulong guildId, string newSecurityActions) + { + // create & send request + + using HttpRequestMessage setActionsRequest = new(HttpMethod.Put, $"https://discord.com/api/v9/guilds/{guildId}/incident-actions"); + setActionsRequest.Headers.Authorization = new AuthenticationHeaderValue("Bot", Environment.GetEnvironmentVariable("CLIPTOK_TOKEN") ?? Program.cfgjson.Core.Token); + + setActionsRequest.Content = new StringContent(newSecurityActions, Encoding.UTF8, "application/json"); + return await Program.httpClient.SendAsync(setActionsRequest); + } + } +} \ No newline at end of file From 9d87c94faf100a1626131560c5b981866210546f Mon Sep 17 00:00:00 2001 From: Milkshake Date: Wed, 19 Jun 2024 11:19:28 -0400 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Erisa A --- .../SecurityActionInteractions.cs | 14 +++++++++----- Commands/SecurityActions.cs | 8 ++++++-- Helpers/SecurityActionHelpers.cs | 4 ++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Commands/InteractionCommands/SecurityActionInteractions.cs b/Commands/InteractionCommands/SecurityActionInteractions.cs index e8b9e537..d9c110d7 100644 --- a/Commands/InteractionCommands/SecurityActionInteractions.cs +++ b/Commands/InteractionCommands/SecurityActionInteractions.cs @@ -42,10 +42,12 @@ public async Task SlashPauseDMs(InteractionContext ctx, [Option("time", "The amo // respond if (setActionsResponse.IsSuccessStatusCode) - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully paused DMs for **{TimeHelpers.TimeToPrettyFormat(t.Subtract(ctx.Interaction.CreationTimestamp.DateTime.ToLocalTime()), false)}**!"); + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully paused DMs until !"); else - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); - } + { + ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } [SlashCommand("unpausedms", "Unpause DMs between server members.", defaultPermission: false)] [HomeServer, SlashRequireHomeserverPerm(ServerPermLevel.Moderator), SlashCommandPermissions(Permissions.ModerateMembers)] @@ -88,7 +90,9 @@ public async Task SlashUnpauseDMs(InteractionContext ctx) if (setActionsResponse.IsSuccessStatusCode) await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully unpaused DMs!"); else - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); - } + { + ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); + } } } \ No newline at end of file diff --git a/Commands/SecurityActions.cs b/Commands/SecurityActions.cs index 181809d8..ed554072 100644 --- a/Commands/SecurityActions.cs +++ b/Commands/SecurityActions.cs @@ -60,8 +60,10 @@ public async Task PauseDMs(CommandContext ctx, [Description("The amount of time if (setActionsResponse.IsSuccessStatusCode) await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully paused DMs for **{TimeHelpers.TimeToPrettyFormat(t.Subtract(ctx.Message.Timestamp.DateTime), false)}**!"); else + { + ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); - } + } [Command("unpausedms")] [Description("Unpause DMs between server members.")] @@ -105,7 +107,9 @@ public async Task UnpauseDMs(CommandContext ctx) if (setActionsResponse.IsSuccessStatusCode) await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully unpaused DMs!"); else + { + ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); - } + } } } \ No newline at end of file diff --git a/Helpers/SecurityActionHelpers.cs b/Helpers/SecurityActionHelpers.cs index 9137283e..7734e379 100644 --- a/Helpers/SecurityActionHelpers.cs +++ b/Helpers/SecurityActionHelpers.cs @@ -4,7 +4,7 @@ public class SecurityActionHelpers { public static async Task GetCurrentSecurityActions(ulong guildId) { - using HttpRequestMessage getActionsRequest = new(HttpMethod.Get, $"https://discord.com/api/v9/guilds/{guildId}"); + using HttpRequestMessage getActionsRequest = new(HttpMethod.Get, $"https://discord.com/api/v{Program.discord.GatewayVersion}/guilds/{guildId}"); getActionsRequest.Headers.Authorization = new AuthenticationHeaderValue("Bot", Environment.GetEnvironmentVariable("CLIPTOK_TOKEN") ?? Program.cfgjson.Core.Token); var getActionsResponse = await Program.httpClient.SendAsync(getActionsRequest); @@ -15,7 +15,7 @@ public static async Task SetCurrentSecurityActions(ulong gu { // create & send request - using HttpRequestMessage setActionsRequest = new(HttpMethod.Put, $"https://discord.com/api/v9/guilds/{guildId}/incident-actions"); + using HttpRequestMessage setActionsRequest = new(HttpMethod.Put, $"https://discord.com/api/v{Program.discord.GatewayVersion}/guilds/{guildId}/incident-actions"); setActionsRequest.Headers.Authorization = new AuthenticationHeaderValue("Bot", Environment.GetEnvironmentVariable("CLIPTOK_TOKEN") ?? Program.cfgjson.Core.Token); setActionsRequest.Content = new StringContent(newSecurityActions, Encoding.UTF8, "application/json"); From 1287212a11fbbaa7e90aca34d521495b2fa990d9 Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Wed, 19 Jun 2024 11:25:55 -0400 Subject: [PATCH 3/3] Fix broken syntax --- Commands/InteractionCommands/SecurityActionInteractions.cs | 2 ++ Commands/SecurityActions.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Commands/InteractionCommands/SecurityActionInteractions.cs b/Commands/InteractionCommands/SecurityActionInteractions.cs index d9c110d7..e9d60605 100644 --- a/Commands/InteractionCommands/SecurityActionInteractions.cs +++ b/Commands/InteractionCommands/SecurityActionInteractions.cs @@ -48,6 +48,7 @@ public async Task SlashPauseDMs(InteractionContext ctx, [Option("time", "The amo ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); } + } [SlashCommand("unpausedms", "Unpause DMs between server members.", defaultPermission: false)] [HomeServer, SlashRequireHomeserverPerm(ServerPermLevel.Moderator), SlashCommandPermissions(Permissions.ModerateMembers)] @@ -94,5 +95,6 @@ public async Task SlashUnpauseDMs(InteractionContext ctx) ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); } + } } } \ No newline at end of file diff --git a/Commands/SecurityActions.cs b/Commands/SecurityActions.cs index ed554072..98cdf9ef 100644 --- a/Commands/SecurityActions.cs +++ b/Commands/SecurityActions.cs @@ -64,6 +64,7 @@ public async Task PauseDMs(CommandContext ctx, [Description("The amount of time ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to pause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); } + } [Command("unpausedms")] [Description("Unpause DMs between server members.")] @@ -111,5 +112,6 @@ public async Task UnpauseDMs(CommandContext ctx) ctx.Client.Logger.LogError("Failed to set Security Actions.\nPayload: {payload}\nResponse: {statuscode} {body}", newSecurityActions.ToString(), (int)setActionsResponse.StatusCode, await setActionsResponse.Content.ReadAsStringAsync()); await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Something went wrong and I wasn't able to unpause DMs! Discord returned status code `{setActionsResponse.StatusCode}`."); } + } } } \ No newline at end of file