Files
GMW/services/backend/src/modules/chatbot/chatbot.toolDefs.ts
T
asepharyanaandClaude Opus 5 (Nous Research) b67856462f feat(chatbot): expand tool set to cover all server-watcher situations
The chatbot agent now has 14 tools (was 4) so it can answer about ANY
server situation from live data instead of a static snapshot:

- get_server_stats (now also returns clean count)
- get_top_channels, get_recent_activity, get_top_flagged
- search_messages (LIKE keyword search)
- get_user_messages, get_user_profile, get_user_reputation
- get_channel_culture
- get_message_detail (full AI analysis of one message)
- get_message_reviews (human moderation queue by status)
- get_voice_recordings (with transcriptions)
- get_moderation_timeline (daily flagged/warn/clean trend)
- get_corrections (AI false-positive correction history)

Security/quality:
- Every executor now uses parameterized drizzle queries (eq/like/and).
  The old code interpolated model-supplied IDs into sql.raw() — a SQL
  injection vector. Removed.
- Split static tool *definitions* into chatbot.toolDefs.ts (no DB import)
  so the LLM-facing schema can be unit-tested without loading the
  database/config layer. chatbot.tools.ts keeps only the executor.

Verified: tsc + biome clean, 40 backend tests pass (4 new covering the
tool-contract: names unique, required args declared, full situation
coverage).

Co-Authored-By: Claude Opus 5 (Nous Research)
2026-08-16 09:22:20 +07:00

271 lines
9.1 KiB
TypeScript

