Skip to content

Instantly share code, notes, and snippets.

@halu0915
Created April 13, 2026 05:12
Show Gist options
  • Select an option

  • Save halu0915/7233888888eea4c676f495b2bdb2c54a to your computer and use it in GitHub Desktop.

Select an option

Save halu0915/7233888888eea4c676f495b2bdb2c54a to your computer and use it in GitHub Desktop.
Claude Code Telegram Daemon — 繞過入站 bug (GitHub #37933)
#!/usr/bin/env bash
# Claude Code Telegram Daemon — 繞過壞掉的插件,直接 Bot API 輪詢 + claude -p 自動回覆
# macOS 版,適用於 Claude Code Telegram 入站 bug (GitHub #37933)
#
# 用法:
# ./claude-telegram-daemon.sh
# 或背景運行:nohup ./claude-telegram-daemon.sh > ~/claude-telegram.log 2>&1 &
#
# 需要:curl, jq, claude CLI
set -euo pipefail
# ===== 設定區 =====
BOT_TOKEN="${CLAUDE_TELEGRAM_BOT_TOKEN:-}"
ALLOWED_USERS="${CLAUDE_TELEGRAM_ALLOWED_USERS:-}"
API="https://api.telegram.org/bot${BOT_TOKEN}"
INBOX="$HOME/.claude/channels/telegram/inbox"
OFFSET_FILE="$HOME/.claude/channels/telegram/.poll_offset"
POLL_INTERVAL=3
if [ -z "$BOT_TOKEN" ]; then
echo "❌ 請設定環境變數 CLAUDE_TELEGRAM_BOT_TOKEN"
echo " export CLAUDE_TELEGRAM_BOT_TOKEN='your-bot-token'"
exit 1
fi
if [ -z "$ALLOWED_USERS" ]; then
echo "❌ 請設定環境變數 CLAUDE_TELEGRAM_ALLOWED_USERS(用戶 ID)"
echo " export CLAUDE_TELEGRAM_ALLOWED_USERS='1234567890'"
echo " 查詢方式:Telegram 搜尋 @userinfobot 取得你的 ID"
exit 1
fi
mkdir -p "$INBOX"
offset=0
[[ -f "$OFFSET_FILE" ]] && offset=$(<"$OFFSET_FILE")
log() { echo "[$(date '+%H:%M:%S')] $*"; }
cleanup() { log "停止 Telegram Daemon"; exit 0; }
trap cleanup SIGTERM SIGINT
log "🤖 Claude Telegram Daemon 啟動(offset=$offset)"
log " Bot Token: ...${BOT_TOKEN: -6}"
log " 允許用戶: $ALLOWED_USERS"
log " Inbox: $INBOX"
while true; do
response=$(curl -sf --max-time 10 \
"${API}/getUpdates?offset=${offset}&timeout=2&allowed_updates=%5B%22message%22%5D" 2>/dev/null) || {
log "⚠️ API 請求失敗,重試..."
sleep "$POLL_INTERVAL"
continue
}
count=$(echo "$response" | jq '.result | length')
for ((i = 0; i < count; i++)); do
update=$(echo "$response" | jq ".result[$i]")
update_id=$(echo "$update" | jq '.update_id')
sender_id=$(echo "$update" | jq -r '.message.from.id // empty')
# 允許清單檢查
if [[ "$sender_id" != "$ALLOWED_USERS" ]]; then
log "⏭️ 忽略來自 $sender_id 的訊息"
offset=$((update_id + 1))
echo "$offset" > "$OFFSET_FILE"
continue
fi
msg=$(echo "$update" | jq '.message')
msg_id=$(echo "$msg" | jq '.message_id')
timestamp=$(date '+%Y%m%d-%H%M%S')
text=$(echo "$msg" | jq -r '.text // .caption // empty')
sender_name=$(echo "$msg" | jq -r '.from.first_name // "User"')
# 處理照片
photo_file=$(echo "$msg" | jq -r '.photo[-1].file_id // empty')
if [[ -n "$photo_file" ]]; then
file_info=$(curl -sf "${API}/getFile?file_id=${photo_file}" 2>/dev/null)
file_path=$(echo "$file_info" | jq -r '.result.file_path // empty')
if [[ -n "$file_path" ]]; then
photo_out="${INBOX}/${timestamp}_${msg_id}.jpg"
curl -sf "https://api.telegram.org/file/bot${BOT_TOKEN}/${file_path}" \
-o "$photo_out" 2>/dev/null
log "📷 照片已存:$photo_out"
fi
fi
# 處理文件
doc_file=$(echo "$msg" | jq -r '.document.file_id // empty')
if [[ -n "$doc_file" ]]; then
doc_name=$(echo "$msg" | jq -r '.document.file_name // "document"')
file_info=$(curl -sf "${API}/getFile?file_id=${doc_file}" 2>/dev/null)
file_path=$(echo "$file_info" | jq -r '.result.file_path // empty')
if [[ -n "$file_path" ]]; then
doc_out="${INBOX}/${timestamp}_${msg_id}_${doc_name}"
curl -sf "https://api.telegram.org/file/bot${BOT_TOKEN}/${file_path}" \
-o "$doc_out" 2>/dev/null
log "📎 文件已存:$doc_out"
fi
fi
# 存文字到 inbox
if [[ -n "$text" ]]; then
echo "$text" > "${INBOX}/${timestamp}_${msg_id}.txt"
log "💬 ${sender_name}:${text:0:50}"
fi
# 用 Claude 自動回覆
if [[ -n "$text" ]]; then
# 顯示打字中
curl -sf -X POST "${API}/sendChatAction" \
-H 'Content-Type: application/json' \
-d "{\"chat_id\":${sender_id},\"action\":\"typing\"}" \
>/dev/null 2>&1 || true
# 呼叫 claude -p 取得回覆
reply=$(claude -p "你是 AI 助理,用繁體中文回覆。
用戶訊息:${text}" 2>/dev/null) || reply=""
if [[ -n "$reply" ]]; then
# 截斷到 Telegram 限制
reply="${reply:0:4096}"
curl -sf -X POST "${API}/sendMessage" \
-H 'Content-Type: application/json' \
-d "{\"chat_id\":${sender_id},\"text\":$(echo "$reply" | jq -Rs .),\"reply_to_message_id\":${msg_id}}" \
>/dev/null 2>&1 || true
log "✅ 已回覆 msg $msg_id"
else
# Claude 失敗,發送 👀 表示已收到
curl -sf -X POST "${API}/setMessageReaction" \
-H 'Content-Type: application/json' \
-d "{\"chat_id\":${sender_id},\"message_id\":${msg_id},\"reaction\":[{\"type\":\"emoji\",\"emoji\":\"👀\"}]}" \
>/dev/null 2>&1 || true
log "⚠️ Claude 回覆失敗,已標記 👀"
fi
else
# 非文字訊息(照片/文件),標記已收到
curl -sf -X POST "${API}/setMessageReaction" \
-H 'Content-Type: application/json' \
-d "{\"chat_id\":${sender_id},\"message_id\":${msg_id},\"reaction\":[{\"type\":\"emoji\",\"emoji\":\"👀\"}]}" \
>/dev/null 2>&1 || true
fi
offset=$((update_id + 1))
echo "$offset" > "$OFFSET_FILE"
done
sleep "$POLL_INTERVAL"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment