#!/bin/bash # 部署通知脚本 - 支持 Telegram 和 Discord # 使用方法: ./notify.sh <message> [color] # status: success 或 failure # title: 通知标题 # message: 通知内容 # color: Discord 消息颜色(可选,默认根据 status 自动选择) set -e STATUS="$1" TITLE="$2" MESSAGE="$3" COLOR="${4:-}" # 根据状态设置默认值 if [ "$STATUS" = "success" ]; then EMOJI="✅" DEFAULT_COLOR="3066993" # 绿色 STATUS_TEXT="成功" elif [ "$STATUS" = "failure" ]; then EMOJI="❌" DEFAULT_COLOR="15158332" # 红色 STATUS_TEXT="失败" else EMOJI="ℹ️" DEFAULT_COLOR="3447003" # 蓝色 STATUS_TEXT="通知" fi COLOR="${COLOR:-$DEFAULT_COLOR}" # 校验 COLOR 必须是数字 if ! [[ "$COLOR" =~ ^[0-9]+$ ]]; then echo "⚠️ 非法 COLOR 值: $COLOR,使用默认颜色 $DEFAULT_COLOR" COLOR="$DEFAULT_COLOR" fi # 获取 Git 信息 REPO="${GITEA_REPO:-unknown}" BRANCH="${GITEA_REF_NAME:-unknown}" COMMIT_SHA="${GITEA_SHA:-unknown}" COMMIT_SHORT="${COMMIT_SHA:0:7}" AUTHOR="${GITEA_ACTOR:-unknown}" WORKFLOW="${GITEA_WORKFLOW:-unknown}" RUN_NUMBER="${GITEA_RUN_NUMBER:-unknown}" RUN_URL="${GITEA_SERVER_URL:-}/${GITEA_REPO}/actions/runs/${GITEA_RUN_ID:-}" # HTML 转义函数(用于 Telegram) html_escape() { local s="$1" s="${s//&/&}" s="${s//</<}" s="${s//>/>}" s="${s//\"/"}" printf '%s' "$s" } # ==================== Telegram 通知 ==================== send_telegram() { if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then echo "⚠️ Telegram 配置缺失,跳过通知" return 0 fi # 转义所有用户可控的变量 local escaped_title escaped_message escaped_repo escaped_branch escaped_author escaped_workflow escaped_run_url escaped_title=$(html_escape "$TITLE") escaped_message=$(html_escape "$MESSAGE") escaped_repo=$(html_escape "$REPO") escaped_branch=$(html_escape "$BRANCH") escaped_author=$(html_escape "$AUTHOR") escaped_workflow=$(html_escape "$WORKFLOW") escaped_run_url=$(html_escape "$RUN_URL") local tg_message="${EMOJI} <b>${escaped_title}</b> <b>状态:</b> ${STATUS_TEXT} <b>仓库:</b> ${escaped_repo} <b>分支:</b> ${escaped_branch} <b>提交:</b> <code>${COMMIT_SHORT}</code> <b>作者:</b> ${escaped_author} <b>工作流:</b> ${escaped_workflow} #${RUN_NUMBER} ${escaped_message} <a href=\"${escaped_run_url}\">查看详情</a>" echo "📤 发送 Telegram 通知..." local response response=$(curl -sS --connect-timeout 5 --max-time 15 -X POST \ "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -d "chat_id=${TELEGRAM_CHAT_ID}" \ --data-urlencode "text=${tg_message}" \ -d "parse_mode=HTML" \ -d "disable_web_page_preview=true" 2>&1) || { echo "❌ Telegram 请求失败: $response" return 1 } if echo "$response" | grep -q '"ok":true'; then echo "✅ Telegram 通知发送成功" else echo "❌ Telegram 通知发送失败: $response" return 1 fi } # ==================== Discord 通知 ==================== send_discord() { if [ -z "$DISCORD_WEBHOOK_URL" ]; then echo "⚠️ Discord Webhook 配置缺失,跳过通知" return 0 fi echo "📤 发送 Discord 通知..." # 使用 python3 构建 JSON,防止注入 local discord_payload discord_payload=$(python3 -c " import json import sys payload = { 'embeds': [{ 'title': sys.argv[1], 'description': sys.argv[2], 'color': int(sys.argv[3]), 'fields': [ {'name': '状态', 'value': sys.argv[4], 'inline': True}, {'name': '仓库', 'value': sys.argv[5], 'inline': True}, {'name': '分支', 'value': sys.argv[6], 'inline': True}, {'name': '提交', 'value': sys.argv[7], 'inline': True}, {'name': '作者', 'value': sys.argv[8], 'inline': True}, {'name': '工作流', 'value': sys.argv[9], 'inline': True} ], 'footer': {'text': 'Gitea Actions'}, 'timestamp': sys.argv[10], 'url': sys.argv[11] }] } print(json.dumps(payload, ensure_ascii=False)) " \ "${EMOJI} ${TITLE}" \ "${MESSAGE}" \ "${COLOR}" \ "${STATUS_TEXT}" \ "${REPO}" \ "${BRANCH}" \ "\`${COMMIT_SHORT}\`" \ "${AUTHOR}" \ "${WORKFLOW} #${RUN_NUMBER}" \ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ "${RUN_URL}" ) local http_code response_body response_body=$(mktemp) http_code=$(curl -sS -o "$response_body" -w "%{http_code}" \ --connect-timeout 5 --max-time 15 -X POST \ "$DISCORD_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$discord_payload" 2>&1) || { echo "❌ Discord 请求失败: $http_code" rm -f "$response_body" return 1 } if [ "$http_code" = "204" ] || [ "$http_code" = "200" ]; then echo "✅ Discord 通知发送成功 (HTTP $http_code)" rm -f "$response_body" else echo "❌ Discord 通知发送失败 (HTTP $http_code): $(cat "$response_body")" rm -f "$response_body" return 1 fi } # ==================== 主流程 ==================== main() { echo "🔔 开始发送部署通知..." echo "状态: $STATUS" echo "标题: $TITLE" echo "消息: $MESSAGE" echo "" local failed=0 # 发送 Telegram 通知 if ! send_telegram; then failed=$((failed + 1)) fi echo "" # 发送 Discord 通知 if ! send_discord; then failed=$((failed + 1)) fi echo "" if [ $failed -eq 0 ]; then echo "✅ 所有通知发送完成" elif [ $failed -ge 2 ]; then echo "❌ 所有通知渠道均发送失败" exit 1 # 全部失败时退出非 0,让 CI 可见 else echo "⚠️ 部分通知发送失败 ($failed 个)" # 部分失败不退出,避免影响主流程 fi } main