Files
keiko/src/events/guildMemberRemove.ts
T
hikari 1c31a49bc4
Node.js CI / CI (push) Successful in 29s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 55s
feat: rewrite as moderation bot (#11)
## Summary

- Replaces the old AI companion bot with a full Discord moderation system
- Adds 8 slash commands: `warn`, `mute`, `unmute`, `kick`, `softban`, `ban`, `unban`, `prune`
- Adds logging for member join/leave, activity (messages, threads, voice), and moderation actions
- Audit log integration captures manual bans, kicks, timeouts, and unbans
- All applicable actions post sanctions to the Hikari sanction API
- All commands are ephemeral, use Components v2, and enforce permission + role hierarchy checks

## Test plan

- [ ] Run `pnpm register` to register all 8 commands to the guild
- [ ] Verify each command appears in Discord and is only visible to members with the appropriate permissions
- [ ] Test each command against a valid target and confirm mod log entry, DM notification, and sanction record
- [ ] Test each command against an invalid target (equal/higher role, self, bot) and confirm correct error response
- [ ] Perform a manual ban, kick, and timeout in the Discord UI and confirm audit log handler picks them up
- [ ] Perform a manual unban and confirm it logs correctly without creating a sanction
- [ ] Verify join/leave messages appear in the welcome log channel
- [ ] Verify message edits, deletes, thread events, and voice state changes appear in the activity log channel

 This issue was created with help from Hikari~ 🌸

Reviewed-on: #11
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-24 20:35:26 -07:00

56 lines
1.4 KiB
TypeScript

/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import {
ChannelType,
type GuildMember,
type PartialGuildMember,
} from "discord.js";
import { channelConfig } from "../config/channels.js";
import { memberMessage } from "../utils/components.js";
import { logger } from "../utils/logger.js";
/**
* Logs a member leave event to the welcome log channel.
* @param member - The guild member who left (may be partial if uncached).
* @returns A promise that resolves when the event has been logged.
*/
export const onGuildMemberRemove = async(
member: GuildMember | PartialGuildMember,
): Promise<void> => {
try {
const rawChannel = member.client.channels.cache.get(
channelConfig.welcomeLog,
);
if (rawChannel?.type !== ChannelType.GuildText) {
return;
}
const joinedAt = member.joinedAt === null
? "Unknown"
: member.joinedAt.toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
});
const fields = [
`**User**: ${member.user.username} (\`${member.id}\`)`,
`**Joined**: ${joinedAt}`,
].join("\n");
await rawChannel.send(memberMessage("leave", fields));
} catch (error) {
await logger.error(
"Failed to log member leave",
error instanceof Error
? error
: new Error(String(error)),
);
}
};