Skip to content

Commit 769db87

Browse files
committed
Add collision detection to mute and ban
Fixes #268
1 parent 0b1a03a commit 769db87

File tree

6 files changed

+163
-0
lines changed

6 files changed

+163
-0
lines changed

Commands/BanCmds.cs

+75
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,33 @@ public async Task BanSlashCommand(SlashCommandContext ctx,
1717
[Parameter("compromised_account"), Description("Whether to include special instructions for compromised accounts")] bool compromisedAccount = false
1818
)
1919
{
20+
// collision detection
21+
if (MostRecentBan is not null && MostRecentBan.MemberId == user.Id)
22+
{
23+
var timeSinceLastBan = DateTime.UtcNow.Subtract((DateTime)MostRecentBan.ActionTime);
24+
if (timeSinceLastBan <= TimeSpan.FromSeconds(5))
25+
{
26+
var response = new DiscordInteractionResponseBuilder()
27+
.WithContent($"{Program.cfgjson.Emoji.Error} {user.Mention} was already banned a few seconds ago, refusing yours to prevent collisions. If you meant to ban them again, try again in a few seconds.")
28+
.AsEphemeral(true);
29+
if (!MostRecentBan.Stub)
30+
response.AddEmbed(await BanStatusEmbed(user, ctx.Guild));
31+
32+
await ctx.RespondAsync(response);
33+
return;
34+
}
35+
}
36+
37+
MostRecentBan = new()
38+
{
39+
MemberId = user.Id,
40+
ActionTime = ctx.Interaction.CreationTimestamp.DateTime,
41+
ModId = ctx.User.Id,
42+
ServerId = ctx.Guild.Id,
43+
Reason = reason,
44+
Stub = true
45+
};
46+
2047
// Initial response to avoid the 3 second timeout, will edit later.
2148
var eout = new DiscordInteractionResponseBuilder().AsEphemeral(true);
2249
await ctx.DeferResponseAsync(true);
@@ -205,6 +232,29 @@ public async Task BanCmd(TextCommandContext ctx,
205232
[Description("The user you wish to ban. Should be a mention or ID.")] DiscordUser targetMember,
206233
[RemainingText, Description("The time and reason for the ban. e.g. '14d trolling' NOTE: Add 'appeal' to the start of the reason to include an appeal link")] string timeAndReason = "No reason specified.")
207234
{
235+
// collision detection
236+
if (MostRecentBan is not null && targetMember.Id == MostRecentBan.MemberId)
237+
{
238+
var timeSinceLastWarning = DateTime.UtcNow.Subtract((DateTime)MostRecentBan.ActionTime);
239+
if (timeSinceLastWarning <= TimeSpan.FromSeconds(5))
240+
{
241+
await ctx.Message.DeleteAsync();
242+
var resp = await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.BSOD} I was asked to ban someone twice within a few seconds, but I'm not going to. If I'm wrong, try again in a few seconds.");
243+
await Task.Delay(5000);
244+
await resp.DeleteAsync();
245+
return;
246+
}
247+
}
248+
249+
MostRecentBan = new()
250+
{
251+
MemberId = targetMember.Id,
252+
ActionTime = DateTime.UtcNow,
253+
ModId = ctx.User.Id,
254+
ServerId = ctx.Guild.Id,
255+
Reason = timeAndReason,
256+
Stub = true
257+
};
208258

209259
if (targetMember.IsBot)
210260
{
@@ -292,6 +342,31 @@ public async Task BankeepCmd(TextCommandContext ctx,
292342
[Description("The user you wish to ban. Should be a mention or ID.")] DiscordUser targetMember,
293343
[RemainingText, Description("The time and reason for the ban. e.g. '14d trolling' NOTE: Add 'appeal' to the start of the reason to include an appeal link")] string timeAndReason = "No reason specified.")
294344
{
345+
// collision detection
346+
if (MostRecentBan is not null && targetMember.Id == MostRecentBan.MemberId)
347+
{
348+
var timeSinceLastWarning = DateTime.UtcNow.Subtract((DateTime)MostRecentBan.ActionTime);
349+
if (timeSinceLastWarning <= TimeSpan.FromSeconds(5))
350+
{
351+
await ctx.Message.DeleteAsync();
352+
var resp = await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.BSOD} I was asked to ban someone twice within a few seconds, but I'm not going to. If I'm wrong, try again in a few seconds.");
353+
await Task.Delay(5000);
354+
await resp.DeleteAsync();
355+
return;
356+
}
357+
}
358+
359+
360+
MostRecentBan = new()
361+
{
362+
MemberId = targetMember.Id,
363+
ActionTime = DateTime.UtcNow,
364+
ModId = ctx.User.Id,
365+
ServerId = ctx.Guild.Id,
366+
Reason = timeAndReason,
367+
Stub = true
368+
};
369+
295370
bool appealable = false;
296371
bool timeParsed = false;
297372

Commands/DebugCmds.cs

+26
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,32 @@ public async Task WarningCacheCmd(TextCommandContext ctx)
322322
}
323323
await ctx.RespondAsync(await StringHelpers.CodeOrHasteBinAsync(JsonConvert.SerializeObject(WarningHelpers.mostRecentWarning, Formatting.Indented), "json"));
324324
}
325+
326+
[Command("bancache")]
327+
[Description("Dump the most recent manual warning")]
328+
public async Task BanCacheCmd(TextCommandContext ctx)
329+
{
330+
if (BanHelpers.MostRecentBan is null)
331+
{
332+
await ctx.RespondAsync("No cached ban found.");
333+
return;
334+
}
335+
await ctx.RespondAsync(await StringHelpers.CodeOrHasteBinAsync(JsonConvert.SerializeObject(BanHelpers.MostRecentBan, Formatting.Indented), "json"));
336+
}
337+
338+
339+
[Command("mutecache")]
340+
[Description("Dump the most recent manual warning")]
341+
public async Task MuteCacheCmd(TextCommandContext ctx)
342+
{
343+
if (MuteHelpers.MostRecentMute is null)
344+
{
345+
await ctx.RespondAsync("No cached mute found.");
346+
return;
347+
}
348+
await ctx.RespondAsync(await StringHelpers.CodeOrHasteBinAsync(JsonConvert.SerializeObject(MuteHelpers.MostRecentMute, Formatting.Indented), "json"));
349+
}
350+
325351
}
326352

