Telegram bot bridge for Claude Code. Send messages from Telegram, get responses back.
flowchart TB
subgraph Telegram["Telegram"]
TG[Telegram App]
API[Telegram Bot API]
end
subgraph Internet["Internet"]
CF[Cloudflare Tunnel]
end
subgraph Local["Local System"]
subgraph Bridge["Bridge Server (bridge.py)"]
HTTP[HTTP Handler<br/>Port 8091]
CMD[Command Processor<br/>/status /clear /resume /stop]
TMUX_CTRL[Tmux Controller<br/>send-keys injection]
end
subgraph Files["File System (~/.claude/)"]
CHAT[telegram_chat_id]
PEND[telegram_pending]
SETTINGS[settings.json<br/>Stop Hook config]
HOOK_FILE[hooks/send-to-telegram.sh]
end
subgraph Claude["Claude Code"]
CC[claude --dangerously-skip-permissions]
HOOK[Stop Hook Trigger]
TRANSCRIPT[transcript.jsonl]
end
SESS[tmux Session: claude]
end
TG --> API
API --> CF
CF --> HTTP
HTTP --> CMD
CMD --> TMUX_CTRL
TMUX_CTRL -->|"tmux send-keys"| SESS
SESS --> CC
CC -->|"Stop Hook"| HOOK
HOOK -->|"Read transcript"| TRANSCRIPT
HOOK -->|"Send Response"| API
CMD -->|"Write"| CHAT
CMD -->|"Write"| PEND
HOOK -->|"Read"| CHAT
HOOK -->|"Read/Delete"| PEND
CC -->|"Load"| SETTINGS
- Message Receive: Telegram → Cloudflare Tunnel → Bridge Server
- Message Injection: Bridge Server → tmux send-keys → Claude Code
- Response Capture: Claude Code Stop Hook → Read transcript.jsonl → Parse output
- Response Send: Stop Hook → Telegram Bot API → Telegram
# macOS
brew install tmux cloudflared
# Verify claude is installed
claude --version
# Setup Python env
uv venv && source .venv/bin/activate
uv pip install -e .git clone https://github.com/liangyimingcom/claudecode-telegram
cd claudecode-telegram- Search for @BotFather on Telegram
- Send
/newbotand follow the prompts - Get your Bot Token (looks like
123456789:ABCdefGHIjklMNOpqrsTUVwxyz)
# Set Bot Token
export TELEGRAM_BOT_TOKEN="your_token_from_botfather"
# Run startup script
./start-bridge.shThe startup script will automatically:
- Check environment variables and dependencies
- Create
~/.claude/hooksdirectory - Install Hook script and update Bot Token
- Merge Hook config into
~/.claude/settings.json(preserves existing settings) - Create tmux session and start Claude Code
- Start Cloudflare Tunnel and set Telegram Webhook
- Start Bridge Server
To stop everything:
./stop-clean-bridge.sh# Create directory
mkdir -p ~/.claude/hooks
# Copy hook script
cp hooks/send-to-telegram.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/send-to-telegram.sh
# Set your Bot Token in the hook
nano ~/.claude/hooks/send-to-telegram.sh
# Modify TELEGRAM_BOT_TOKEN="your_token_here"Or set via environment variable (recommended):
export TELEGRAM_BOT_TOKEN="your_token"Add Hook config to ~/.claude/settings.json:
{
"hooks": {
"Stop": [{"hooks": [{"type": "command", "command": "~/.claude/hooks/send-to-telegram.sh"}]}]
}
}tmux new -s claude
claude --dangerously-skip-permissionsIn another terminal:
export TELEGRAM_BOT_TOKEN="your_token"
python3 bridge.pycloudflared tunnel --url http://localhost:8091curl "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook?url=https://YOUR-TUNNEL-URL.trycloudflare.com"| Command | Description | Behavior |
|---|---|---|
/status |
Check status | Returns tmux session status |
/clear |
Clear conversation | Sends /clear command to Claude Code |
/stop |
Interrupt operation | Sends Escape key to interrupt |
/resume |
Resume session | Shows recent sessions as inline keyboard |
/continue_ |
Continue recent | Auto-continues most recent session |
/loop <prompt> |
Ralph Loop | Runs prompt with max 5 iterations |
| Variable | Default | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
required | Bot token from BotFather |
TMUX_SESSION |
claude |
tmux session name |
PORT |
8091 |
Bridge Server listen port |
SKIP_TUNNEL |
unset | Set to 1 to skip cloudflared tunnel |
WEBHOOK_URL |
unset | Manually specify Webhook URL (when SKIP_TUNNEL=1) |
.
├── bridge.py # Bridge Server
├── start-bridge.sh # One-click startup script
├── stop-clean-bridge.sh # Stop and cleanup script
├── hooks/
│ └── send-to-telegram.sh # Claude Code Stop Hook script
├── pyproject.toml # Python project config
├── README.md # English documentation
└── README-cn.md # Chinese documentation
| File | Path | Description |
|---|---|---|
| Settings | ~/.claude/settings.json |
Claude Code settings, defines Stop Hook |
| Hook Script | ~/.claude/hooks/send-to-telegram.sh |
Response sending script |
| Chat ID | ~/.claude/telegram_chat_id |
Current Telegram chat ID |
| Pending Flag | ~/.claude/telegram_pending |
Pending message timestamp |
| History | ~/.claude/history.jsonl |
Claude session history (for /resume) |
Claude Code uses ~/.claude/settings.json to configure hooks:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/send-to-telegram.sh"
}
]
}
]
}
}hooks.Stop: Defines Stop Hook, triggered when Claude Code completes a response- The hook reads
transcript_pathfrom stdin to extract the assistant's response - Response is converted from Markdown to HTML and sent to Telegram
# List sessions
tmux ls
# Attach to session
tmux attach -t claude# View pending file
cat ~/.claude/telegram_pending
# Manual cleanup (if stuck)
rm ~/.claude/telegram_pendingIf port 8091 is already in use, you'll see:
Error: Port 8091 is already in use
Please specify a different port: export PORT=<port> (current default: 8091)
Solution: Use a different port via environment variable:
PORT=9091 ./start-bridge.shOr check what's using the port and stop it:
lsof -i :8091
kill -9 <PID>| Issue | Cause | Solution |
|---|---|---|
| No response after sending message | tmux session doesn't exist | Run tmux new -s claude to start session |
| Hook not triggering | settings.json not configured | Verify Stop Hook in ~/.claude/settings.json |
| Response timeout | Pending file expired (>10 min) | Check system time, clean pending file |
| HTML format error | Markdown conversion failed | Hook falls back to plain text automatically |
| Address already in use | Port 8091 occupied | Use PORT=9091 ./start-bridge.sh |
If the pending file is older than 10 minutes, the Stop Hook will automatically delete it and skip sending the response.
If HTML format sending fails, the Hook automatically falls back to plain text format.
If the tmux session doesn't exist, the Bridge Server returns a "tmux not found" error message to Telegram.
MIT
