2026-06-09 19:46:08 +07:00
import { createChildLogger } from "@bete/shared/logger" ;
const logger = createChildLogger ( "stickerPrompt" );
2026-06-01 21:44:29 +07:00
/**
* Sticker-specific prompt templates for AI moderation.
*
* Discord stickers are cartoon/meme artwork — not real photos.
* These prompts give the LLM proper context to avoid false-positive flags
* based solely on sticker names or cartoon imagery.
*/
/**
* Prompt used when a sticker image was successfully downloaded (from cache
* or network) and is being sent to the vision LLM as a base64 image.
*
* Explains that stickers are cartoon art, not documentation of real events,
* and instructs the model to apply looser standards for cartoon content.
*/
export function buildStickerVisionPrompt (
stickerName : string ,
messageId : string ,
) : string {
2026-06-09 19:46:08 +07:00
logger . debug ({ stickerName , messageId }, "Building sticker vision prompt" );
2026-06-01 21:44:29 +07:00
return [
`Analisis sticker Discord berikut sebagai evidence moderasi.` ,
`Sticker " ${ stickerName } " berasal dari pesan id= ${ messageId } .` ,
`` ,
`PENTING — Konteks Sticker:` ,
`- Sticker Discord adalah gambar KARTUN/MEME/ILUSTRASI, BUKAN foto atau video nyata.` ,
`- Sticker sering bersifat humor, satir, atau ekspresi emosi yang dilebih-lebihkan.` ,
`- Gambar di sticker bisa menampilkan adegan yang terlihat "keras" (tokoh kartun menginjak sesuatu, ledakan komik, senjata kartun, tokoh berantem) — itu SENI KARTUN, bukan dokumentasi kekerasan atau ancaman nyata.` ,
`- Teks di sticker sering berupa lelucon, sindiran, atau ekspresi khas komunitas — bukan ancaman literal.` ,
`` ,
`Jelaskan isi visual, teks yang terlihat, dan konteks risiko.` ,
`Terapkan standar yang lebih longgar untuk konten kartun/meme:` ,
`- Adegan kartun yang terlihat "keras" ≠ kekerasan nyata → jangan flag "violence" kecuali jelas menargetkan individu/kelompok nyata dengan ancaman serius.` ,
`- Nama sticker yang terdengar provokatif (mis. "Singa injek pejabat") adalah konteks satir/kartun, bukan bukti pelanggaran.` ,
`- Humor/satir/politik kartun ≠ SARA atau hate speech.` ,
`- Sticker yang menampilkan tokoh kartun dalam pose agresif adalah ekspresi/emosi umum di Discord, bukan harassment.` ,
`` ,
`Jawab Bahasa Indonesia, maksimal 3 kalimat. Jangan bilang kurang konteks atau perlu admin cek.` ,
]. join ( "\n" );
}
/**
* Wrapper for text-only evidence when a sticker image failed to download.
*
* Returns a formatted string that explicitly tells the LLM not to flag
* based on the sticker name alone, since names can sound provocative
* while the actual cartoon image is harmless.
*/
export function buildStickerTextOnlyWarning (
stickerName : string ,
stickerUrl : string ,
) : string {
2026-06-09 19:46:08 +07:00
logger . debug (
{ stickerName , stickerUrl },
"Building sticker text-only warning" ,
);
2026-06-01 21:44:29 +07:00
return (
`[sticker: " ${ stickerName } " ( ${ stickerUrl } ) — GAMBAR GAGAL DIUNDUH. ` +
`" ${ stickerName } " adalah sticker kartun/meme Discord. ` +
`JANGAN flag berdasarkan nama sticker saja tanpa gambar visual. ` +
`Sticker Discord adalah seni kartun/ekspresi humor, bukan foto nyata. ` +
`Nama yang terdengar provokatif adalah hal umum untuk sticker satir/humor di Discord.]`
);
}
2026-07-02 00:02:41 +07:00
/**
* Build a neutral fallback text when an image/video attachment cannot be
* downloaded or analyzed. Unlike the sticker warning, this is deliberately
* neutral — it only records *that* an attachment existed, without any
* instruction to the LLM about how to treat it. The absence of visual
* data already means the LLM must rely on message content alone.
*
* Returns a formatted string for inclusion in the media context block.
*/
export function buildAttachmentTextOnlyWarning (
filename : string ,
messageId : string ,
) : string {
const fallback = `[attachment: " ${ filename } " dari pesan id= ${ messageId } — tidak tersedia untuk analisis visual]` ;
logger . debug ({ filename , messageId }, "Built attachment text-only fallback" );
return fallback ;
}
2026-06-01 21:44:29 +07:00
/**
* Prompt used when a custom emoji image was successfully downloaded
* and is being sent to the vision LLM as a base64 image.
*
* Custom emojis are small icons — context is similar to stickers.
*/
export function buildCustomEmojiVisionPrompt (
emojiName : string ,
messageId : string ,
) : string {
2026-06-09 19:46:08 +07:00
logger . debug ({ emojiName , messageId }, "Building custom emoji vision prompt" );
2026-06-01 21:44:29 +07:00
return [
`Analisis custom emoji Discord berikut sebagai evidence moderasi.` ,
`Emoji " ${ emojiName } " berasal dari pesan id= ${ messageId } .` ,
`` ,
`PENTING — Konteks Custom Emoji:` ,
`- Custom emoji Discord adalah ikon kecil/ekspresi, BUKAN foto atau dokumen nyata.` ,
`- Emoji sering digunakan untuk ekspresi emosi, reaksi, atau lelucon.` ,
`- Jangan flag berdasarkan nama emoji saja — analisis isi visual gambar.` ,
`- Emoji yang terlihat lucu/aneh adalah hal umum di Discord, bukan pelanggaran.` ,
`` ,
`Jelaskan isi visual dan konteks risiko.` ,
`Jawab Bahasa Indonesia, maksimal 2 kalimat. Jangan bilang kurang konteks.` ,
]. join ( "\n" );
}
/**
* Fallback text for when a custom emoji image failed to download.
*/
export function buildCustomEmojiTextOnlyFallback ( emojiName : string ) : string {
2026-06-09 19:46:08 +07:00
logger . debug ({ emojiName }, "Building custom emoji text-only fallback" );
2026-06-01 21:44:29 +07:00
return (
`[custom_emoji: " ${ emojiName } " — GAMBAR GAGAL DIUNDUH. ` +
`" ${ emojiName } " adalah custom emoji Discord (ikon kecil). ` +
`JANGAN flag berdasarkan nama emoji saja tanpa gambar visual. ` +
`Custom emoji di Discord adalah ekspresi/emosi umum, bukan konten ofensif.]`
);
}
2026-06-02 11:08:41 +07:00
/**
* Prompt for analyzing regular images (attachments, embeds, links).
*
* VISION MODEL ONLY DESCRIBES — it does NOT decide moderation.
* The main text LLM makes all moderation decisions using the description.
*/
export function buildGeneralImageVisionPrompt (
sourceLabel : string ,
_messageId : string ,
) : string {
2026-06-09 19:46:08 +07:00
logger . debug ({ sourceLabel }, "Building general image vision prompt" );
2026-06-02 11:08:41 +07:00
return [
`Deskripsikan gambar ini secara objektif dan spesifik.` ,
` ${ sourceLabel } ` ,
`` ,
`Jelaskan HANYA apa yang kamu LIHAT:` ,
`- Objek utama apa yang ada di gambar?` ,
`- Teks apa yang terlihat? (tulis persis jika bisa dibaca)` ,
`- Warna dominan dan layout/tata letak?` ,
`- Apakah ini screenshot, foto, meme, kartun, atau dokumen?` ,
`- Konteks: apakah terlihat seperti aplikasi chat, terminal/console,` ,
` media sosial, game, website, editor kode, dokumen, atau lainnya?` ,
`` ,
`PENTING — Deskripsi saja, JANGAN MEMUTUSKAN MODERASI:` ,
`- JANGAN sebut "gambling", "judi", "pelanggaran", "melanggar", atau flag apapun.` ,
`- JANGAN bilang "harus dihapus", "harus diblokir", atau rekomendasi tindakan.` ,
`- Tugasmu HANYA mendeskripsikan isi gambar. BUKAN menilai.` ,
`- Screenshot terminal/console/shell/editor kode → deskripsikan sebagai "terminal/console".` ,
`- Screenshot aplikasi chat (Discord/WA/Telegram/dll) → deskripsikan sebagai "aplikasi chat".` ,
`- Screenshot website dengan grafik/chart → deskripsikan kontennya secara faktual.` ,
`- JANGAN PERNAH mengklaim gambar adalah "situs judi" atau "antarmuka perjudian".` ,
` Itu BUKAN tugasmu. Kamu hanya perlu menyebutkan: "tampilan website dengan grafik",` ,
` "screenshot terminal", "aplikasi chat dengan teks percakapan", dll.` ,
`` ,
`Format jawaban: Deskripsi singkat 2-3 kalimat dalam Bahasa Indonesia.` ,
`Mulai dengan menyebutkan JENIS gambar (screenshot/foto/kartun/dokumen).` ,
]. join ( "\n" );
}