import { env } from "@/env"; import { prisma } from "../prisma"; export type { WebhookAction } from "@/types/admin"; import type { WebhookAction } from "@/types/admin"; interface WebhookPayload { action: WebhookAction; actor: string; target: string; targetId?: number; details?: string; } const ACTION_COLORS: Record = { ban: 0xf87272, unban: 0x36d399, kick: 0xfbbd23, disconnect: 0xfbbd23, room_delete: 0xf87272, user_edit: 0x3abff8, user_create: 0x36d399, news_publish: 0x36d399, news_delete: 0xf87272, hotel_alert: 0xa78bfa, staff_alert: 0xa78bfa, ticket_create: 0x3abff8, event_create: 0x36d399, event_update: 0x3abff8, event_cancel: 0xf87272, poll_create: 0x36d399, poll_close: 0xa78bfa, }; async function getSetting( cmsKey: string, envValue?: string, ): Promise { if (envValue) return envValue; try { const setting = await prisma.websiteSetting.findUnique({ where: { key: cmsKey }, }); return setting?.value || null; } catch { return null; } } async function sendDiscord(payload: WebhookPayload): Promise { const url = await getSetting("webhook_discord_url", env.DISCORD_WEBHOOK_URL); if (!url) return; const embed = { title: `[${payload.action.toUpperCase().replace("_", " ")}]`, color: ACTION_COLORS[payload.action] ?? 0x666666, fields: [ { name: "Staff", value: payload.actor, inline: true }, { name: "Target", value: payload.target, inline: true }, ], timestamp: new Date().toISOString(), }; if (payload.details) { embed.fields.push({ name: "Details", value: payload.details, inline: false, }); } try { await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ embeds: [embed] }), signal: AbortSignal.timeout(5000), }); } catch (err) { console.error("[Webhook] Discord send failed:", { action: payload.action, error: (err as Error).message, }); } } async function sendTelegram(payload: WebhookPayload): Promise { const token = await getSetting( "webhook_telegram_bot_token", env.TELEGRAM_BOT_TOKEN, ); const chatId = await getSetting( "webhook_telegram_chat_id", env.TELEGRAM_CHAT_ID, ); if (!token || !chatId) return; const text = [ `[${payload.action.toUpperCase().replace("_", " ")}]`, `Staff: ${payload.actor}`, `Target: ${payload.target}`, payload.details ? `Details: ${payload.details}` : "", ] .filter(Boolean) .join("\n"); try { await fetch(`https://api.telegram.org/bot${token}/sendMessage`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ chat_id: chatId, text, parse_mode: "HTML" }), signal: AbortSignal.timeout(5000), }); } catch (err) { console.error("[Webhook] Telegram send failed:", { action: payload.action, error: (err as Error).message, }); } } /** Fire-and-forget notification to Discord + Telegram */ export function notify(payload: WebhookPayload): void { sendDiscord(payload).catch(() => {}); sendTelegram(payload).catch(() => {}); }