feat: include resource links in sanction DMs

Adds appeal form, sanction logs, contact page, and community rejoin
link to all sanction DM notifications, separated from the sanction
details using a Components v2 separator.
This commit is contained in:
2026-03-31 16:50:27 -07:00
committed by Naomi Carrigan
parent 01d6da8a33
commit 088f74e559
6 changed files with 41 additions and 10 deletions
+4 -2
View File
@@ -14,7 +14,7 @@ import {
} from "discord.js"; } from "discord.js";
import { logModerationAction } from "../modules/logModAction.js"; import { logModerationAction } from "../modules/logModAction.js";
import { sendSanction } from "../modules/sendSanction.js"; import { sendSanction } from "../modules/sendSanction.js";
import { errorReply, successReply } from "../utils/components.js"; import { errorReply, sanctionDmMessage, successReply } from "../utils/components.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js"; import type { Command } from "../interfaces/command.js";
@@ -103,7 +103,9 @@ const banCommand: Command = {
try { try {
await target.send( await target.send(
`You have been banned from **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`, sanctionDmMessage(
`You have been banned from **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`,
),
); );
} catch { } catch {
// DMs may be closed; continue without failing the command. // DMs may be closed; continue without failing the command.
+4 -2
View File
@@ -14,7 +14,7 @@ import {
} from "discord.js"; } from "discord.js";
import { logModerationAction } from "../modules/logModAction.js"; import { logModerationAction } from "../modules/logModAction.js";
import { sendSanction } from "../modules/sendSanction.js"; import { sendSanction } from "../modules/sendSanction.js";
import { errorReply, successReply } from "../utils/components.js"; import { errorReply, sanctionDmMessage, successReply } from "../utils/components.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js"; import type { Command } from "../interfaces/command.js";
@@ -98,7 +98,9 @@ const kickCommand: Command = {
try { try {
await target.send( await target.send(
`You have been kicked from **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`, sanctionDmMessage(
`You have been kicked from **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`,
),
); );
} catch { } catch {
// DMs may be closed; continue without failing the command. // DMs may be closed; continue without failing the command.
+4 -2
View File
@@ -14,7 +14,7 @@ import {
} from "discord.js"; } from "discord.js";
import { logModerationAction } from "../modules/logModAction.js"; import { logModerationAction } from "../modules/logModAction.js";
import { sendSanction } from "../modules/sendSanction.js"; import { sendSanction } from "../modules/sendSanction.js";
import { errorReply, successReply } from "../utils/components.js"; import { errorReply, sanctionDmMessage, successReply } from "../utils/components.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js"; import type { Command } from "../interfaces/command.js";
@@ -155,7 +155,9 @@ const muteCommand: Command = {
try { try {
await target.send( await target.send(
`You have been muted in **${interaction.guild?.name ?? "the server"}** for **${durationLabel}**.\n**Reason:** ${reason}`, sanctionDmMessage(
`You have been muted in **${interaction.guild?.name ?? "the server"}** for **${durationLabel}**.\n**Reason:** ${reason}`,
),
); );
} catch { } catch {
// DMs may be closed; continue without failing the command. // DMs may be closed; continue without failing the command.
+4 -2
View File
@@ -14,7 +14,7 @@ import {
} from "discord.js"; } from "discord.js";
import { logModerationAction } from "../modules/logModAction.js"; import { logModerationAction } from "../modules/logModAction.js";
import { sendSanction } from "../modules/sendSanction.js"; import { sendSanction } from "../modules/sendSanction.js";
import { errorReply, successReply } from "../utils/components.js"; import { errorReply, sanctionDmMessage, successReply } from "../utils/components.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js"; import type { Command } from "../interfaces/command.js";
@@ -94,7 +94,9 @@ const softbanCommand: Command = {
try { try {
await target.send( await target.send(
`You have been softbanned from **${interaction.guild?.name ?? "the server"}** (your recent messages have been removed).\n**Reason:** ${reason}`, sanctionDmMessage(
`You have been softbanned from **${interaction.guild?.name ?? "the server"}** (your recent messages have been removed).\n**Reason:** ${reason}`,
),
); );
} catch { } catch {
// DMs may be closed; continue without failing the command. // DMs may be closed; continue without failing the command.
+4 -2
View File
@@ -14,7 +14,7 @@ import {
} from "discord.js"; } from "discord.js";
import { logModerationAction } from "../modules/logModAction.js"; import { logModerationAction } from "../modules/logModAction.js";
import { sendSanction } from "../modules/sendSanction.js"; import { sendSanction } from "../modules/sendSanction.js";
import { errorReply, successReply } from "../utils/components.js"; import { errorReply, sanctionDmMessage, successReply } from "../utils/components.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js"; import type { Command } from "../interfaces/command.js";
@@ -99,7 +99,9 @@ const warnCommand: Command = {
try { try {
await target.send( await target.send(
`You have received a warning in **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`, sanctionDmMessage(
`You have received a warning in **${interaction.guild?.name ?? "the server"}**.\n**Reason:** ${reason}`,
),
); );
} catch { } catch {
// DMs may be closed; continue without failing the command. // DMs may be closed; continue without failing the command.
+21
View File
@@ -173,10 +173,31 @@ const memberMessage = (
}; };
}; };
const sanctionLinks = [
"**Appeal this sanction:** https://forms.nhcarrigan.com/o/docs/forms/4w5VHsYiEkiS2mewvtuJYL/4",
"**View sanction logs:** https://hikari.nhcarrigan.com/sanctions",
"**Contact us:** https://docs.nhcarrigan.com/about/contact/",
"**Rejoin our community:** https://chat.nhcarrigan.com",
].join("\n");
/**
* Builds a Components v2 DM payload for sanction notifications.
* Includes a separator between the sanction details and the resource links.
* @param sanctionText - The formatted sanction message (action + reason).
* @returns A Discord message payload object.
*/
const sanctionDmMessage = (sanctionText: string): Record<string, unknown> => {
return {
components: [ buildContainer("modAction", sanctionText, sanctionLinks) ],
flags: MessageFlags.IsComponentsV2,
};
};
export { export {
activityMessage, activityMessage,
errorReply, errorReply,
memberMessage, memberMessage,
modLogMessage, modLogMessage,
sanctionDmMessage,
successReply, successReply,
}; };