#!/bin/bash # isep-notify 的「Bot API 直送」測試(inkstone/ISEP#130) # # 它守什麼: 雲端唯一走得通的通知路。2026-09-07 從雲端實測:leo21c 的 notify_leo 回 404、 # youlin 被 egress proxy 擋(403 policy denial)、api.telegram.org 通。 # ⇒ 有 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID 就先直送;沒有要講清楚缺哪兩個,不能靜默。 # # 🔴 全程不打真 Telegram:Bot API 指到本機一個假伺服器(只回固定 JSON), # 實例那條用 ISEP_NOTIFY_OFFLINE=1 關掉、閘用 ISEP_NOTIFY_GUARD 指到真跡。 cd "$(dirname "$0")/.." || exit 1 N=scripts/isep-notify TMP=$(mktemp -d); trap 'rm -rf "$TMP"; [ -n "${SRV:-}" ] && kill "$SRV" 2>/dev/null' EXIT export ISEP_NOTIFY_STATE_DIR="$TMP/state" ISEP_NOTIFY_GUARD="$PWD/hooks/prod-write-guard.sh" unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID PASS=0; FAIL=0 ok(){ PASS=$((PASS+1)); printf ' ✅ %s\n' "$1"; } bad(){ FAIL=$((FAIL+1)); printf ' ❌ %s\n' "$1"; } # 假 Bot API:記下收到什麼,回 Telegram 的形狀 cat > "$TMP/srv.py" <<'PY' import json, sys from http.server import BaseHTTPRequestHandler, HTTPServer LOG = sys.argv[2] class H(BaseHTTPRequestHandler): def do_POST(self): body = self.rfile.read(int(self.headers.get("Content-Length") or 0)).decode() open(LOG, "a").write(json.dumps({"path": self.path, "body": body, "ua": self.headers.get("User-Agent")}) + "\n") ok = "/botGOOD/" in self.path out = json.dumps({"ok": True, "result": {"message_id": 42}} if ok else {"ok": False, "description": "Unauthorized"}).encode() self.send_response(200 if ok else 401); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(out) def log_message(self, *a): pass HTTPServer(("127.0.0.1", int(sys.argv[1])), H).serve_forever() PY PORT=$(python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",0));print(s.getsockname()[1])') python3 "$TMP/srv.py" "$PORT" "$TMP/log" & SRV=$! for i in 1 2 3 4 5 6 7 8 9 10; do curl -s -o /dev/null "http://127.0.0.1:$PORT/" && break; sleep 0.2; done export ISEP_NOTIFY_BOT_API="http://127.0.0.1:$PORT" echo "── A. 沒有憑證:講清楚缺哪兩個,不靜默 ──" OUT=$(ISEP_NOTIFY_OFFLINE=1 python3 "$N" --text "測試" 2>&1); RC=$? printf '%s' "$OUT" | grep -q 'TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID' && ok "A1 沒憑證 ⇒ 報告點名缺 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID(leo 看了知道要在 Cloud environment 加什麼)" || bad "A1 沒點名缺什麼:$OUT" [ "$RC" != 0 ] && printf '%s' "$OUT" | grep -q 'leo 的手機上沒有出現' && ok "A2 兩條路都沒通 ⇒ 離開碼非 0、明說沒送到" || bad "A2 rc=$RC:$OUT" [ ! -s "$TMP/log" ] && ok "A3 沒憑證就不打 Bot API" || bad "A3 沒憑證還打了:$(cat "$TMP/log")" echo "── B. 有憑證:先直送,送到就結束 ──" : > "$TMP/log" OUT=$(TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=777 python3 "$N" --text "雲端收工摘要" 2>&1); RC=$? [ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q 'Bot API 直送):送到了' && ok "B1 憑證在 ⇒ Bot API 直送,離開碼 0" || bad "B1 rc=$RC:$OUT" python3 - "$TMP/log" <<'PY' && ok "B2 打到 /botGOOD/sendMessage、chat_id=777、內文有署名、UA 帶了" || bad "B2 送出去的形狀不對:$(cat "$TMP/log")" import json, sys rows = [json.loads(l) for l in open(sys.argv[1])] assert len(rows) == 1, rows r = rows[0]; b = json.loads(r["body"]) assert r["path"] == "/botGOOD/sendMessage", r["path"] assert b["chat_id"] == "777" and b["text"].startswith("[總管] 雲端收工摘要"), b assert "isep-notify" in (r["ua"] or ""), r["ua"] PY printf '%s' "$OUT" | grep -q 'notify_leo' && bad "B3 直送成功後不該再碰實例那條" || ok "B3 直送成功 ⇒ 不再打實例的 notify_leo" echo "── C. 憑證錯:Bot API 回 ok=false ⇒ 不算送到,退到原本那條 ──" : > "$TMP/log" # 實例那條也指到假伺服器(它對非 /botGOOD/ 的路徑回 401)——兩條都不出這台機器 OUT=$(TELEGRAM_BOT_TOKEN=BAD TELEGRAM_CHAT_ID=777 ISEP_NOTIFY_URL="http://127.0.0.1:$PORT/webhooks/named/leo/notify_leo/trigger" python3 "$N" --text "x" 2>&1); RC=$? printf '%s' "$OUT" | grep -q 'Bot API HTTP 401' && ok "C1 401 被講出來(不是「送出成功」)" || bad "C1 沒講 401:$OUT" [ "$RC" != 0 ] && printf '%s' "$OUT" | grep -q 'leo 的手機上沒有出現' && ok "C2 直送失敗+實例關著 ⇒ 仍然明說沒送到" || bad "C2 rc=$RC:$OUT" echo "── D. 閘說會擋,直送仍走得通(它不碰實例)──" : > "$TMP/log"; mkdir -p "$TMP/oldguard" printf '#!/bin/sh\nexit 2\n' > "$TMP/oldguard/prod-write-guard.sh"; chmod +x "$TMP/oldguard/prod-write-guard.sh" OUT=$(ISEP_NOTIFY_GUARD="$TMP/oldguard/prod-write-guard.sh" TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=1 python3 "$N" --text "y" 2>&1); RC=$? [ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q '通知閘:\*\*block\*\*' && printf '%s' "$OUT" | grep -q '直送):送到了' \ && ok "D1 閘 block + 憑證在 ⇒ 直送照樣送到(閘擋的是寫實例,直送沒碰實例)" || bad "D1 rc=$RC:$OUT" echo "── E. --dry-run 什麼都不送 ──" : > "$TMP/log" OUT=$(TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=1 python3 "$N" --text "z" --dry-run 2>&1) [ ! -s "$TMP/log" ] && printf '%s' "$OUT" | grep -q 'dry-run' && ok "E1 --dry-run:Bot API 一次都沒被打" || bad "E1 dry-run 打了:$(cat "$TMP/log")" echo; echo "$PASS/$((PASS+FAIL)) 通過" [ "$FAIL" -eq 0 ]