327353
class OverridesCmd

Commands/MuteCmds.cs

+51
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,33 @@ public async Task MuteSlashCommand(
1414
[Parameter("time"), Description("The length of time to mute for.")] string time = ""
1515
)
1616
{
17+
// collision detection
18+
if (MuteHelpers.MostRecentMute is not null && MuteHelpers.MostRecentMute.MemberId == targetUser.Id)
19+
{
20+
var timeSinceLastBan = DateTime.UtcNow.Subtract((DateTime)MuteHelpers.MostRecentMute.ActionTime);
21+
if (timeSinceLastBan <= TimeSpan.FromSeconds(5))
22+
{
23+
var response = new DiscordInteractionResponseBuilder()
24+
.WithContent($"{Program.cfgjson.Emoji.Error} {targetUser.Mention} was already muted a few seconds ago, refusing yours to prevent collisions. If you meant to mute them again, try again in a few seconds.")
25+
.AsEphemeral(true);
26+
if (!MuteHelpers.MostRecentMute.Stub)
27+
response.AddEmbed(await MuteHelpers.MuteStatusEmbed(targetUser, ctx.Guild));
28+
29+
await ctx.RespondAsync(response);
30+
return;
31+
}
32+
}
33+
34+
MuteHelpers.MostRecentMute = new()
35+
{
36+
MemberId = targetUser.Id,
37+
ActionTime = ctx.Interaction.CreationTimestamp.DateTime,
38+
ModId = ctx.User.Id,
39+
ServerId = ctx.Guild.Id,
40+
Reason = reason,
41+
Stub = true
42+
};
43+
1744
await ctx.DeferResponseAsync(ephemeral: true);
1845
DiscordMember targetMember = default;
1946
try
@@ -242,6 +269,30 @@ public async Task MuteCmd(
242269
[RemainingText, Description("Combined argument for the time and reason for the mute. For example '1h rule 7' or 'rule 10'")] string timeAndReason = "No reason specified."
243270
)
244271
{
272+
// collision detection
273+
if (MuteHelpers.MostRecentMute is not null && targetUser.Id == MuteHelpers.MostRecentMute.MemberId)
274+
{
275+
var timeSinceLastWarning = DateTime.UtcNow.Subtract((DateTime)MuteHelpers.MostRecentMute.ActionTime);
276+
if (timeSinceLastWarning <= TimeSpan.FromSeconds(5))
277+
{
278+
await ctx.Message.DeleteAsync();
279+
var resp = await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.BSOD} I was asked to mute someone twice within a few seconds, but I'm not going to. If I'm wrong, try again in a few seconds.");
280+
await Task.Delay(5000);
281+
await resp.DeleteAsync();
282+
return;
283+
}
284+
}
285+
286+
MuteHelpers.MostRecentMute = new()
287+
{
288+
MemberId = targetUser.Id,
289+
ActionTime = DateTime.UtcNow,
290+
ModId = ctx.User.Id,
291+
ServerId = ctx.Guild.Id,
292+
Reason = timeAndReason,
293+
Stub = true
294+
};
295+
245296
DiscordMember targetMember = default;
246297
try
247298
{

Helpers/BanHelpers.cs

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
{
33
public class BanHelpers
44
{
5+
public static MemberPunishment MostRecentBan;
6+
57
public static async Task<bool> BanFromServerAsync(ulong targetUserId, string reason, ulong moderatorId, DiscordGuild guild, int deleteDays = 7, DiscordChannel channel = null, TimeSpan banDuration = default, bool appealable = false, bool compromisedAccount = false)
68
{
79
bool permaBan = false;
@@ -82,6 +84,9 @@ public static async Task<bool> BanFromServerAsync(ulong targetUserId, string rea
8284

8385
await Program.db.HashSetAsync("bans", targetUserId, JsonConvert.SerializeObject(newBan));
8486

87+
// used for collision detection
88+
MostRecentBan = newBan;
89+
8590
// If ban is for a compromised account, add to list so the context message can be more-easily deleted later
8691
if (compromisedAccount)
8792
Program.db.HashSet("compromisedAccountBans", targetUserId, JsonConvert.SerializeObject(newBan));

Helpers/MuteHelpers.cs

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
{
33
public class MuteHelpers
44
{
5+
public static MemberPunishment MostRecentMute;
6+
57
public static async Task<DiscordEmbed> MuteStatusEmbed(DiscordUser user, DiscordGuild guild)
68
{
79
DiscordMember member = default;
@@ -312,6 +314,7 @@ public static (int MuteHours, int WarnsSinceThreshold) GetHoursToMuteFor(Diction
312314
}
313315

314316
await Program.db.HashSetAsync("mutes", naughtyUser.Id, JsonConvert.SerializeObject(newMute));
317+
MostRecentMute = newMute;
315318

316319
return output;
317320
}

Structs.cs

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public class MemberPunishment
6767

6868
[JsonProperty("dmMessageReference")]
6969
public MessageReference DmMessageReference { get; set; }
70+
71+
[JsonProperty("stub")]
72+
public bool Stub { get; set; } = false;
7073
}
7174

7275
public class ConfigJson

0 commit comments

Comments
 (0)