/**
* Static tool *definitions* for the chatbot LLM (OpenAI function-calling
* format). Kept separate from the executor (chatbot.tools.ts) so the schema
* the model depends on can be imported without pulling in the database /
* config layer.
*
* The chatbot is a server-watcher agent: it can answer about ANY server
* situation — activity, moderation queue, specific users, channels, voice
* recordings, AI correction history, and trends over time — by calling these
* tools, which the executor implements against real tables.
*/
export interface ToolDef {
type: "function";
function: {
name: string;
description: string;
parameters: {
type: "object";
properties: Record<string, unknown>;
required?: string[];
};
};
}
export const tools: ToolDef[] = [
{
type: "function",
function: {
name: "get_server_stats",
description:
"Ambil statistik ringkas server/guild: total pesan, user aktif, jumlah pesan flagged, warn, dan clean. Panggil untuk jawab pertanyaan umum soal kondisi server. guildId/channelId otomatis ter-isi dari scope; kosongkan untuk semua data.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
},
},
},
},
{
type: "function",
function: {
name: "get_top_channels",
description:
"Ambil daftar channel paling aktif (jumlah pesan terbanyak). Panggil untuk 'channel mana paling ramai' atau aktivitas per-channel.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
limit: {
type: "number",
description: "Jumlah channel teratas (default 5, max 10).",
},
},
},
},
},
{
type: "function",
function: {
name: "get_recent_activity",
description:
"Ambil pesan terbaru di server: siapa, di channel mana, jam berapa, isinya. Panggil untuk 'lagi ngapain' / aktivitas terbaru.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
limit: {
type: "number",
description: "Jumlah pesan terakhir (default 5, max 20).",
},
},
},
},
},
{
type: "function",
function: {
name: "get_top_flagged",
description:
"Ambil pesan dengan ai_status flagged (beserta alasan, severity, analysis). Panggil untuk bahas pesan bermasalah / kerjaan moderator.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
limit: { type: "number", description: "Jumlah pesan (default 5)." },
},
},
},
},
{
type: "function",
function: {
name: "search_messages",
description:
"Cari pesan berdasarkan kata kunci di isi pesan (case-insensitive, LIKE). Untuk 'ada yang bahas X gak?' / temukan topik tertentu. Hindari kata terlalu umum.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "Kata kunci pencarian (wajib).",
},
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
limit: { type: "number", description: "Jumlah hasil (default 5)." },
},
required: ["query"],
},
},
},
{
type: "function",
function: {
name: "get_user_messages",
description:
"Ambil pesan terbaru dari satu user tertentu (user_id), opsional di-scope ke guild/channel. Untuk 'chat si A gimana akhir-akhir ini?' — butuh user_id.",
parameters: {
type: "object",
properties: {
userId: { type: "string", description: "ID user (wajib)." },
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
limit: { type: "number", description: "Jumlah pesan (default 10)." },
},
required: ["userId"],
},
},
},
{
type: "function",
function: {
name: "get_user_profile",
description:
"Ambil ringkasan profil AI dari seorang user (pola perilaku, gaya bicara) dari tabel user_profiles. Untuk 'siapa si A?' / konteks perilaku. Butuh user_id.",
parameters: {
type: "object",
properties: {
userId: { type: "string", description: "ID user (wajib)." },
guildId: { type: "string", description: "ID server (opsional)." },
},
required: ["userId"],
},
},
},
{
type: "function",
function: {
name: "get_user_reputation",
description:
"Ambil skor trust, jumlah infraction, dan streak pesan bersih seorang user dari user_reputations. Untuk 'berapa trust score si A?' / riwayat pelanggaran. Butuh user_id.",
parameters: {
type: "object",
properties: {
userId: { type: "string", description: "ID user (wajib)." },
guildId: { type: "string", description: "ID server (opsional)." },
},
required: ["userId"],
},
},
},
{
type: "function",
function: {
name: "get_channel_culture",
description:
"Ambil ringkasan norma/slang channel dari tabel channel_cultures (AI-generated). Untuk 'norma channel ini gimana?' / konteks sebelum nge-flag. Butuh channel_id.",
parameters: {
type: "object",
properties: {
channelId: { type: "string", description: "ID channel (wajib)." },
},
required: ["channelId"],
},
},
},
{
type: "function",
function: {
name: "get_message_detail",
description:
"Ambil 1 pesan lengkap beserta hasil analisis AI-nya (status, flags, score, severity, kategori, analysis, recommended action). Untuk jelasin keputusan moderasi pada pesan tertentu. Butuh message_id.",
parameters: {
type: "object",
properties: {
messageId: { type: "string", description: "ID pesan (wajib)." },
},
required: ["messageId"],
},
},
},
{
type: "function",
function: {
name: "get_message_reviews",
description:
"Ambil antrean review moderasi manual (message_reviews) berdasarkan status: pending/approved/rejected/escalated. Untuk 'ada review moderasi pending?' / cek kerjaan human moderator. guildId otomatis ter-isi.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
status: {
type: "string",
description:
"Status review: pending / approved / rejected / escalated (opsional, default semua).",
},
limit: { type: "number", description: "Jumlah (default 10)." },
},
},
},
},
{
type: "function",
function: {
name: "get_voice_recordings",
description:
"Ambil rekaman suara terbaru (voice_recordings): user, channel, transkripsi, status upload. Untuk 'ada rekaman suara terbaru?' / cek transkripsi. Bisa di-scope ke user_id atau channel_id.",
parameters: {
type: "object",
properties: {
userId: { type: "string", description: "Filter user (opsional)." },
channelId: {
type: "string",
description: "Filter channel (opsional).",
},
guildId: { type: "string", description: "ID server (opsional)." },
limit: { type: "number", description: "Jumlah (default 10)." },
},
},
},
},
{
type: "function",
function: {
name: "get_moderation_timeline",
description:
"Ambil tren harian: per hari, jumlah total pesan vs flagged vs warn vs clean. Untuk 'minggu ini pelanggaran naik?' / lihat tren moderasi. guildId otomatis ter-isi.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
channelId: { type: "string", description: "ID channel (opsional)." },
days: {
type: "number",
description: "Jumlah hari ke belakang (default 14, max 60).",
},
},
},
},
},
{
type: "function",
function: {
name: "get_corrections",
description:
"Ambil riwayat koreksi false-positive AI (corrected_moderations): pesan yang awalnya di-flag tapi dikoreksi manusia, beserta alasannya. Untuk 'AI pernah salah nge-flag apa aja?' / audit akurasi moderasi.",
parameters: {
type: "object",
properties: {
guildId: { type: "string", description: "ID server (opsional)." },
limit: { type: "number", description: "Jumlah (default 10)." },
},
},
},
},
];