Logo
热心市民王先生

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-action400+功能丰富,支持图片/文件/按钮
yakshasan/telegram-notify50+轻量级,简单易用

适用场景

  • 不想编写复杂 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 组件),理由:

  1. 平衡了易用性和功能性
  2. 社区维护成熟,文档完善
  3. 支持 Markdown 格式和 Inline Keyboard
  4. 无需编写复杂代码

如果担心第三方依赖,选择方案 A,使用原生 curl 调用。

如果是企业级应用,选择方案 C,自建服务确保可控性。


下一步

查看详细的实施指南:[04-implementation-guide.md]