Files
keiko/src/events/guildMemberUpdate.ts
T
hikari 453ebd0f15
Node.js CI / CI (push) Successful in 28s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m12s
feat: sanction DM links and per-event colour coding (#13)
## Summary

- Adds resource links (appeal form, sanction logs, contact page, community invite) to all sanction DMs, separated from the sanction text by a Components v2 separator
- Adds a unique accent colour for every mod log and activity log event type, giving each action a distinct visual identity at a glance

## Changes

- `src/utils/components.ts` — Added `sanctionDmMessage` helper with two-section container (sanction text + links); added full `Colours` palette covering all sanction and activity event types; added `ColourKey` export
- `src/commands/{ban,kick,mute,softban,warn}.ts` — Updated DMs to use `sanctionDmMessage` with the appropriate colour
- `src/modules/logModAction.ts` / `logActivity.ts` — Thread `colour` parameter through to message builders
- All event and command files updated with their respective colours

 This PR was created with help from Hikari~ 🌸

Reviewed-on: #13
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-31 17:33:35 -07:00

122 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
/* eslint-disable unicorn/no-keyword-prefix -- old/new prefixes are the established Discord.js event parameter names */
/* eslint-disable max-lines-per-function -- Event handler checks multiple independent change types */
/* eslint-disable complexity -- Event handler branches cover distinct member update scenarios */
/* eslint-disable max-statements -- Event handler has many intermediate variables across change types */
import { logActivity } from "../modules/logActivity.js";
import { logger } from "../utils/logger.js";
import type { GuildMember, PartialGuildMember } from "discord.js";
/**
* Logs member update events (roles, nickname, server avatar) to the activity
* log channel. Skips bots and partial old-member states where comparison is
* unreliable.
* @param oldMember - The guild member before the update.
* @param newMember - The guild member after the update.
* @returns A promise that resolves when all applicable changes have been logged.
*/
export const onGuildMemberUpdate = async(
oldMember: GuildMember | PartialGuildMember,
newMember: GuildMember,
): Promise<void> => {
if (newMember.user.bot) {
return;
}
if (oldMember.partial) {
return;
}
const { username, id: userId } = newMember.user;
try {
if (oldMember.nickname !== newMember.nickname) {
const before = oldMember.nickname ?? "*(none)*";
const after = newMember.nickname ?? "*(none)*";
await logActivity({
client: newMember.client,
colour: "nicknameChange",
emoji: "📝",
fields: [
`**User**: ${username} (\`${userId}\`)`,
`**Before**: ${before}`,
`**After**: ${after}`,
].join("\n"),
title: "Nickname Changed",
});
}
const addedRoles = newMember.roles.cache.filter(
(role) => {
return !oldMember.roles.cache.has(role.id);
},
);
if (addedRoles.size > 0) {
const roleList = addedRoles.map((role) => {
return `<@&${role.id}>`;
}).join(", ");
await logActivity({
client: newMember.client,
colour: "rolesAdded",
emoji: "",
fields: [
`**User**: ${username} (\`${userId}\`)`,
`**Roles Added**: ${roleList}`,
].join("\n"),
title: "Roles Added",
});
}
const removedRoles = oldMember.roles.cache.filter(
(role) => {
return !newMember.roles.cache.has(role.id);
},
);
if (removedRoles.size > 0) {
const roleList = removedRoles.map((role) => {
return `<@&${role.id}>`;
}).join(", ");
await logActivity({
client: newMember.client,
colour: "rolesRemoved",
emoji: "",
fields: [
`**User**: ${username} (\`${userId}\`)`,
`**Roles Removed**: ${roleList}`,
].join("\n"),
title: "Roles Removed",
});
}
if (oldMember.avatar !== newMember.avatar) {
await logActivity({
client: newMember.client,
colour: "serverAvatar",
emoji: "🖼️",
fields: [
`**User**: ${username} (\`${userId}\`)`,
`**New Server Avatar**: ${newMember.displayAvatarURL()}`,
].join("\n"),
title: "Server Avatar Changed",
});
}
} catch (error) {
await logger.error(
"Failed to log member update",
error instanceof Error
? error
: new Error(String(error)),
);
}
};