03-解决方案设计 (Solution Design)
技术研究 人工智能 Telegram
基于前面的能力验证分析,本章节提出三种可行的解决方案,从简单到复杂覆盖不同场景需求。
方案概览
基于前面的能力验证分析,本章节提出三种可行的解决方案,从简单到复杂覆盖不同场景需求。
方案 A:直接 API 调用(推荐)
方案描述
GitHub Actions 工作流直接调用 Telegram Bot API 的 sendMessage 方法发送通知消息。这是最简洁、最直接的实现方式。
架构图
sequenceDiagram
participant GA as GitHub Actions
participant TA as Telegram API
participant Bot as Telegram Bot
participant User as 用户/群组
GA->>+TA: HTTP POST /sendMessage
Note over GA,TA: 包含 token, chat_id, text
TA->>+Bot: 投递消息
Bot->>+User: 显示通知消息
Bot-->>-TA: 确认接收
TA-->>-GA: 返回响应
实现方式
GitHub Actions Workflow 配置:
name: CI/CD with Telegram Notification
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Tests
id: tests
run: npm test
continue-on-error: true
- name: Send Telegram Notification
if: always()
run: |
STATUS="${{ steps.tests.outcome }}"
if [ "$STATUS" == "success" ]; then
MESSAGE="✅ 构建成功\\n仓库: ${{ github.repository }}\\n分支: ${{ github.ref_name }}"
else
MESSAGE="❌ 构建失败\\n仓库: ${{ github.repository }}\\n分支: ${{ github.ref_name }}\\n请检查日志"
fi
curl -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" \
-d "{
\"chat_id\": \"${{ secrets.TELEGRAM_CHAT_ID }}\",
\"text\": \"$MESSAGE\",
\"parse_mode\": \"Markdown\"
}"
优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 零依赖,使用 curl 即可 | ❌ 需要手动处理 JSON 转义和格式化 |
| ✅ 执行速度快 | ❌ 错误处理较繁琐 |
| ✅ 完全透明可控 | ❌ 不支持复杂的交互元素 |
适用场景
- 简单的 CI/CD 状态通知
- 不需要复杂交互的一次性通知
- 对执行速度敏感的场景
方案 B:使用 GitHub Action 组件
方案描述
使用社区维护的 GitHub Action(如 appleboy/telegram-action)简化配置。
架构图
sequenceDiagram
participant GA as GitHub Actions
participant Action as telegram-action
participant TA as Telegram API
participant User as 用户/群组
GA->>+Action: 调用 with 参数
Action->>+TA: 封装好的 API 请求
TA->>+User: 投递消息
TA-->>-Action: 响应
Action-->>-GA: 执行结果
实现方式
name: CI/CD with Telegram Action
on: [push, pull_request]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Send Telegram Message
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_CHAT_ID }}
token: ${{ secrets.TELEGRAM_TOKEN }}
message: |
🔔 GitHub Actions 通知
事件: ${{ github.event_name }}
仓库: ${{ github.repository }}
提交: ${{ github.sha }}
作者: ${{ github.actor }}
状态: ${{ job.status }}
# 可选:添加格式化
format: markdown
# 可选:添加按钮
inline_keyboard: |
[查看工作流](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 配置简单,YAML 可读性好 | ❌ 引入第三方依赖 |
| ✅ 内置错误处理和重试 | ❌ 功能受限于 Action 作者实现 |
| ✅ 支持富文本和按钮 | ❌ 可能存在维护风险 |
推荐组件
| 组件 | Stars | 特点 |
|---|---|---|
| appleboy/telegram-action | 400+ | 功能丰富,支持图片/文件/按钮 |
| yakshasan/telegram-notify | 50+ | 轻量级,简单易用 |
适用场景
- 不想编写复杂 shell 脚本
- 需要富文本格式和交互按钮
- 愿意接受第三方依赖
方案 C:自建中间服务
方案描述
搭建一个中间服务层,GitHub Actions 调用自己的 API,再由服务层转发到 Telegram。
架构图
sequenceDiagram
participant GA as GitHub Actions
participant API as 自建 API 服务
participant DB as 数据库/缓存
participant TA as Telegram API
participant Bot as Telegram Bot
GA->>+API: Webhook / API 调用
Note over GA,API: 携带事件信息和签名
API->>DB: 查询/记录事件
API->>+TA: 发送消息
TA->>+Bot: 投递
Bot-->>-TA: 确认
TA-->>-API: 响应
API-->>-GA: 处理结果
实现方式
中间服务示例(Node.js + Express):
// server.js
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
// 验证请求签名
function verifySignature(payload, signature) {
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${digest}`)
);
}
// 接收 GitHub Actions 请求
app.post('/webhook/ci-notification', async (req, res) => {
const signature = req.headers['x-hub-signature-256'];
const payload = JSON.stringify(req.body);
if (!verifySignature(payload, signature)) {
return res.status(401).json({ error: 'Unauthorized' });
}
const { event, repository, status, details } = req.body;
// 自定义消息格式
const message = formatMessage(event, repository, status, details);
try {
await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
chat_id: req.body.chat_id,
text: message,
parse_mode: 'MarkdownV2',
reply_markup: {
inline_keyboard: [
[{ text: '📊 查看详情', callback_data: `details:${details.id}` }],
[{ text: '🔄 重新部署', callback_data: `redeploy:${repository}` }]
]
}
});
res.json({ success: true });
} catch (error) {
console.error('Telegram API error:', error);
res.status(500).json({ error: 'Failed to send message' });
}
});
function formatMessage(event, repo, status, details) {
const emoji = status === 'success' ? '✅' : '❌';
return `
${emoji} *CI/CD ${event}*
📦 *Repository:* \`${repo}\`
🔖 *Status:* ${status.toUpperCase()}
⏱ *Duration:* ${details.duration}s
👤 *Triggered by:* ${details.actor}
`.trim();
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
GitHub Actions 调用:
- name: Notify via Custom Webhook
run: |
PAYLOAD=$(jq -n \
--arg event "${{ github.event_name }}" \
--arg repo "${{ github.repository }}" \
--arg status "${{ job.status }}" \
--arg actor "${{ github.actor }}" \
'{
event: $event,
repository: $repo,
status: $status,
actor: $actor,
chat_id: "${{ secrets.TELEGRAM_CHAT_ID }}"
}')
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.WEBHOOK_SECRET }}" | sed 's/^.* //')
curl -X POST "${{ secrets.WEBHOOK_URL }}/ci-notification" \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: sha256=$SIGNATURE" \
-d "$PAYLOAD"
优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 完全可控的业务逻辑 | ❌ 需要维护额外服务 |
| ✅ 可以做请求聚合和限流 | ❌ 架构复杂度增加 |
| ✅ 安全审计和日志记录 | ❌ 引入单点故障 |
| ✅ 支持复杂的交互流程 | ❌ 成本增加 |
适用场景
- 企业级应用,需要完整的审计日志
- 需要复杂的业务逻辑(如根据条件路由到不同聊天组)
- 已有现有的后端服务体系
方案对比与选型建议
| 维度 | 方案 A | 方案 B | 方案 C |
|---|---|---|---|
| 复杂度 | ⭐⭐ 低 | ⭐⭐ 低 | ⭐⭐⭐⭐⭐ 高 |
| 灵活性 | ⭐⭐⭐ 中 | ⭐⭐ 低 | ⭐⭐⭐⭐⭐ 高 |
| 维护成本 | ⭐⭐ 低 | ⭐⭐⭐ 中 | ⭐⭐⭐⭐⭐ 高 |
| 功能丰富度 | ⭐⭐⭐ 中 | ⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐⭐ 极高 |
| 安全性 | ⭐⭐⭐ 中 | ⭐⭐⭐ 中 | ⭐⭐⭐⭐⭐ 高 |
选型决策树
开始
│
├─ 是否需要复杂交互或业务逻辑?
│ ├─ 是 → 方案 C(自建服务)
│ └─ 否 → 继续
│
├─ 是否希望配置简单、开箱即用?
│ ├─ 是 → 方案 B(Action 组件)
│ └─ 否 → 方案 A(直接 curl)
│
└─ 结束
推荐方案
对于大多数用户,推荐方案 B(使用 GitHub Action 组件),理由:
- 平衡了易用性和功能性
- 社区维护成熟,文档完善
- 支持 Markdown 格式和 Inline Keyboard
- 无需编写复杂代码
如果担心第三方依赖,选择方案 A,使用原生 curl 调用。
如果是企业级应用,选择方案 C,自建服务确保可控性。
下一步
查看详细的实施指南:[04-implementation-guide.md]