Skip to content

Commit d1e64e2

Browse files
committed
Add collision detection for simultaneous warning commands
1 parent 96ddf88 commit d1e64e2

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

Commands/DebugCmds.cs

+11
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,17 @@ public async Task ThrowNRE(TextCommandContext ctx, bool catchAsWarning = false)
310310
}
311311
}
312312

313+
[Command("warningcache")]
314+
[Description("Dump the most recent manual warning")]
315+
public async Task WarningCacheCmd(TextCommandContext ctx)
316+
{
317+
if (WarningHelpers.mostRecentWarning is null)
318+
{
319+
await ctx.RespondAsync("No cached warning found.");
320+
return;
321+
}
322+
await ctx.RespondAsync(await StringHelpers.CodeOrHasteBinAsync(JsonConvert.SerializeObject(WarningHelpers.mostRecentWarning, Formatting.Indented), "json"));
323+
}
313324
}
314325

315326
class OverridesCmd

Commands/WarningCmds.cs

+48
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,32 @@ public async Task WarnSlashCommand(SlashCommandContext ctx,
2424
[Parameter("channel"), Description("The channel to warn the user in, implied if not supplied.")] DiscordChannel channel = null
2525
)
2626
{
27+
// collision detection
28+
if (mostRecentWarning is not null && user.Id == mostRecentWarning.TargetUserId)
29+
{
30+
var timeSinceLastWarning = DateTime.UtcNow.Subtract(mostRecentWarning.WarnTimestamp);
31+
if (timeSinceLastWarning <= TimeSpan.FromSeconds(5))
32+
{
33+
var response = new DiscordInteractionResponseBuilder()
34+
.WithContent($"{Program.cfgjson.Emoji.Error} {user.Mention} was already warned a few seconds ago, refusing yours to prevent collisions. If your warning is unrelated, try again in a few seconds.")
35+
.AsEphemeral(true);
36+
if (!mostRecentWarning.Stub)
37+
response.AddEmbed(await FancyWarnEmbedAsync(mostRecentWarning, detailed: true));
38+
39+
await ctx.RespondAsync(response);
40+
return;
41+
}
42+
}
43+
44+
// this gets updated with a full warning object later, shove a stub in for now
45+
mostRecentWarning = new()
46+
{
47+
TargetUserId = user.Id,
48+
ModUserId = ctx.User.Id,
49+
WarnTimestamp = DateTime.Now,
50+
Stub = true // make it clear this isn't a real warning
51+
};
52+
2753
// Initial response to avoid the 3 second timeout, will edit later.
2854
await ctx.DeferResponseAsync(true);
2955

@@ -367,6 +393,28 @@ public async Task WarnCmd(
367393
[RemainingText, Description("The reason for giving this warning.")] string reason = null
368394
)
369395
{
396+
// collision detection
397+
if (mostRecentWarning is not null && targetUser.Id == mostRecentWarning.TargetUserId)
398+
{
399+
var timeSinceLastWarning = DateTime.UtcNow.Subtract(mostRecentWarning.WarnTimestamp);
400+
if (timeSinceLastWarning <= TimeSpan.FromSeconds(5))
401+
{
402+
await ctx.Message.DeleteAsync();
403+
var resp = await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.BSOD} I was asked to warn someone twice within a few seconds, but I'm not going to. If I'm wrong, try again in a few seconds.");
404+
await Task.Delay(5000);
405+
await resp.DeleteAsync();
406+
return;
407+
}
408+
}
409+
410+
// this gets updated with a full warning object later, shove a stub in for now
411+
mostRecentWarning = new(){
412+
TargetUserId = targetUser.Id,
413+
ModUserId = ctx.User.Id,
414+
WarnTimestamp = DateTime.Now,
415+
Stub = true // make it clear this isn't a real warning
416+
};
417+
370418
DiscordMember targetMember;
371419
try
372420
{

Helpers/WarningHelpers.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
{
33
public class WarningHelpers
44
{
5+
public static UserWarning mostRecentWarning;
6+
57
public static async Task<DiscordEmbed> GenerateWarningsEmbedAsync(DiscordUser targetUser)
68
{
79
var warningsOutput = (await Program.db.HashGetAllAsync(targetUser.Id.ToString()))
@@ -233,9 +235,16 @@ public static async Task<UserWarning> GiveWarningAsync(DiscordUser targetUser, D
233235

234236
Program.db.HashSet(targetUser.Id.ToString(), warning.WarningId, JsonConvert.SerializeObject(warning));
235237

238+
// Now that the warning is in DM, prevent future collisions by caching it.
239+
if (!modUser.IsBot)
240+
{
241+
mostRecentWarning = warning;
236242
// If warning is automatic (if responsible moderator is a bot), add to list so the context message can be more-easily deleted later
237-
if (modUser.IsBot)
243+
}
244+
else
245+
{
238246
Program.db.HashSet("automaticWarnings", warningId, JsonConvert.SerializeObject(warning));
247+
}
239248

240249
LogChannelHelper.LogMessageAsync("mod",
241250
new DiscordMessageBuilder()

Structs.cs

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public class UserWarning
2828

2929
[JsonProperty("type")]
3030
public WarningType Type { get; set; }
31+
32+
[JsonProperty("stub")]
33+
public bool Stub { get; set; } = false;
3134
}
3235

3336
public class MessageReference

0 commit comments

Comments
 (0)