# TG通知
- 找到BotFather创建bot,拿到API (TG_TOKEN),找到@userinfobot 获得user ID(CHAT_ID)
- 进入cloud flare workers,创建一个新worker
- 脚本信息如下
export default { async fetch(req, env) { // 健康检查 if (req.method === "GET") return new Response("OK", { status: 200 }); const ct = req.headers.get("content-type") || ""; let b = {}; try { if (ct.includes("application/json")) { b = await req.json(); } else if (ct.includes("application/x-www-form-urlencoded")) { const f = await req.formData(); b = Object.fromEntries(f.entries()); } else { return new Response("Unsupported Content-Type", { status: 415 }); } } catch (e) { return new Response("Bad Request: invalid body", { status: 400 }); } // ------- helpers ------- const esc = s => String(s ?? "") .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); const num = x => Number(x || 0); const fmtBps = (Bps) => { const bit = num(Bps) * 8; if (bit >= 1e9) return (bit/1e9).toFixed(2) + " Gbps"; if (bit >= 1e6) return (bit/1e6).toFixed(2) + " Mbps"; if (bit >= 1e3) return (bit/1e3).toFixed(2) + " Kbps"; return bit.toFixed(0) + " bps"; }; const fmtBytesBin = (bytes) => { const v = num(bytes), KiB = 1024, MiB = KiB**2, GiB = KiB**3, TiB = KiB**4; if (v >= TiB) return (v/TiB).toFixed(2) + " TiB"; if (v >= GiB) return (v/GiB).toFixed(2) + " GiB"; if (v >= MiB) return (v/MiB).toFixed(2) + " MiB"; if (v >= KiB) return (v/KiB).toFixed(2) + " KiB"; return v + " B"; }; const fmtPct = v => Number(v || 0).toFixed(2) + "%"; const fmtLoad = v => isFinite(num(v)) ? num(v).toFixed(2) : "—"; // 兼容 msg / event const eventStr = String(b.msg ?? b.event ?? ""); const isRecovery = eventStr.includes("恢复"); const icon = isRecovery ? "✅" : "🚨"; const title = isRecovery ? "恢复" : "告警"; // ------- build message ------- const lines = []; lines.push(`${icon} ${title} • ${esc(b.name)} (${esc(b.ip)})`); if (b.datetime) lines.push(`时间:${esc(b.datetime)}`); lines.push(`IN ${fmtBps(b.net_in)} | OUT ${fmtBps(b.net_out)}`); lines.push(`Load 1/5/15: ${fmtLoad(b.load1)} / ${fmtLoad(b.load5)} / ${fmtLoad(b.load15)}`); lines.push(`CPU ${fmtPct(b.cpu)} | MEM ${fmtBytesBin(b.mem)} | DISK ${fmtBytesBin(b.disk)}`); if (eventStr) lines.push(`${esc(eventStr)}`); let text = lines.join("\n"); if (text.length > 4000) text = text.slice(0, 4000) + "\n…"; // ------- send to Telegram ------- if (!env.TG_TOKEN || !env.CHAT_ID) { return new Response("Missing TG_TOKEN/CHAT_ID", { status: 500 }); } try { const tg = await fetch(`https://api.telegram.org/bot${env.TG_TOKEN}/sendMessage`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ chat_id: env.CHAT_ID, text, parse_mode: "HTML", disable_web_page_preview: true }) }); const body = await tg.text(); return new Response(body, { status: tg.status }); } catch (e) { return new Response("Upstream error: " + (e && e.message || String(e)), { status: 502 }); } } } - 返回在设置 –> 域和路由 –> 变量和机密中创建环境变量
类型 名称 值 纯文本 CHAT_ID你的User ID 密钥 TG_TOKEN你的Bot token - 部署,并记录你的worker url
- 脚本信息如下
- 进入哪吒面板后台,创建新通知
- URL 填写之前获得的woker url
- 请求方式:POST
- 类型:JSON
- 请求头:不填也可以
- 请求体:
{"datetime":"#DATETIME#","name":"#SERVER.NAME#","ip":"#SERVER.IP#","cpu":"#SERVER.CPU#","mem":"#SERVER.MEM#","disk":"#SERVER.DISK#","net_in":"#SERVER.NETINSPEED#","net_out":"#SERVER.NETOUTSPEED#","load1":"#SERVER.LOAD1#","load5":"#SERVER.LOAD5#","load15":"#SERVER.LOAD15#","msg":"#NEZHA#"} - 确认
- 通知同一tab中分组 –>通知,创建一个通知分组,取个名字,加入刚刚woker进入该通知组
- 通知 –>警报规则
- 示例规则: [{“type”:”cpu”,”min”:90,”duration”:900,”cover”:0}], [{“type”:”disk”,”min”:95,”duration”:900,”cover”:0}], [{“type”:”net_in_speed”,”min”:12500000,”duration”:900,”cover”:0}]
- 通知组,选刚刚创建的通知分组
- 选择触发模式
- 启用,确认