claude-telegram-mirror 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -0
- package/dist/bot/commands.d.ts +41 -0
- package/dist/bot/commands.d.ts.map +1 -0
- package/dist/bot/commands.js +231 -0
- package/dist/bot/commands.js.map +1 -0
- package/dist/bot/formatting.d.ts +62 -0
- package/dist/bot/formatting.d.ts.map +1 -0
- package/dist/bot/formatting.js +295 -0
- package/dist/bot/formatting.js.map +1 -0
- package/dist/bot/telegram.d.ts +93 -0
- package/dist/bot/telegram.d.ts.map +1 -0
- package/dist/bot/telegram.js +378 -0
- package/dist/bot/telegram.js.map +1 -0
- package/dist/bot/types.d.ts +28 -0
- package/dist/bot/types.d.ts.map +1 -0
- package/dist/bot/types.js +5 -0
- package/dist/bot/types.js.map +1 -0
- package/dist/bridge/daemon.d.ts +93 -0
- package/dist/bridge/daemon.d.ts.map +1 -0
- package/dist/bridge/daemon.js +626 -0
- package/dist/bridge/daemon.js.map +1 -0
- package/dist/bridge/index.d.ts +10 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +9 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/injector.d.ts +97 -0
- package/dist/bridge/injector.d.ts.map +1 -0
- package/dist/bridge/injector.js +289 -0
- package/dist/bridge/injector.js.map +1 -0
- package/dist/bridge/session.d.ts +108 -0
- package/dist/bridge/session.d.ts.map +1 -0
- package/dist/bridge/session.js +381 -0
- package/dist/bridge/session.js.map +1 -0
- package/dist/bridge/socket.d.ts +97 -0
- package/dist/bridge/socket.d.ts.map +1 -0
- package/dist/bridge/socket.js +436 -0
- package/dist/bridge/socket.js.map +1 -0
- package/dist/bridge/types.d.ts +38 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +5 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +332 -0
- package/dist/cli.js.map +1 -0
- package/dist/hooks/handler.d.ts +94 -0
- package/dist/hooks/handler.d.ts.map +1 -0
- package/dist/hooks/handler.js +431 -0
- package/dist/hooks/handler.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/installer.d.ts +46 -0
- package/dist/hooks/installer.d.ts.map +1 -0
- package/dist/hooks/installer.js +317 -0
- package/dist/hooks/installer.js.map +1 -0
- package/dist/hooks/types.d.ts +88 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +6 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/service/doctor.d.ts +10 -0
- package/dist/service/doctor.d.ts.map +1 -0
- package/dist/service/doctor.js +424 -0
- package/dist/service/doctor.js.map +1 -0
- package/dist/service/manager.d.ts +48 -0
- package/dist/service/manager.d.ts.map +1 -0
- package/dist/service/manager.js +584 -0
- package/dist/service/manager.js.map +1 -0
- package/dist/service/setup.d.ts +10 -0
- package/dist/service/setup.d.ts.map +1 -0
- package/dist/service/setup.js +266 -0
- package/dist/service/setup.js.map +1 -0
- package/dist/utils/chunker.d.ts +24 -0
- package/dist/utils/chunker.d.ts.map +1 -0
- package/dist/utils/chunker.js +123 -0
- package/dist/utils/chunker.js.map +1 -0
- package/dist/utils/config.d.ts +48 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +154 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +28 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +88 -0
- package/postinstall.cjs +76 -0
- package/scripts/claude-wrapper.sh +122 -0
- package/scripts/doctor.sh +433 -0
- package/scripts/get-chat-id.sh +64 -0
- package/scripts/global-hooks.sh +39 -0
- package/scripts/install.sh +831 -0
- package/scripts/start-daemon.sh +49 -0
- package/scripts/telegram-hook.sh +449 -0
- package/scripts/uninstall.sh +261 -0
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Claude Code Telegram Mirror
|
|
2
|
+
|
|
3
|
+
Bidirectional communication between Claude Code CLI and Telegram. Control your Claude Code sessions from your phone.
|
|
4
|
+
|
|
5
|
+
**Supported platforms:** Linux, macOS
|
|
6
|
+
|
|
7
|
+
## Quick Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -fsSL https://raw.githubusercontent.com/robertelee78/claude-telegram-mirror/master/scripts/install.sh | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or download and review first:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
curl -fsSL https://raw.githubusercontent.com/robertelee78/claude-telegram-mirror/master/scripts/install.sh -o install.sh
|
|
17
|
+
less install.sh # Review the script
|
|
18
|
+
bash install.sh
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The interactive installer will guide you through:
|
|
22
|
+
1. Checking prerequisites (node, npm, git, tmux, jq, nc)
|
|
23
|
+
2. Creating a Telegram bot via @BotFather
|
|
24
|
+
3. Setting up a supergroup with Topics
|
|
25
|
+
4. Verifying bot permissions
|
|
26
|
+
5. Installing hooks and the system service
|
|
27
|
+
|
|
28
|
+
### Other Scripts
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Diagnose issues
|
|
32
|
+
~/.local/share/claude-telegram-mirror/scripts/doctor.sh
|
|
33
|
+
|
|
34
|
+
# Uninstall completely
|
|
35
|
+
~/.local/share/claude-telegram-mirror/scripts/uninstall.sh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Manual Setup (for developers)
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary>Click to expand manual installation steps</summary>
|
|
42
|
+
|
|
43
|
+
For developers who want to work on the source code:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 1. Clone and build
|
|
47
|
+
git clone https://github.com/robertelee78/claude-telegram-mirror.git
|
|
48
|
+
cd claude-telegram-mirror && npm install && npm run build
|
|
49
|
+
|
|
50
|
+
# 2. Create a Telegram bot via @BotFather, get the token
|
|
51
|
+
|
|
52
|
+
# 3. Create a supergroup with Topics enabled, add your bot as admin
|
|
53
|
+
|
|
54
|
+
# 4. Get your chat ID
|
|
55
|
+
./scripts/get-chat-id.sh YOUR_BOT_TOKEN
|
|
56
|
+
|
|
57
|
+
# 5. Configure environment
|
|
58
|
+
cat > ~/.telegram-env << 'EOF'
|
|
59
|
+
export TELEGRAM_BOT_TOKEN="your-token-here"
|
|
60
|
+
export TELEGRAM_CHAT_ID="-100your-chat-id"
|
|
61
|
+
export TELEGRAM_MIRROR=true
|
|
62
|
+
EOF
|
|
63
|
+
|
|
64
|
+
# 6. Install hooks
|
|
65
|
+
node dist/cli.js install-hooks # Global install
|
|
66
|
+
# OR for projects with custom .claude/settings.json:
|
|
67
|
+
cd /path/to/project && node dist/cli.js install-hooks --project
|
|
68
|
+
|
|
69
|
+
# 7. Start daemon (choose one)
|
|
70
|
+
node dist/cli.js start # Foreground (for testing)
|
|
71
|
+
node dist/cli.js service install && \
|
|
72
|
+
node dist/cli.js service start # As system service (recommended)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Note:** When using the Quick Install method, use `ctm` instead of `node dist/cli.js`.
|
|
76
|
+
|
|
77
|
+
</details>
|
|
78
|
+
|
|
79
|
+
## Features
|
|
80
|
+
|
|
81
|
+
- **CLI → Telegram**: Mirror Claude's responses, tool usage, and notifications
|
|
82
|
+
- **Telegram → CLI**: Send prompts from Telegram directly to Claude Code
|
|
83
|
+
- **Session Threading**: Each Claude session gets its own Forum Topic
|
|
84
|
+
- **Multi-System Support**: Run separate daemons on multiple machines
|
|
85
|
+
- **Compaction Notifications**: Get notified when Claude summarizes context
|
|
86
|
+
|
|
87
|
+
## Architecture
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
91
|
+
│ Claude Code │────▶│ Bridge Daemon │────▶│ Telegram │
|
|
92
|
+
│ CLI │◀────│ │◀────│ Bot │
|
|
93
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
94
|
+
│ │
|
|
95
|
+
│ hooks │ Unix socket
|
|
96
|
+
▼ ▼
|
|
97
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
98
|
+
│ telegram-hook │────▶│ Socket Server │
|
|
99
|
+
│ (bash) │ │ │
|
|
100
|
+
└─────────────────┘ └─────────────────┘
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Flow:**
|
|
104
|
+
1. Claude Code hooks capture events (prompts, responses, tool use)
|
|
105
|
+
2. Hook script sends JSON to bridge daemon via Unix socket
|
|
106
|
+
3. Bridge forwards messages to Telegram Forum Topic
|
|
107
|
+
4. Telegram replies are injected into CLI via `tmux send-keys`
|
|
108
|
+
|
|
109
|
+
## Multi-System Architecture
|
|
110
|
+
|
|
111
|
+
When running Claude Code on multiple machines, each system needs its own bot to avoid Telegram API conflicts (error 409: only one polling connection per bot token is allowed).
|
|
112
|
+
|
|
113
|
+
**The model:**
|
|
114
|
+
- **One daemon per host** - Each machine runs its own bridge daemon
|
|
115
|
+
- **One bot per daemon** - Each daemon uses a unique Telegram bot
|
|
116
|
+
- **Multiple sessions per host** - One daemon handles all Claude sessions on that machine
|
|
117
|
+
- **Shared supergroup** - All bots post to the same Telegram supergroup
|
|
118
|
+
|
|
119
|
+
### Setup for Multiple Systems
|
|
120
|
+
|
|
121
|
+
1. **Create one bot per system** via [@BotFather](https://t.me/botfather)
|
|
122
|
+
- System A: `@claude_mirror_system_a_bot`
|
|
123
|
+
- System B: `@claude_mirror_system_b_bot`
|
|
124
|
+
|
|
125
|
+
2. **Add all bots to the same supergroup** with admin permissions
|
|
126
|
+
|
|
127
|
+
3. **Configure each system** with its own bot token:
|
|
128
|
+
```bash
|
|
129
|
+
# On System A (~/.telegram-env)
|
|
130
|
+
export TELEGRAM_BOT_TOKEN="token-for-system-a-bot"
|
|
131
|
+
export TELEGRAM_CHAT_ID="-100shared-group-id"
|
|
132
|
+
|
|
133
|
+
# On System B (~/.telegram-env)
|
|
134
|
+
export TELEGRAM_BOT_TOKEN="token-for-system-b-bot"
|
|
135
|
+
export TELEGRAM_CHAT_ID="-100shared-group-id" # Same group!
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
4. **Each daemon creates topics for its sessions** - Messages route correctly because each daemon only processes topics it created.
|
|
139
|
+
|
|
140
|
+
## Prerequisites
|
|
141
|
+
|
|
142
|
+
- Node.js 18+
|
|
143
|
+
- Claude Code CLI
|
|
144
|
+
- tmux (for bidirectional communication)
|
|
145
|
+
- jq (JSON processing)
|
|
146
|
+
- nc (netcat, for socket communication)
|
|
147
|
+
- Telegram account
|
|
148
|
+
|
|
149
|
+
## Telegram Setup
|
|
150
|
+
|
|
151
|
+
### 1. Create a Bot
|
|
152
|
+
|
|
153
|
+
1. Message [@BotFather](https://t.me/botfather) → `/newbot`
|
|
154
|
+
2. Choose name and username (must end in `bot`)
|
|
155
|
+
3. Save the API token
|
|
156
|
+
|
|
157
|
+
### 2. Create Supergroup with Topics
|
|
158
|
+
|
|
159
|
+
1. Create a new group in Telegram
|
|
160
|
+
2. Add your bot to the group
|
|
161
|
+
3. Group Settings → Enable **Topics**
|
|
162
|
+
|
|
163
|
+
### 3. Make Bot an Admin
|
|
164
|
+
|
|
165
|
+
1. Group Settings → Administrators → Add your bot
|
|
166
|
+
2. Enable: **Manage Topics**, **Post Messages**
|
|
167
|
+
|
|
168
|
+
### 4. Get Chat ID
|
|
169
|
+
|
|
170
|
+
1. Send any message in the group
|
|
171
|
+
2. Run the helper script:
|
|
172
|
+
```bash
|
|
173
|
+
./scripts/get-chat-id.sh YOUR_BOT_TOKEN
|
|
174
|
+
```
|
|
175
|
+
Or manually: `https://api.telegram.org/botYOUR_TOKEN/getUpdates`
|
|
176
|
+
3. Copy the chat ID (supergroups start with `-100`)
|
|
177
|
+
|
|
178
|
+
### 5. Disable Privacy Mode
|
|
179
|
+
|
|
180
|
+
1. [@BotFather](https://t.me/botfather) → `/mybots` → Select bot
|
|
181
|
+
2. Bot Settings → Group Privacy → **Turn off**
|
|
182
|
+
|
|
183
|
+
## Configuration
|
|
184
|
+
|
|
185
|
+
### Environment Variables
|
|
186
|
+
|
|
187
|
+
Create `~/.telegram-env`:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
export TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
|
191
|
+
export TELEGRAM_CHAT_ID="-1001234567890"
|
|
192
|
+
export TELEGRAM_MIRROR=true
|
|
193
|
+
# Optional:
|
|
194
|
+
# export TELEGRAM_MIRROR_VERBOSE=true
|
|
195
|
+
# export TELEGRAM_BRIDGE_SOCKET=~/.config/claude-telegram-mirror/bridge.sock
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Source in your shell profile (`~/.bashrc` or `~/.zshrc`):
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
[[ -f ~/.telegram-env ]] && source ~/.telegram-env
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Test Connection
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
ctm config --test
|
|
208
|
+
# ✅ Bot connected: @your_bot_username
|
|
209
|
+
# ✅ Test message sent to chat
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Usage
|
|
213
|
+
|
|
214
|
+
### Start the Bridge
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Foreground (for testing)
|
|
218
|
+
ctm start
|
|
219
|
+
|
|
220
|
+
# As system service (recommended for production)
|
|
221
|
+
ctm service install # Install systemd/launchd service
|
|
222
|
+
ctm service start # Start the service
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Run Claude in tmux
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
tmux new -s claude
|
|
229
|
+
claude
|
|
230
|
+
# Bridge auto-detects tmux session
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### CLI Commands
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# Daemon control
|
|
237
|
+
ctm start # Start daemon in foreground
|
|
238
|
+
ctm status # Show status
|
|
239
|
+
ctm config --test # Test connection
|
|
240
|
+
|
|
241
|
+
# Hook management
|
|
242
|
+
ctm install-hooks # Install global hooks
|
|
243
|
+
ctm install-hooks -p # Install to current project's .claude/
|
|
244
|
+
ctm uninstall-hooks # Remove hooks
|
|
245
|
+
ctm hooks # Show hook status
|
|
246
|
+
|
|
247
|
+
# Service management (systemd on Linux, launchd on macOS)
|
|
248
|
+
ctm service install # Install as system service
|
|
249
|
+
ctm service uninstall # Remove system service
|
|
250
|
+
ctm service start # Start service
|
|
251
|
+
ctm service stop # Stop service
|
|
252
|
+
ctm service restart # Restart service
|
|
253
|
+
ctm service status # Show service status
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Note:** The `ctm` command is installed to `~/.local/bin/` by the installer. If you cloned the repo manually, use `node dist/cli.js` instead.
|
|
257
|
+
|
|
258
|
+
## Project-Level Hooks
|
|
259
|
+
|
|
260
|
+
If your project has `.claude/settings.json` with custom hooks, global hooks are ignored. Install hooks to the project:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
cd /path/to/your/project
|
|
264
|
+
ctm install-hooks --project
|
|
265
|
+
# or shorthand:
|
|
266
|
+
ctm install-hooks -p
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The installer will prompt you to set up project-level hooks during installation. You can also add them later to any project.
|
|
270
|
+
|
|
271
|
+
## How Messages Flow
|
|
272
|
+
|
|
273
|
+
| Direction | Event | Display |
|
|
274
|
+
|-----------|-------|---------|
|
|
275
|
+
| CLI → Telegram | User types | 👤 User (cli): ... |
|
|
276
|
+
| CLI → Telegram | Tool starts | 🔧 Running: Bash |
|
|
277
|
+
| CLI → Telegram | Claude responds | 🤖 Claude: ... |
|
|
278
|
+
| CLI → Telegram | Session starts | New Forum Topic created |
|
|
279
|
+
| CLI → Telegram | Context compacting | ⏳ Notification sent |
|
|
280
|
+
| Telegram → CLI | User sends message | Injected via tmux |
|
|
281
|
+
|
|
282
|
+
## Technical Details
|
|
283
|
+
|
|
284
|
+
- **Session storage**: SQLite at `~/.config/claude-telegram-mirror/sessions.db`
|
|
285
|
+
- **Socket path**: `~/.config/claude-telegram-mirror/bridge.sock`
|
|
286
|
+
- **Response extraction**: Reads Claude's transcript `.jsonl` on Stop event
|
|
287
|
+
- **Deduplication**: Telegram-originated messages tracked to prevent echo
|
|
288
|
+
- **Topic routing**: Each daemon only processes topics it created (multi-bot safe)
|
|
289
|
+
- **Compaction alerts**: PreCompact hook sends notification before context summarization
|
|
290
|
+
|
|
291
|
+
## Troubleshooting
|
|
292
|
+
|
|
293
|
+
**Hooks not firing?**
|
|
294
|
+
- Check if project has local `.claude/settings.json` overriding globals
|
|
295
|
+
- Run `ctm install-hooks -p` from project directory
|
|
296
|
+
- Restart Claude Code after installing hooks
|
|
297
|
+
|
|
298
|
+
**409 Conflict error?**
|
|
299
|
+
- Only one polling connection per bot token is allowed
|
|
300
|
+
- If running multiple systems, each needs its own bot (see Multi-System Architecture)
|
|
301
|
+
- Kill duplicate daemons: `pkill -f "claude-telegram-mirror"`
|
|
302
|
+
|
|
303
|
+
**Bridge not receiving events?**
|
|
304
|
+
- Check socket: `ls -la ~/.config/claude-telegram-mirror/bridge.sock`
|
|
305
|
+
- Enable debug: `export TELEGRAM_HOOK_DEBUG=1` then retry
|
|
306
|
+
- Check debug log: `cat ~/.config/claude-telegram-mirror/hook-debug.log`
|
|
307
|
+
|
|
308
|
+
**tmux injection not working?**
|
|
309
|
+
- Verify tmux session: `tmux list-sessions`
|
|
310
|
+
- Check daemon logs for "Session tmux target stored"
|
|
311
|
+
|
|
312
|
+
**Messages going to wrong topic?**
|
|
313
|
+
- Clear session DB: `rm ~/.config/claude-telegram-mirror/sessions.db`
|
|
314
|
+
|
|
315
|
+
**Service not starting (Linux)?**
|
|
316
|
+
- Check status: `systemctl --user status claude-telegram-mirror`
|
|
317
|
+
- View logs: `journalctl --user -u claude-telegram-mirror -f`
|
|
318
|
+
- Enable linger: `loginctl enable-linger $USER`
|
|
319
|
+
|
|
320
|
+
**Service not starting (macOS)?**
|
|
321
|
+
- Check status: `launchctl list | grep claude`
|
|
322
|
+
- View logs: `cat ~/Library/Logs/claude-telegram-mirror.*.log`
|
|
323
|
+
- Check permissions: Ensure Terminal has Accessibility access
|
|
324
|
+
|
|
325
|
+
## License
|
|
326
|
+
|
|
327
|
+
MIT
|
|
328
|
+
|
|
329
|
+
## Credits
|
|
330
|
+
|
|
331
|
+
Built as part of the claude-mobile project for remote Claude Code interaction.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot Command Handlers
|
|
3
|
+
* Implements all Telegram bot commands
|
|
4
|
+
*/
|
|
5
|
+
import { Bot, Context, SessionFlavor, InlineKeyboard } from 'grammy';
|
|
6
|
+
interface SessionData {
|
|
7
|
+
attachedSessionId: string | null;
|
|
8
|
+
muted: boolean;
|
|
9
|
+
lastActivity: number;
|
|
10
|
+
}
|
|
11
|
+
type BotContext = Context & SessionFlavor<SessionData>;
|
|
12
|
+
export interface BridgeCallbacks {
|
|
13
|
+
getActiveSessions(): Promise<Array<{
|
|
14
|
+
id: string;
|
|
15
|
+
startedAt: Date;
|
|
16
|
+
projectDir?: string;
|
|
17
|
+
}>>;
|
|
18
|
+
abortSession(sessionId: string): Promise<boolean>;
|
|
19
|
+
sendToSession(sessionId: string, text: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Register all bot commands
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerCommands(bot: Bot<BotContext>, bridge?: BridgeCallbacks): void;
|
|
25
|
+
/**
|
|
26
|
+
* Create approval keyboard
|
|
27
|
+
*/
|
|
28
|
+
export declare function createApprovalKeyboard(approvalId: string): InlineKeyboard;
|
|
29
|
+
/**
|
|
30
|
+
* Register approval handlers
|
|
31
|
+
*/
|
|
32
|
+
export declare function registerApprovalHandlers(bot: Bot<BotContext>, onApproval: (approvalId: string, action: 'approve' | 'reject' | 'abort') => Promise<void>): void;
|
|
33
|
+
/**
|
|
34
|
+
* Register tool details handler
|
|
35
|
+
*/
|
|
36
|
+
export declare function registerToolDetailsHandler(bot: Bot<BotContext>, getToolDetails: (toolUseId: string) => {
|
|
37
|
+
tool: string;
|
|
38
|
+
input: unknown;
|
|
39
|
+
} | undefined): void;
|
|
40
|
+
export default registerCommands;
|
|
41
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/bot/commands.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AASrE,UAAU,WAAW;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;AAGvD,MAAM,WAAW,eAAe;IAC9B,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC1F,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,MAAM,CAAC,EAAE,eAAe,GACvB,IAAI,CA2NN;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAMzE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GACxF,IAAI,CA4BN;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAClF,IAAI,CA+BN;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot Command Handlers
|
|
3
|
+
* Implements all Telegram bot commands
|
|
4
|
+
*/
|
|
5
|
+
import { InlineKeyboard } from 'grammy';
|
|
6
|
+
import { formatHelp, formatStatus, formatToolDetails } from './formatting.js';
|
|
7
|
+
import logger from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Register all bot commands
|
|
10
|
+
*/
|
|
11
|
+
export function registerCommands(bot, bridge) {
|
|
12
|
+
// /start - Welcome message
|
|
13
|
+
bot.command('start', async (ctx) => {
|
|
14
|
+
await ctx.reply('👋 *Claude Code Mirror Bot*\n\n' +
|
|
15
|
+
'I mirror your Claude Code sessions to Telegram, allowing you to:\n' +
|
|
16
|
+
'• Monitor agent progress from your phone\n' +
|
|
17
|
+
'• Send responses and commands remotely\n' +
|
|
18
|
+
'• Approve/reject actions via buttons\n\n' +
|
|
19
|
+
'Use /help to see all available commands.', { parse_mode: 'Markdown' });
|
|
20
|
+
logger.info('User started bot');
|
|
21
|
+
});
|
|
22
|
+
// /help - Show commands
|
|
23
|
+
bot.command('help', async (ctx) => {
|
|
24
|
+
await ctx.reply(formatHelp(), { parse_mode: 'Markdown' });
|
|
25
|
+
});
|
|
26
|
+
// /status - Current session status
|
|
27
|
+
bot.command('status', async (ctx) => {
|
|
28
|
+
const session = ctx.session;
|
|
29
|
+
const hasSession = !!session?.attachedSessionId;
|
|
30
|
+
await ctx.reply(formatStatus(hasSession, session?.attachedSessionId || undefined, session?.muted), { parse_mode: 'Markdown' });
|
|
31
|
+
});
|
|
32
|
+
// /sessions - List active sessions
|
|
33
|
+
bot.command('sessions', async (ctx) => {
|
|
34
|
+
if (!bridge) {
|
|
35
|
+
await ctx.reply('⚠️ Bridge not connected. No session info available.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const sessions = await bridge.getActiveSessions();
|
|
40
|
+
if (sessions.length === 0) {
|
|
41
|
+
await ctx.reply('📭 No active sessions.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let message = '📋 *Active Sessions:*\n\n';
|
|
45
|
+
sessions.forEach((s, idx) => {
|
|
46
|
+
const age = Math.floor((Date.now() - s.startedAt.getTime()) / 60000);
|
|
47
|
+
message += `${idx + 1}. \`${s.id}\`\n`;
|
|
48
|
+
message += ` Started: ${age}m ago\n`;
|
|
49
|
+
if (s.projectDir) {
|
|
50
|
+
message += ` Project: \`${s.projectDir}\`\n`;
|
|
51
|
+
}
|
|
52
|
+
message += '\n';
|
|
53
|
+
});
|
|
54
|
+
message += '_Use /attach <id> to attach to a session_';
|
|
55
|
+
await ctx.reply(message, { parse_mode: 'Markdown' });
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
logger.error('Failed to get sessions', { error });
|
|
59
|
+
await ctx.reply('❌ Failed to fetch sessions.');
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// /attach <id> - Attach to session
|
|
63
|
+
bot.command('attach', async (ctx) => {
|
|
64
|
+
const sessionId = ctx.match?.trim();
|
|
65
|
+
if (!sessionId) {
|
|
66
|
+
await ctx.reply('⚠️ Please provide a session ID.\n\n' +
|
|
67
|
+
'Usage: `/attach <session-id>`\n\n' +
|
|
68
|
+
'Use /sessions to see available sessions.', { parse_mode: 'Markdown' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
ctx.session.attachedSessionId = sessionId;
|
|
72
|
+
ctx.session.muted = false;
|
|
73
|
+
await ctx.reply(`✅ Attached to session \`${sessionId}\`\n\n` +
|
|
74
|
+
'You will now receive updates from this session.\n' +
|
|
75
|
+
'Reply with text to send input.', { parse_mode: 'Markdown' });
|
|
76
|
+
logger.info('Attached to session', { sessionId });
|
|
77
|
+
});
|
|
78
|
+
// /detach - Detach from session
|
|
79
|
+
bot.command('detach', async (ctx) => {
|
|
80
|
+
const currentSession = ctx.session.attachedSessionId;
|
|
81
|
+
if (!currentSession) {
|
|
82
|
+
await ctx.reply('ℹ️ You are not attached to any session.');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
ctx.session.attachedSessionId = null;
|
|
86
|
+
await ctx.reply(`🔌 Detached from session \`${currentSession}\`\n\n` +
|
|
87
|
+
'You will no longer receive updates.', { parse_mode: 'Markdown' });
|
|
88
|
+
logger.info('Detached from session', { sessionId: currentSession });
|
|
89
|
+
});
|
|
90
|
+
// /mute - Mute notifications
|
|
91
|
+
bot.command('mute', async (ctx) => {
|
|
92
|
+
if (ctx.session.muted) {
|
|
93
|
+
await ctx.reply('🔇 Notifications already muted.');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
ctx.session.muted = true;
|
|
97
|
+
await ctx.reply('🔇 Notifications muted.\n\n' +
|
|
98
|
+
'Use /unmute to resume.', { parse_mode: 'Markdown' });
|
|
99
|
+
logger.info('Notifications muted');
|
|
100
|
+
});
|
|
101
|
+
// /unmute - Resume notifications
|
|
102
|
+
bot.command('unmute', async (ctx) => {
|
|
103
|
+
if (!ctx.session.muted) {
|
|
104
|
+
await ctx.reply('🔔 Notifications already active.');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
ctx.session.muted = false;
|
|
108
|
+
await ctx.reply('🔔 Notifications resumed.');
|
|
109
|
+
logger.info('Notifications unmuted');
|
|
110
|
+
});
|
|
111
|
+
// /abort - Abort current session
|
|
112
|
+
bot.command('abort', async (ctx) => {
|
|
113
|
+
const sessionId = ctx.session.attachedSessionId;
|
|
114
|
+
if (!sessionId) {
|
|
115
|
+
await ctx.reply('⚠️ No session attached. Use /attach first.');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Show confirmation
|
|
119
|
+
const keyboard = new InlineKeyboard()
|
|
120
|
+
.text('🛑 Yes, abort', `confirm_abort:${sessionId}`)
|
|
121
|
+
.text('❌ Cancel', 'cancel_abort');
|
|
122
|
+
await ctx.reply(`⚠️ *Abort Session?*\n\n` +
|
|
123
|
+
`This will terminate session \`${sessionId}\`.\n\n` +
|
|
124
|
+
'Are you sure?', {
|
|
125
|
+
parse_mode: 'Markdown',
|
|
126
|
+
reply_markup: keyboard
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
// Handle abort confirmation
|
|
130
|
+
bot.callbackQuery(/^confirm_abort:(.+)$/, async (ctx) => {
|
|
131
|
+
const sessionId = ctx.match[1];
|
|
132
|
+
if (bridge) {
|
|
133
|
+
try {
|
|
134
|
+
const success = await bridge.abortSession(sessionId);
|
|
135
|
+
if (success) {
|
|
136
|
+
ctx.session.attachedSessionId = null;
|
|
137
|
+
await ctx.editMessageText(`🛑 Session \`${sessionId}\` aborted.`, { parse_mode: 'Markdown' });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await ctx.editMessageText('❌ Failed to abort session.');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
logger.error('Failed to abort session', { sessionId, error });
|
|
145
|
+
await ctx.editMessageText('❌ Error aborting session.');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
ctx.session.attachedSessionId = null;
|
|
150
|
+
await ctx.editMessageText(`🛑 Detached from session \`${sessionId}\`.\n\n` +
|
|
151
|
+
'_(Bridge not connected - session may still be running)_', { parse_mode: 'Markdown' });
|
|
152
|
+
}
|
|
153
|
+
await ctx.answerCallbackQuery();
|
|
154
|
+
});
|
|
155
|
+
// Handle abort cancellation
|
|
156
|
+
bot.callbackQuery('cancel_abort', async (ctx) => {
|
|
157
|
+
await ctx.editMessageText('✅ Abort cancelled.');
|
|
158
|
+
await ctx.answerCallbackQuery();
|
|
159
|
+
});
|
|
160
|
+
// /ping - Simple health check
|
|
161
|
+
bot.command('ping', async (ctx) => {
|
|
162
|
+
const start = Date.now();
|
|
163
|
+
const msg = await ctx.reply('🏓 Pong!');
|
|
164
|
+
const latency = Date.now() - start;
|
|
165
|
+
await ctx.api.editMessageText(ctx.chat.id, msg.message_id, `🏓 Pong! _${latency}ms_`, { parse_mode: 'Markdown' });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create approval keyboard
|
|
170
|
+
*/
|
|
171
|
+
export function createApprovalKeyboard(approvalId) {
|
|
172
|
+
return new InlineKeyboard()
|
|
173
|
+
.text('✅ Approve', `approve:${approvalId}`)
|
|
174
|
+
.text('❌ Reject', `reject:${approvalId}`)
|
|
175
|
+
.row()
|
|
176
|
+
.text('🛑 Abort Session', `abort:${approvalId}`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Register approval handlers
|
|
180
|
+
*/
|
|
181
|
+
export function registerApprovalHandlers(bot, onApproval) {
|
|
182
|
+
bot.callbackQuery(/^(approve|reject|abort):(.+)$/, async (ctx) => {
|
|
183
|
+
const action = ctx.match[1];
|
|
184
|
+
const approvalId = ctx.match[2];
|
|
185
|
+
try {
|
|
186
|
+
await onApproval(approvalId, action);
|
|
187
|
+
// Update message to show decision
|
|
188
|
+
const actionText = {
|
|
189
|
+
approve: '✅ Approved',
|
|
190
|
+
reject: '❌ Rejected',
|
|
191
|
+
abort: '🛑 Session Aborted'
|
|
192
|
+
}[action];
|
|
193
|
+
const originalText = ctx.callbackQuery.message?.text || '';
|
|
194
|
+
await ctx.editMessageText(`${originalText}\n\n*Decision:* ${actionText}`, { parse_mode: 'Markdown' });
|
|
195
|
+
await ctx.answerCallbackQuery({ text: `${actionText}!` });
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
logger.error('Failed to process approval', { approvalId, action, error });
|
|
199
|
+
await ctx.answerCallbackQuery({ text: 'Error processing response' });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Register tool details handler
|
|
205
|
+
*/
|
|
206
|
+
export function registerToolDetailsHandler(bot, getToolDetails) {
|
|
207
|
+
bot.callbackQuery(/^tooldetails:(.+)$/, async (ctx) => {
|
|
208
|
+
const toolUseId = ctx.match[1];
|
|
209
|
+
try {
|
|
210
|
+
const details = getToolDetails(toolUseId);
|
|
211
|
+
if (!details) {
|
|
212
|
+
await ctx.answerCallbackQuery({ text: 'Details expired (5 min cache)', show_alert: true });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Format the tool details nicely for mobile
|
|
216
|
+
const formattedDetails = formatToolDetails(details.tool, details.input);
|
|
217
|
+
// Reply with full details (don't edit original - keep it clean)
|
|
218
|
+
await ctx.reply(formattedDetails, {
|
|
219
|
+
parse_mode: 'Markdown',
|
|
220
|
+
reply_parameters: { message_id: ctx.callbackQuery.message?.message_id || 0 }
|
|
221
|
+
});
|
|
222
|
+
await ctx.answerCallbackQuery();
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger.error('Failed to show tool details', { toolUseId, error });
|
|
226
|
+
await ctx.answerCallbackQuery({ text: 'Error loading details' });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
export default registerCommands;
|
|
231
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/bot/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Formatting Utilities
|
|
3
|
+
* Formats Claude Code output for Telegram display
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Strip ANSI escape codes from text
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripAnsi(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Escape special characters for MarkdownV2
|
|
11
|
+
* Note: Don't escape inside code blocks
|
|
12
|
+
*/
|
|
13
|
+
export declare function escapeMarkdownV2(text: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Format agent response for Telegram
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatAgentResponse(content: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format tool execution for Telegram
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatToolExecution(tool: string, input: unknown, output: unknown, verbose?: boolean): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format approval request for Telegram
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatApprovalRequest(prompt: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Format error message for Telegram
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatError(error: Error | string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Format session start notification
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatSessionStart(sessionId: string, projectDir?: string, hostname?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Format session end notification
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatSessionEnd(sessionId: string, duration?: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Format status message
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatStatus(isActive: boolean, sessionId?: string, muted?: boolean): string;
|
|
42
|
+
/**
|
|
43
|
+
* Format help message
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatHelp(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Format a message and chunk if necessary
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatAndChunk(content: string, maxLength?: number): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Detect code language from content (best effort)
|
|
52
|
+
*/
|
|
53
|
+
export declare function detectLanguage(content: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Wrap content in code block with language detection
|
|
56
|
+
*/
|
|
57
|
+
export declare function wrapInCodeBlock(content: string, language?: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Format tool details for mobile-friendly Telegram display
|
|
60
|
+
*/
|
|
61
|
+
export declare function formatToolDetails(tool: string, input: unknown): string;
|
|
62
|
+
//# sourceMappingURL=formatting.d.ts.map
|