feat(ai-moderation): reset offensive nickname instead of deleting message
When the ONLY violation is offensive_username (message content clean): - Message is NOT deleted (nickname-only violation bypasses auto-delete) - Member's server nickname is reset to default username via setNickname(null) (Discord shows the global username again) - Action 'reset_nickname' logged to moderation_actions; cooldown 10min per guild:user (LRU) so repeated messages by same member don't hammer the Discord PATCH - Config: AUTO_NICKNAME_RESET_ENABLED / AUTO_NICKNAME_RESET_COOLDOWN_MS
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Nickname-only enforcement — offensive username flag handling (pure, no DB)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
isNicknameOnlyViolation,
|
||||
parseModerationFlags,
|
||||
} from "../src/modules/ai-moderation/autoDeleteEligibility.js";
|
||||
import type {
|
||||
AnalysisResult,
|
||||
MessageRecord,
|
||||
} from "../src/modules/message-capture/types.js";
|
||||
|
||||
function msg(flagsJson: string | null): MessageRecord {
|
||||
return {
|
||||
id: "m1",
|
||||
guild_id: "g1",
|
||||
channel_id: "c1",
|
||||
thread_id: null,
|
||||
user_id: "u1",
|
||||
username: "user1",
|
||||
avatar_url: null,
|
||||
content: "halo semua",
|
||||
edited_content: null,
|
||||
created_at: Date.now(),
|
||||
edited_at: null,
|
||||
deleted_at: null,
|
||||
type: "text",
|
||||
is_reply: null,
|
||||
is_forward: null,
|
||||
is_crosspost: null,
|
||||
reference_message_id: null,
|
||||
reference_channel_id: null,
|
||||
reference_guild_id: null,
|
||||
metadata: null,
|
||||
ai_moderation_flags: flagsJson,
|
||||
};
|
||||
}
|
||||
|
||||
describe("parseModerationFlags", () => {
|
||||
it("parses JSON array from stored column", () => {
|
||||
expect(parseModerationFlags(msg('["offensive_username","sara"]'))).toEqual([
|
||||
"offensive_username",
|
||||
"sara",
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns [] for null / malformed values", () => {
|
||||
expect(parseModerationFlags(msg(null))).toEqual([]);
|
||||
expect(parseModerationFlags(msg("not-json"))).toEqual([]);
|
||||
});
|
||||
|
||||
it("prefers structured analysisResult flags", () => {
|
||||
const result = { flags: ["vulgar_language"] } as AnalysisResult;
|
||||
expect(parseModerationFlags(msg('["old_flag"]'), result)).toEqual([
|
||||
"vulgar_language",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isNicknameOnlyViolation", () => {
|
||||
it("true when the ONLY flag is offensive_username", () => {
|
||||
expect(isNicknameOnlyViolation(msg('["offensive_username"]'))).toBe(true);
|
||||
});
|
||||
|
||||
it("false when other flags ride along (message itself violated)", () => {
|
||||
expect(isNicknameOnlyViolation(msg('["offensive_username","sara"]'))).toBe(
|
||||
false,
|
||||
);
|
||||
expect(isNicknameOnlyViolation(msg('["harassment"]'))).toBe(false);
|
||||
});
|
||||
|
||||
it("false when no flags at all", () => {
|
||||
expect(isNicknameOnlyViolation(msg(null))).toBe(false);
|
||||
expect(isNicknameOnlyViolation(msg("[]"))).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user