Skip to content

Commit

Permalink
Merge branch 'main' into erisa/dsharpplus-5.0.0-nightly-02295
Browse files Browse the repository at this point in the history
  • Loading branch information
Erisa committed Jun 24, 2024
2 parents f7d2996 + 2f852a5 commit 1366645
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 68 deletions.
100 changes: 100 additions & 0 deletions Commands/InteractionCommands/SecurityActionInteractions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 until <t:{TimeHelpers.ToUnixTimestamp(t)}>!");
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}`.");
}
}

[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
{
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}`.");
}
}
}
}
18 changes: 15 additions & 3 deletions Commands/InteractionCommands/WarningInteractions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,12 @@ public async Task WarndetailsSlashCommand(InteractionContext ctx,
[Option("public", "Whether to show the output publicly.")] bool publicWarnings = false
)
{
long warnId = default;
if (warning.Contains(' '))
{
warning = warning.Split(' ')[0];
}

long warnId;
try
{
warnId = Convert.ToInt64(warning);
Expand All @@ -197,8 +201,12 @@ public async Task DelwarnSlashCommand(InteractionContext ctx,
[Option("public", "Whether to show the output publicly. Default: false")] bool showPublic = false
)
{
long warnId = default;
if (warningId.Contains(' '))
{
warningId = warningId.Split(' ')[0];
}

long warnId;
try
{
warnId = Convert.ToInt64(warningId);
Expand Down Expand Up @@ -246,8 +254,12 @@ public async Task EditWarnSlashCommand(InteractionContext ctx,
[Option("new_reason", "The new reason for the warning")] string reason,
[Option("public", "Whether to show the output publicly. Default: false")] bool showPublic = false)
{
long warnId = default;
if (warning.Contains(' '))
{
warning = warning.Split(' ')[0];
}

long warnId;
try
{
warnId = Convert.ToInt64(warning);
Expand Down
117 changes: 117 additions & 0 deletions Commands/SecurityActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
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
{
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.")]
[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
{
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}`.");
}
}
}
}
19 changes: 13 additions & 6 deletions Commands/TechSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ public async Task AskCmd(CommandContext ctx, [Description("Optional, a user to p
{
await ctx.Message.DeleteAsync();
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
.WithTitle("**__Need Help Or Have a Problem?__**")
.WithDescription(
$"You're probably looking for <#{Program.cfgjson.TechSupportChannel}> or <#{Program.cfgjson.SupportForumId}>!\n\n" +
$"Once there, please be sure to provide **plenty of details**, ping the <@&{Program.cfgjson.CommunityTechSupportRoleID}> role, and *be patient!*\n\n" +
$"Look under the `🔧 Support` category for the appropriate channel for your issue. See <#413274922413195275> for more info."
)
.WithColor(13920845);
if (ctx.Channel.Id == Program.cfgjson.TechSupportChannel || ctx.Channel.ParentId == Program.cfgjson.SupportForumId)
{
embed.Title = "**__Need help?__**";
embed.Description = $"You are in the right place! Please state your question with *plenty of detail* and mention the <@&{Program.cfgjson.CommunityTechSupportRoleID}> role and someone may be able to help you.\n\n" +
$"Details includes error codes and other specific information.";
}
else
{
embed.Title = "**__Need Help Or Have a Problem?__**";
embed.Description = $"You're probably looking for <#{Program.cfgjson.TechSupportChannel}> or <#{Program.cfgjson.SupportForumId}>!\n\n" +
$"Once there, please be sure to provide **plenty of details**, ping the <@&{Program.cfgjson.CommunityTechSupportRoleID}> role, and *be patient!*\n\n" +
$"Look under the `🔧 Support` category for the appropriate channel for your issue. See <#413274922413195275> for more info.";
}

if (user != default)
{
Expand Down
16 changes: 5 additions & 11 deletions Events/ChannelEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,13 @@ public static async Task ChannelUpdated(DiscordClient _, ChannelUpdatedEventArgs
// Delay to allow leave to complete first
await Task.Delay(500);

// Try to fetch member. If it fails, they are not in the guild.
try
{
await e.Guild.GetMemberAsync((ulong)userOverwrites.Name);
}
catch
{
// Failed to fetch user. They probably left or were otherwise removed from the server.
// Preserve overrides.
return;
}
// Try to fetch member. If it fails, they are not in the guild. If this is a voice channel, remove the override.
// (if they are not in the guild & this is not a voice channel, skip; otherwise, code below handles removal)
if (!e.Guild.Members.ContainsKey((ulong)userOverwrites.Name) && e.ChannelAfter.Type != ChannelType.Voice)
continue;

// User could be fetched, so they are in the server and their override was removed. Remove from db.
// (or user could not be fetched & this is a voice channel; remove)

var overrides = await Program.db.HashGetAsync("overrides", userOverwrites.Name);
var dict = JsonConvert.DeserializeObject<Dictionary<ulong, DiscordOverwrite>>(overrides);
Expand Down
2 changes: 1 addition & 1 deletion Events/MemberEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static async Task GuildMemberAdded(DiscordClient client, GuildMemberAdded
{
if (db.HashExists(IdAutoBanSet.Name, e.Member.Id))
{
return;
continue;
}

if (e.Member.Id > IdAutoBanSet.LowerBound && e.Member.Id < IdAutoBanSet.UpperBound)
Expand Down
1 change: 1 addition & 0 deletions GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
25 changes: 25 additions & 0 deletions Helpers/SecurityActionHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Cliptok.Helpers
{
public class SecurityActionHelpers
{
public static async Task<JToken> GetCurrentSecurityActions(ulong 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);
return ((JObject)JsonConvert.DeserializeObject(await getActionsResponse.Content.ReadAsStringAsync()))["incidents_data"];
}

public static async Task<HttpResponseMessage> SetCurrentSecurityActions(ulong guildId, string newSecurityActions)
{
// create & send request

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");
return await Program.httpClient.SendAsync(setActionsRequest);
}
}
}
Loading

0 comments on commit 1366645

Please sign in to comment.