feat(moderation): Indonesian slang normalizer and false-positive prevention

- Add indonesian-badwords dependency for local lexical signal
- Add Indonesian slang lexicon with woy/woi/hadeh as safe casual terms
- Normalize Discord custom emoji <:name:id> to [emoji:name] in prompts
- Wire normalization evidence into both conversationContext and llmModerationClient prompts
- Harden system prompt: woy/woi are casual greetings, not SARA/hate
- Add tests for emoji normalization, slang mapping, badword detection

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-05-29 18:39:10 +07:00
co-authored by Claude Opus 4.8
parent 87a4afec26
commit fb09ac81c5
8 changed files with 327 additions and 40 deletions
+45 -37
View File
@@ -56,6 +56,9 @@ importers:
helmet:
specifier: ^8.1.0
version: 8.1.0
indonesian-badwords:
specifier: ^1.0.1
version: 1.0.1
libsodium-wrappers:
specifier: ^0.8.4
version: 0.8.4
@@ -110,7 +113,7 @@ importers:
devDependencies:
'@biomejs/biome':
specifier: latest
version: 2.4.15
version: 2.4.16
'@tailwindcss/postcss':
specifier: ^4.3.0
version: 4.3.0
@@ -250,7 +253,7 @@ importers:
devDependencies:
'@biomejs/biome':
specifier: latest
version: 2.4.15
version: 2.4.16
'@discordjs/docgen':
specifier: ^0.11.1
version: 0.11.1
@@ -321,8 +324,8 @@ packages:
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/biome@2.4.15':
resolution: {integrity: sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==}
'@biomejs/biome@2.4.16':
resolution: {integrity: sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==}
engines: {node: '>=14.21.3'}
hasBin: true
@@ -332,8 +335,8 @@ packages:
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-arm64@2.4.15':
resolution: {integrity: sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==}
'@biomejs/cli-darwin-arm64@2.4.16':
resolution: {integrity: sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
@@ -344,8 +347,8 @@ packages:
cpu: [x64]
os: [darwin]
'@biomejs/cli-darwin-x64@2.4.15':
resolution: {integrity: sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==}
'@biomejs/cli-darwin-x64@2.4.16':
resolution: {integrity: sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
@@ -357,8 +360,8 @@ packages:
os: [linux]
libc: [musl]
'@biomejs/cli-linux-arm64-musl@2.4.15':
resolution: {integrity: sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==}
'@biomejs/cli-linux-arm64-musl@2.4.16':
resolution: {integrity: sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
@@ -371,8 +374,8 @@ packages:
os: [linux]
libc: [glibc]
'@biomejs/cli-linux-arm64@2.4.15':
resolution: {integrity: sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==}
'@biomejs/cli-linux-arm64@2.4.16':
resolution: {integrity: sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
@@ -385,8 +388,8 @@ packages:
os: [linux]
libc: [musl]
'@biomejs/cli-linux-x64-musl@2.4.15':
resolution: {integrity: sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==}
'@biomejs/cli-linux-x64-musl@2.4.16':
resolution: {integrity: sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
@@ -399,8 +402,8 @@ packages:
os: [linux]
libc: [glibc]
'@biomejs/cli-linux-x64@2.4.15':
resolution: {integrity: sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==}
'@biomejs/cli-linux-x64@2.4.16':
resolution: {integrity: sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
@@ -412,8 +415,8 @@ packages:
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-arm64@2.4.15':
resolution: {integrity: sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==}
'@biomejs/cli-win32-arm64@2.4.16':
resolution: {integrity: sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
@@ -424,8 +427,8 @@ packages:
cpu: [x64]
os: [win32]
'@biomejs/cli-win32-x64@2.4.15':
resolution: {integrity: sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==}
'@biomejs/cli-win32-x64@2.4.16':
resolution: {integrity: sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
@@ -3098,6 +3101,9 @@ packages:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
indonesian-badwords@1.0.1:
resolution: {integrity: sha512-A8V6hklYqql15yelhnx5jkoDLru9hPr/q7TE6S4fICZvb7lbluWUXqGjcWpgmDCsbXpecs38/fbuhvM0em/7CQ==}
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
@@ -4935,63 +4941,63 @@ snapshots:
'@biomejs/cli-win32-arm64': 2.3.8
'@biomejs/cli-win32-x64': 2.3.8
'@biomejs/biome@2.4.15':
'@biomejs/biome@2.4.16':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.4.15
'@biomejs/cli-darwin-x64': 2.4.15
'@biomejs/cli-linux-arm64': 2.4.15
'@biomejs/cli-linux-arm64-musl': 2.4.15
'@biomejs/cli-linux-x64': 2.4.15
'@biomejs/cli-linux-x64-musl': 2.4.15
'@biomejs/cli-win32-arm64': 2.4.15
'@biomejs/cli-win32-x64': 2.4.15
'@biomejs/cli-darwin-arm64': 2.4.16
'@biomejs/cli-darwin-x64': 2.4.16
'@biomejs/cli-linux-arm64': 2.4.16
'@biomejs/cli-linux-arm64-musl': 2.4.16
'@biomejs/cli-linux-x64': 2.4.16
'@biomejs/cli-linux-x64-musl': 2.4.16
'@biomejs/cli-win32-arm64': 2.4.16
'@biomejs/cli-win32-x64': 2.4.16
'@biomejs/cli-darwin-arm64@2.3.8':
optional: true
'@biomejs/cli-darwin-arm64@2.4.15':
'@biomejs/cli-darwin-arm64@2.4.16':
optional: true
'@biomejs/cli-darwin-x64@2.3.8':
optional: true
'@biomejs/cli-darwin-x64@2.4.15':
'@biomejs/cli-darwin-x64@2.4.16':
optional: true
'@biomejs/cli-linux-arm64-musl@2.3.8':
optional: true
'@biomejs/cli-linux-arm64-musl@2.4.15':
'@biomejs/cli-linux-arm64-musl@2.4.16':
optional: true
'@biomejs/cli-linux-arm64@2.3.8':
optional: true
'@biomejs/cli-linux-arm64@2.4.15':
'@biomejs/cli-linux-arm64@2.4.16':
optional: true
'@biomejs/cli-linux-x64-musl@2.3.8':
optional: true
'@biomejs/cli-linux-x64-musl@2.4.15':
'@biomejs/cli-linux-x64-musl@2.4.16':
optional: true
'@biomejs/cli-linux-x64@2.3.8':
optional: true
'@biomejs/cli-linux-x64@2.4.15':
'@biomejs/cli-linux-x64@2.4.16':
optional: true
'@biomejs/cli-win32-arm64@2.3.8':
optional: true
'@biomejs/cli-win32-arm64@2.4.15':
'@biomejs/cli-win32-arm64@2.4.16':
optional: true
'@biomejs/cli-win32-x64@2.3.8':
optional: true
'@biomejs/cli-win32-x64@2.4.15':
'@biomejs/cli-win32-x64@2.4.16':
optional: true
'@colors/colors@1.6.0': {}
@@ -7370,6 +7376,8 @@ snapshots:
indent-string@4.0.0: {}
indonesian-badwords@1.0.1: {}
inflight@1.0.6:
dependencies:
once: 1.4.0