feat: expand traffic alerts to discord/telegram
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s
This commit is contained in:
@@ -25,6 +25,11 @@ API_KEY="your-secure-mcp-key"
|
||||
GITHUB_TOKEN="github_pat_..."
|
||||
# 監控告警:外部導流/外部操作事件 webhook(可留空)
|
||||
VIBEWORK_TRAFFIC_WEBHOOK_URL="https://your-webhook"
|
||||
# 直接推送到 Discord(可留空)
|
||||
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/xxx"
|
||||
# Telegram 告警(可留空)
|
||||
TELEGRAM_BOT_TOKEN="123456:abcdef"
|
||||
TELEGRAM_CHAT_ID="-1001234567890"
|
||||
# optional:只允許有 token 的 /api/traffic 查詢
|
||||
TRAFFIC_MONITOR_TOKEN="your-monitor-token"
|
||||
# optional:Scout 掃描參數
|
||||
|
||||
@@ -13,6 +13,29 @@ export type TrafficAlertEvent = {
|
||||
};
|
||||
|
||||
const TRAFFIC_WEBHOOK_URL = process.env.VIBEWORK_TRAFFIC_WEBHOOK_URL?.trim();
|
||||
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN?.trim();
|
||||
const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID?.trim();
|
||||
const DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL?.trim();
|
||||
|
||||
function escapeMarkdown(value: unknown) {
|
||||
if (value === null || value === undefined) return "";
|
||||
return String(value)
|
||||
.replace(/([_*[\]()~`>#+=|{}.!-])/g, "\\$1")
|
||||
.replace(/\n/g, "\\n");
|
||||
}
|
||||
|
||||
function buildTelegramMessage(event: TrafficAlertEvent) {
|
||||
return (
|
||||
`*VibeWork 流量告警*` +
|
||||
`\n- 平台: \`agent-bounty-protocol\`` +
|
||||
`\n- 等級: \`${event.level}\`` +
|
||||
`\n- 行為: \`${event.action}\`` +
|
||||
`\n- 通道: \`${event.surface}\`` +
|
||||
`\n- Actor: \`${event.actorType}/${event.actorId}\`` +
|
||||
`\n- 任務: \`${event.taskId || "n/a"}\`` +
|
||||
`\n- 訊息: ${escapeMarkdown(event.message)}`
|
||||
);
|
||||
}
|
||||
|
||||
function resolveEntityFromTrafficEvent(event: TrafficAlertEvent) {
|
||||
if (event.taskId) {
|
||||
@@ -55,25 +78,69 @@ async function writeTrafficAuditEvent(event: TrafficAlertEvent) {
|
||||
export async function sendTrafficAlert(event: TrafficAlertEvent): Promise<void> {
|
||||
void writeTrafficAuditEvent(event);
|
||||
|
||||
if (!TRAFFIC_WEBHOOK_URL) return;
|
||||
|
||||
const payload = {
|
||||
platform: "agent-bounty-protocol",
|
||||
created_at: new Date().toISOString(),
|
||||
...event,
|
||||
};
|
||||
|
||||
try {
|
||||
await fetch(TRAFFIC_WEBHOOK_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-trace-source": "agent-bounty-protocol",
|
||||
const notifyTargets = [
|
||||
TRAFFIC_WEBHOOK_URL && {
|
||||
kind: "generic",
|
||||
url: TRAFFIC_WEBHOOK_URL,
|
||||
init: {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-trace-source": "agent-bounty-protocol",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
signal: AbortSignal.timeout(3000),
|
||||
});
|
||||
} catch {
|
||||
// Avoid affecting request flow if webhook fails.
|
||||
}
|
||||
},
|
||||
DISCORD_WEBHOOK_URL && {
|
||||
kind: "discord",
|
||||
url: DISCORD_WEBHOOK_URL,
|
||||
init: {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: `\`\`\`\n${JSON.stringify(payload, null, 2)}\n\`\`\``,
|
||||
}),
|
||||
},
|
||||
},
|
||||
TELEGRAM_BOT_TOKEN && TELEGRAM_CHAT_ID && {
|
||||
kind: "telegram",
|
||||
url: `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`,
|
||||
init: {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
chat_id: TELEGRAM_CHAT_ID,
|
||||
text: buildTelegramMessage(event),
|
||||
parse_mode: "MarkdownV2",
|
||||
}),
|
||||
},
|
||||
},
|
||||
].filter(Boolean) as Array<{ kind: string; url: string; init: RequestInit }>;
|
||||
|
||||
if (notifyTargets.length === 0) return;
|
||||
|
||||
await Promise.allSettled(
|
||||
notifyTargets.map(async (target) => {
|
||||
try {
|
||||
const response = await fetch(target.url, { ...target.init, signal: AbortSignal.timeout(3000) });
|
||||
if (!response.ok) {
|
||||
console.warn(
|
||||
`[Traffic alert notify failed] ${target.kind} ${response.status} ${await response.text()}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[Traffic alert notify failed] ${target.kind}`, error);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user