告别 Bark,拥抱 Serverless:用 Cloudflare Workers + Telegram 打造全能通知网关
📌 为什么需要这个方案?
你是否遇到过这些痛点:
- Bark 只能在 iOS 用,Android 备机收不到通知?
- 想在电脑上同步看到 GitHub 的构建结果?
- Bark 的通知样式太丑,无法自定义?
- 需要一个跨平台的统一通知中心?
起因是 Bark 只支持 iOS,对于 iOS 的状态推送来说,Bark 确实是接收服务器通知的神器。但是我的主力是 Android,我需要定制化一个推送消息提醒。
查了一下后台刚好支持使用 Webhook,我决定利用 Cloudflare Workers 和 Telegram Bot,搭建一套完全免费、支持全平台、且高度可定制的"通知中心"。
🚀 方案优势
✅ 全平台同步
Telegram 的推送服务极其稳定,支持 iOS/Android/PC/Mac,消息看一眼就在所有设备同步已读。
✅ 完全免费
Cloudflare Workers 每天提供 10 万次免费请求,对于个人用途几乎是无限的。
✅ 高度定制
不同于 Bark 的"原样转发",Workers 允许我们编写 JavaScript 代码,对 GitHub、博客评论、服务器报警等 JSON 数据进行清洗和格式化。
🔍 核心原理
外部服务 (GitHub/博客)
↓
发送 Webhook
↓
Cloudflare Worker (你的代码)
↓
清洗格式
↓
Telegram Bot
↓
你的手机/电脑
🛠️ 准备工作
在开始之前,你需要准备:
- ✅ Cloudflare 账号(免费):用于部署 Worker
- ✅ Telegram 账号(免费):用于接收消息
💬 第一阶段:搞定 Telegram 机器人(收信端)
这一步我们要获取两个核心数据:机器人的 Token和你的 Chat ID。
步骤 1:申请机器人
- 打开 Telegram,在搜索栏输入
@BotFather
⚠️ 注意:一定要认准蓝V认证的官方机器人
- 点击
Start,发送指令:
/newbot
- BotFather 会问你两个问题:
- Name : 给机器人起个昵称,随便填(比如
MyNotifyBot) - Username : 给机器人起个唯一的 ID
- Name : 给机器人起个昵称,随便填(比如
📝 必须以
bot结尾,比如r2_notify_bot
- 成功后,BotFather 会回复一段长文,找到 HTTP API Token,格式如下:
123456789:ABCDefGhIJKlmNoPQrStuVWxyz
[!IMPORTANT]
复制这串字符,妥善保管,千万别发给别人!
步骤 2:获取你的 Chat ID
为了防止机器人把消息发给陌生人,我们需要限制它只能发给你。
- 在 Telegram 搜索
@userinfobot - 点击
Start - 它会回复你的信息,找到
Id这一行,是一串纯数字(例如987654321)
📝 记下这串数字,这是你的 Chat ID
步骤 3:初始化机器人(重要!)
[!WARNING]
非常重要的一步:去搜索你刚才创建的机器人 ID(比如@r2_notify_bot),点击Start随便发句Hello。原因:如果没和机器人对话过,它无法主动给你发消息。
☁️ 第二阶段:部署 Cloudflare Worker
这一步是把我们的"代码逻辑"放上网。
步骤 1:创建 Worker
- 登录 Cloudflare Dashboard
- 左侧菜单点击 Workers & Pages → Overview
- 点击蓝色的 Create Application 按钮
- 点击 Create Worker 按钮
- Name: 起个名字,比如
tg-notifier,然后点击 Deploy(部署)
步骤 2:写入代码
- 部署成功后,点击 Edit code 进入在线编辑器
- 清空左侧编辑器里默认的
Hello World代码 - 复制粘贴下面这段完整的代码:
export default {
async fetch(request, env) {
// -------------------------------------------------------------
// 🔧 环境变量配置 (后面会教你在后台设置,不要硬编码在这里)
// -------------------------------------------------------------
const TG_TOKEN = env.TG_TOKEN; // 你的 Bot Token
const TG_CHAT_ID = env.TG_CHAT_ID; // 你的 User ID
const SECRET = env.WEBHOOK_SECRET; // 自定义通信密钥
// 你的博客/后台地址 (用于点击跳转)
const ADMIN_URL = "https://your-blog.com/admin";
// -------------------------------------------------------------
// 1. 安全检查:只允许 POST 请求
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
// 2. 密码检查:防止被爆破
const url = new URL(request.url);
const key = url.searchParams.get("key");
if (SECRET && key !== SECRET) {
return new Response("401 Unauthorized: 密码错误", { status: 401 });
}
try {
const payload = await request.json();
const userAgent = request.headers.get("User-Agent") || "";
let msgText = ""; // 最终要发送的消息内容
// ================= 场景 A: GitHub Actions 构建通知 =================
if (userAgent.startsWith("GitHub-Hookshot")) {
// 只处理 Workflow Run 事件
if (payload.workflow_run) {
const run = payload.workflow_run;
const repo = payload.repository.full_name;
const branch = run.head_branch;
const commitMsg = run.head_commit.message;
// 只有构建成功或失败才通知,忽略 "进行中" 的状态
if (payload.action === "completed") {
const statusEmoji = run.conclusion === "success" ? "✅" : "❌";
if (run.conclusion === "success") {
msgText = `
🎉 <b>项目构建成功!</b>
---------------------------
📦 <b>仓库:</b> ${repo}
🌿 <b>分支:</b> ${branch}
🚀 <b>说明:</b> <code>${commitMsg}</code>
---------------------------
✅ <i>CI/CD 流水线已完成,服务即将更新。</i>
`;
} else if (run.conclusion === "failure") {
msgText = `
❌ <b>构建失败警告</b>
---------------------------
📦 <b>仓库:</b> ${repo}
🌿 <b>分支:</b> ${branch}
---------------------------
⚠️ 请立即检查 GitHub Actions 日志!
`;
}
} else {
// 忽略其他无关状态,不发送通知
return new Response("GitHub Event Ignored (Processing)", { status: 200 });
}
} else {
return new Response("Ignored GitHub Event (Not workflow)", { status: 200 });
}
}
// ================= 场景 B: 博客/CMS 通知 =================
// 1. 新评论通知
else if (payload.author && payload.text) {
const source = payload.refType === 'posts' ? '文章' : '页面';
// 尝试解析评论者链接
let commenterLink = "无";
if (payload.url && payload.url.startsWith('http')) {
commenterLink = `<a href="${payload.url}">点击访问</a>`;
}
msgText = `
💬 <b>收到新评论 (${source})</b>
---------------------------
👤 <b>用户:</b> ${payload.author}
📧 <b>邮箱:</b> ${payload.mail || "未填写"}
🌐 <b>网址:</b> ${commenterLink}
---------------------------
📝 <b>内容:</b>
<pre>${payload.text}</pre>
---------------------------
👮♂️ <a href="${ADMIN_URL}">👉 进入后台审核</a>
`;
}
// 2. 兜底逻辑:普通文本消息 (兼容 Bark 格式)
else if (payload.text) {
if (payload.title) {
msgText = `📢 <b>${payload.title}</b>\n${payload.text}`;
} else {
msgText = `📝 <b>新消息</b>\n${payload.text}`;
}
}
// ================= 发送消息到 Telegram =================
if (msgText) {
const tgUrl = `https://api.telegram.org/bot${TG_TOKEN}/sendMessage`;
const resp = await fetch(tgUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: TG_CHAT_ID,
text: msgText,
parse_mode: "HTML", // 开启 HTML 格式支持
disable_web_page_preview: true
})
});
return new Response("Sent to Telegram", { status: 200 });
}
return new Response("No message sent", { status: 200 });
} catch (err) {
return new Response(`Error: ${err.message}`, { status: 500 });
}
}
};
- 点击右上角的 Save and Deploy 保存代码
🔐 第三阶段:配置环境变量(最容易出错的一步!)
[!IMPORTANT]
千万不要把你的 Token 直接写在代码里! 我们要用 Cloudflare 的环境变量功能。
详细步骤
- 回到 Worker 的详细页面(点击左上角箭头返回)
- 点击顶部的 Settings 选项卡
- 点击左侧的 Variables and Secrets
- 点击 Add variable,依次添加下面三个变量:
| Variable name(变量名) | Value(值) | 说明 |
|---|---|---|
TG_TOKEN | 123456789:ABCD... | 第一阶段申请的那个长 Token |
TG_CHAT_ID | 987654321 | 第一阶段获取的纯数字 ID |
WEBHOOK_SECRET | MySecretPassword2025 | 你自己设一个复杂的密码,防止被别人乱调用 |
- 添加完后,点击 Deploy(或者 Save and Deploy)确保生效
🎯 第四阶段:实战对接
假设你的 Worker 域名是:
https://tg-notifier.你的用户名.workers.dev
你设置的密码(WEBHOOK_SECRET)是:MyPass123
那么你的完整调用地址就是:
https://tg-notifier.你的用户名.workers.dev/?key=MyPass123
1️⃣ 通用测试(CURL)
打开电脑终端(Terminal/CMD),输入:
curl -X POST "https://你的worker地址/?key=MyPass123" \
-H "Content-Type: application/json" \
-d '{"text": "Hello World! 这是来自 Serverless 的问候"}'
如果你手机上的 Telegram 响了,恭喜你,大功告成!🎉
2️⃣ 对接 GitHub Actions
[!WARNING]
这是很多新手容易出错的地方,请务必注意 URL 的填写方式!
详细步骤
- 进入你的 GitHub 仓库 → Settings → Webhooks
- 点击 Add webhook
- Payload URL:
- ✅ 正确写法:
https://....workers.dev/?key=MyPass123(然后在下面 Secret 框里填密码)
- ✅ 正确写法:
- Content type: 选择
application/json - Which events? (触发事件):
- 选择
Let me select individual events - 取消勾选
Pushes - 只勾选
Workflow runs(这才是构建通知)
- 选择
- 点击 Add webhook
这样配置后,每当你提交代码触发构建,Telegram 都会收到一条格式清晰的通知。
3️⃣ 对接博客系统(如 Mix Space)
在博客后台的 Webhook 设置中:
- Webhook URL: 同样填入带 Key 的完整地址
https://....workers.dev/?key=MyPass123
- 事件: 根据需要勾选(如"收到评论"、"文章发布")
效果如下:

