242 lines
7.5 KiB
TypeScript
242 lines
7.5 KiB
TypeScript
import { EmbedBuilder, Colors, time, TimestampStyles } from "discord.js";
|
|
import type { ModerationCase } from "./moderation.types";
|
|
|
|
/**
|
|
* Get color based on case type
|
|
*/
|
|
function getCaseColor(type: string): number {
|
|
switch (type) {
|
|
case 'warn': return Colors.Yellow;
|
|
case 'timeout': return Colors.Orange;
|
|
case 'kick': return Colors.Red;
|
|
case 'ban': return Colors.DarkRed;
|
|
case 'note': return Colors.Blue;
|
|
case 'prune': return Colors.Grey;
|
|
default: return Colors.Grey;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get emoji based on case type
|
|
*/
|
|
function getCaseEmoji(type: string): string {
|
|
switch (type) {
|
|
case 'warn': return '⚠️';
|
|
case 'timeout': return '🔇';
|
|
case 'kick': return '👢';
|
|
case 'ban': return '🔨';
|
|
case 'note': return '📝';
|
|
case 'prune': return '🧹';
|
|
default: return '📋';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display a single case
|
|
*/
|
|
export function getCaseEmbed(moderationCase: ModerationCase): EmbedBuilder {
|
|
const emoji = getCaseEmoji(moderationCase.type);
|
|
const color = getCaseColor(moderationCase.type);
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setTitle(`${emoji} Case ${moderationCase.caseId}`)
|
|
.setColor(color)
|
|
.addFields(
|
|
{ name: 'Type', value: moderationCase.type.toUpperCase(), inline: true },
|
|
{ name: 'Status', value: moderationCase.active ? '🟢 Active' : '⚫ Resolved', inline: true },
|
|
{ name: '\u200B', value: '\u200B', inline: true },
|
|
{ name: 'User', value: `${moderationCase.username} (${moderationCase.userId})`, inline: false },
|
|
{ name: 'Moderator', value: moderationCase.moderatorName, inline: true },
|
|
{ name: 'Date', value: time(moderationCase.createdAt, TimestampStyles.ShortDateTime), inline: true }
|
|
)
|
|
.addFields({ name: 'Reason', value: moderationCase.reason })
|
|
.setTimestamp(moderationCase.createdAt);
|
|
|
|
// Add resolution info if resolved
|
|
if (!moderationCase.active && moderationCase.resolvedAt) {
|
|
embed.addFields(
|
|
{ name: '\u200B', value: '**Resolution**' },
|
|
{ name: 'Resolved At', value: time(moderationCase.resolvedAt, TimestampStyles.ShortDateTime), inline: true }
|
|
);
|
|
|
|
if (moderationCase.resolvedReason) {
|
|
embed.addFields({ name: 'Resolution Reason', value: moderationCase.resolvedReason });
|
|
}
|
|
}
|
|
|
|
// Add metadata if present
|
|
if (moderationCase.metadata && Object.keys(moderationCase.metadata).length > 0) {
|
|
const metadataStr = JSON.stringify(moderationCase.metadata, null, 2);
|
|
if (metadataStr.length < 1024) {
|
|
embed.addFields({ name: 'Additional Info', value: `\`\`\`json\n${metadataStr}\n\`\`\`` });
|
|
}
|
|
}
|
|
|
|
return embed;
|
|
}
|
|
|
|
/**
|
|
* Display a list of cases
|
|
*/
|
|
export function getCasesListEmbed(
|
|
cases: ModerationCase[],
|
|
title: string,
|
|
description?: string
|
|
): EmbedBuilder {
|
|
const embed = new EmbedBuilder()
|
|
.setTitle(title)
|
|
.setColor(Colors.Blue)
|
|
.setTimestamp();
|
|
|
|
if (description) {
|
|
embed.setDescription(description);
|
|
}
|
|
|
|
if (cases.length === 0) {
|
|
embed.setDescription('No cases found.');
|
|
return embed;
|
|
}
|
|
|
|
// Group by type for better display
|
|
const casesByType: Record<string, ModerationCase[]> = {};
|
|
for (const c of cases) {
|
|
if (!casesByType[c.type]) {
|
|
casesByType[c.type] = [];
|
|
}
|
|
casesByType[c.type]!.push(c);
|
|
}
|
|
|
|
// Add fields for each type
|
|
for (const [type, typeCases] of Object.entries(casesByType)) {
|
|
const emoji = getCaseEmoji(type);
|
|
const caseList = typeCases.slice(0, 5).map(c => {
|
|
const status = c.active ? '🟢' : '⚫';
|
|
const date = time(c.createdAt, TimestampStyles.ShortDate);
|
|
return `${status} **${c.caseId}** - ${c.reason.substring(0, 50)}${c.reason.length > 50 ? '...' : ''} (${date})`;
|
|
}).join('\n');
|
|
|
|
embed.addFields({
|
|
name: `${emoji} ${type.toUpperCase()} (${typeCases.length})`,
|
|
value: caseList || 'None',
|
|
inline: false
|
|
});
|
|
|
|
if (typeCases.length > 5) {
|
|
embed.addFields({
|
|
name: '\u200B',
|
|
value: `_...and ${typeCases.length - 5} more_`,
|
|
inline: false
|
|
});
|
|
}
|
|
}
|
|
|
|
return embed;
|
|
}
|
|
|
|
/**
|
|
* Display user's active warnings
|
|
*/
|
|
export function getWarningsEmbed(warnings: ModerationCase[], username: string): EmbedBuilder {
|
|
const embed = new EmbedBuilder()
|
|
.setTitle(`⚠️ Active Warnings for ${username}`)
|
|
.setColor(Colors.Yellow)
|
|
.setTimestamp();
|
|
|
|
if (warnings.length === 0) {
|
|
embed.setDescription('No active warnings.');
|
|
return embed;
|
|
}
|
|
|
|
embed.setDescription(`**Total Active Warnings:** ${warnings.length}`);
|
|
|
|
for (const warning of warnings.slice(0, 10)) {
|
|
const date = time(warning.createdAt, TimestampStyles.ShortDateTime);
|
|
embed.addFields({
|
|
name: `${warning.caseId} - ${date}`,
|
|
value: `**Moderator:** ${warning.moderatorName}\n**Reason:** ${warning.reason}`,
|
|
inline: false
|
|
});
|
|
}
|
|
|
|
if (warnings.length > 10) {
|
|
embed.addFields({
|
|
name: '\u200B',
|
|
value: `_...and ${warnings.length - 10} more warnings. Use \`/cases\` to view all._`,
|
|
inline: false
|
|
});
|
|
}
|
|
|
|
return embed;
|
|
}
|
|
|
|
/**
|
|
* Success message after warning a user
|
|
*/
|
|
export function getWarnSuccessEmbed(caseId: string, username: string, reason: string): EmbedBuilder {
|
|
return new EmbedBuilder()
|
|
.setTitle('✅ Warning Issued')
|
|
.setDescription(`**${username}** has been warned.`)
|
|
.addFields(
|
|
{ name: 'Case ID', value: caseId, inline: true },
|
|
{ name: 'Reason', value: reason, inline: false }
|
|
)
|
|
.setColor(Colors.Green)
|
|
.setTimestamp();
|
|
}
|
|
|
|
/**
|
|
* Success message after adding a note
|
|
*/
|
|
export function getNoteSuccessEmbed(caseId: string, username: string): EmbedBuilder {
|
|
return new EmbedBuilder()
|
|
.setTitle('✅ Note Added')
|
|
.setDescription(`Staff note added for **${username}**.`)
|
|
.addFields({ name: 'Case ID', value: caseId, inline: true })
|
|
.setColor(Colors.Green)
|
|
.setTimestamp();
|
|
}
|
|
|
|
/**
|
|
* Success message after clearing a warning
|
|
*/
|
|
export function getClearSuccessEmbed(caseId: string): EmbedBuilder {
|
|
return new EmbedBuilder()
|
|
.setTitle('✅ Warning Cleared')
|
|
.setDescription(`Case **${caseId}** has been resolved.`)
|
|
.setColor(Colors.Green)
|
|
.setTimestamp();
|
|
}
|
|
|
|
/**
|
|
* Error embed for moderation operations
|
|
*/
|
|
export function getModerationErrorEmbed(message: string): EmbedBuilder {
|
|
return new EmbedBuilder()
|
|
.setTitle('❌ Error')
|
|
.setDescription(message)
|
|
.setColor(Colors.Red)
|
|
.setTimestamp();
|
|
}
|
|
|
|
/**
|
|
* Warning embed to send to user via DM
|
|
*/
|
|
export function getUserWarningEmbed(
|
|
serverName: string,
|
|
reason: string,
|
|
caseId: string,
|
|
warningCount: number
|
|
): EmbedBuilder {
|
|
return new EmbedBuilder()
|
|
.setTitle('⚠️ You have received a warning')
|
|
.setDescription(`You have been warned in **${serverName}**.`)
|
|
.addFields(
|
|
{ name: 'Reason', value: reason, inline: false },
|
|
{ name: 'Case ID', value: caseId, inline: true },
|
|
{ name: 'Total Warnings', value: warningCount.toString(), inline: true }
|
|
)
|
|
.setColor(Colors.Yellow)
|
|
.setTimestamp()
|
|
.setFooter({ text: 'Please review the server rules to avoid further action.' });
|
|
}
|