fix: add server_name to moderation_actions for full context retention
Denormalize guild name alongside username so the moderation dashboard shows both TARGET and server even after message table purges. Migration 0018. Frontend displays 'username · server_name' in TARGET.
This commit is contained in:
@@ -125,6 +125,7 @@ export class ModerationRepository {
|
||||
a.evidence,
|
||||
a.policy_version,
|
||||
a.username,
|
||||
a.server_name,
|
||||
LEFT(m.content, 300) AS content
|
||||
FROM moderation_actions a
|
||||
LEFT JOIN messages m ON m.id = a.message_id
|
||||
@@ -155,6 +156,7 @@ export class ModerationRepository {
|
||||
evidence: parseJsonArray(r.evidence),
|
||||
policy_version: r.policy_version ? String(r.policy_version) : null,
|
||||
username: r.username ? String(r.username) : null,
|
||||
server_name: r.server_name ? String(r.server_name) : null,
|
||||
content: r.content ? String(r.content) : null,
|
||||
}));
|
||||
|
||||
@@ -338,6 +340,7 @@ export class ModerationRepository {
|
||||
confidence: r.confidence != null ? Number(r.confidence) : null,
|
||||
score: r.score != null ? Number(r.score) : null,
|
||||
username: r.username ? String(r.username) : null,
|
||||
server_name: r.server_name ? String(r.server_name) : null,
|
||||
content: r.content ? String(r.content) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE "moderation_actions" ADD COLUMN "server_name" text;
|
||||
@@ -127,6 +127,13 @@
|
||||
"when": 1787810000000,
|
||||
"tag": "0017_add_username_to_moderation_actions",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"version": "7",
|
||||
"when": 1787820000000,
|
||||
"tag": "0018_add_server_name_to_moderation_actions",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -188,6 +188,7 @@ function hasPermissionApi(channel: unknown): channel is {
|
||||
async function logAutoDeleteAttempt(
|
||||
message: MessageRecord,
|
||||
result: AutoDeleteResult,
|
||||
serverName?: string | null,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await messageStore.createModerationAction({
|
||||
@@ -198,6 +199,7 @@ async function logAutoDeleteAttempt(
|
||||
reason: result.reason,
|
||||
...verdictToActionFields(message),
|
||||
username: message.username,
|
||||
server_name: serverName ?? null,
|
||||
executed_by: "auto-delete-manager",
|
||||
status: result.deleted
|
||||
? "executed"
|
||||
@@ -264,6 +266,7 @@ export async function attemptAutoDeleteFlaggedMessage(
|
||||
"nickname melanggar aturan server (offensive_username); pesan dibiarkan",
|
||||
...verdictToActionFields(message),
|
||||
username: message.username,
|
||||
server_name: null,
|
||||
executed_by: "auto-delete-manager",
|
||||
status: resetOk ? "executed" : "failed",
|
||||
error: resetOk ? null : "nickname_reset_failed",
|
||||
@@ -384,7 +387,7 @@ export async function attemptAutoDeleteFlaggedMessage(
|
||||
skipped: true,
|
||||
reason: "dry_run",
|
||||
};
|
||||
await logAutoDeleteAttempt(message, result);
|
||||
await logAutoDeleteAttempt(message, result, guild.name);
|
||||
logger.info(
|
||||
{ messageId: message.id, channelId },
|
||||
"Auto-delete dry-run: would delete flagged message",
|
||||
@@ -416,7 +419,7 @@ export async function attemptAutoDeleteFlaggedMessage(
|
||||
skipped: false,
|
||||
reason: "deleted",
|
||||
};
|
||||
await logAutoDeleteAttempt(message, result);
|
||||
await logAutoDeleteAttempt(message, result, guild.name);
|
||||
logger.info(
|
||||
{ messageId: message.id, channelId },
|
||||
"Auto-deleted AI-flagged message",
|
||||
|
||||
@@ -104,6 +104,7 @@ export class ModerationHandler {
|
||||
| "ban_user",
|
||||
reason: payload.reason ?? null,
|
||||
username: null,
|
||||
server_name: null,
|
||||
executed_by: payload.executed_by ?? "command-handler",
|
||||
status: "executed",
|
||||
error: null,
|
||||
|
||||
@@ -643,6 +643,7 @@ export const pgModerationActionsTable = pgTable(
|
||||
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
||||
policy_version: pgText("policy_version"), // rules.ts policy version string
|
||||
username: pgText("username"), // denormalized from messages table for retention safety
|
||||
server_name: pgText("server_name"), // denormalized guild name for retention safety
|
||||
},
|
||||
(table) => ({
|
||||
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
||||
|
||||
@@ -60,6 +60,7 @@ export const pgModerationActionsTable = pgTable(
|
||||
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
||||
policy_version: pgText("policy_version"), // rules.ts policy version string
|
||||
username: pgText("username"), // denormalized from messages table for retention safety
|
||||
server_name: pgText("server_name"), // denormalized guild name for retention safety
|
||||
},
|
||||
(table) => ({
|
||||
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
||||
|
||||
@@ -209,6 +209,7 @@ export interface ModerationAction {
|
||||
action_type: ModerationActionType;
|
||||
reason: string | null;
|
||||
username: string | null;
|
||||
server_name: string | null;
|
||||
executed_by: string | null;
|
||||
status: "pending" | "executed" | "failed";
|
||||
error: string | null;
|
||||
|
||||
@@ -151,6 +151,14 @@ export function LiveModerationFeed({
|
||||
<span className="text-ink-soft">
|
||||
{a.username ?? a.user_id ?? "UNKNOWN_SUBJECT"}
|
||||
</span>
|
||||
{a.server_name && (
|
||||
<>
|
||||
<span>·</span>
|
||||
<span className="text-ink-soft">
|
||||
{a.server_name}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface ModerationAction {
|
||||
created_at: number | null;
|
||||
executed_at: number | null;
|
||||
username: string | null;
|
||||
server_name: string | null;
|
||||
content: string | null;
|
||||
// ── Explainability (structured verdict, surfaced read-only to public web) ──
|
||||
flags: string[] | null;
|
||||
@@ -80,6 +81,7 @@ export interface CategoryAction {
|
||||
confidence: number | null;
|
||||
score: number | null;
|
||||
username: string | null;
|
||||
server_name: string | null;
|
||||
content: string | null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user