❓ 常见问题排查
Q1: 收到报错 "401 Unauthorized"?
A: 你的"钥匙"没对上。
- 检查 Cloudflare 里的
WEBHOOK_SECRET到底设的是什么 - 检查你填在 GitHub/博客里的 URL,
?key=后面跟的是不是一模一样的字符串
Q2: 收到报错 "Method Not Allowed"?
A: 你是不是直接用浏览器打开了那个链接?
浏览器默认是 GET 请求,而我们的代码限制了只收 POST 请求。请用 curl 测试或通过 Webhook 触发。
Q3: GitHub 显示 200 成功,但手机没响?
A: 检查以下几点:
- 检查
TG_CHAT_ID对不对 - 确认你有没有先在 Telegram 里给机器人发过一条消息(激活会话)
- 检查代码里的
payload.action判断逻辑,GitHub 的 PING 事件通常会被代码里的Ignored逻辑过滤掉,这是正常的 - 试着触发一次真正的构建(而不是 PING 测试)
Q4: Telegram 没收到通知?
A: 逐步排查:
- 在 Cloudflare Worker 的 Logs 里查看是否有错误
- 用
curl直接测试 Worker 是否正常工作 - 检查
TG_TOKEN和TG_CHAT_ID是否正确 - 确认机器人没有被你拉黑或删除
🎨 进阶定制
自定义消息格式
你可以在代码中修改 msgText 的内容,支持以下 HTML 标签:
粗体斜体<code>代码</code><pre>代码块</pre><a href="链接">超链接</a>
添加更多场景
比如监控服务器状态、Docker 容器状态等,只需在 else if 中添加新的判断逻辑即可。