claude-telegram 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 +283 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +140 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/activity.d.ts +17 -0
- package/dist/src/activity.d.ts.map +1 -0
- package/dist/src/activity.js +91 -0
- package/dist/src/activity.js.map +1 -0
- package/dist/src/bot.d.ts +16 -0
- package/dist/src/bot.d.ts.map +1 -0
- package/dist/src/bot.js +385 -0
- package/dist/src/bot.js.map +1 -0
- package/dist/src/claude.d.ts +18 -0
- package/dist/src/claude.d.ts.map +1 -0
- package/dist/src/claude.js +195 -0
- package/dist/src/claude.js.map +1 -0
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +89 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/modules.d.ts +60 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +77 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/sender.d.ts +7 -0
- package/dist/src/sender.d.ts.map +1 -0
- package/dist/src/sender.js +89 -0
- package/dist/src/sender.js.map +1 -0
- package/dist/src/session.d.ts +25 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +64 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/shutdown.d.ts +22 -0
- package/dist/src/shutdown.d.ts.map +1 -0
- package/dist/src/shutdown.js +80 -0
- package/dist/src/shutdown.js.map +1 -0
- package/dist/src/types.d.ts +59 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# claude-telegram
|
|
2
|
+
|
|
3
|
+
A simple and modular Telegram orchestrator on top of Claude Code CLI.
|
|
4
|
+
|
|
5
|
+
One npm package that connects a Telegram bot to Claude Code via `--resume` sessions, with whitelist access control and live activity status.
|
|
6
|
+
|
|
7
|
+
## claude-telegram vs OpenClaw
|
|
8
|
+
|
|
9
|
+
| | claude-telegram | [OpenClaw](https://github.com/openclaw/openclaw) |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Core | ~1500 LOC, one dependency (grammY) | 50+ integrations, large codebase |
|
|
12
|
+
| Approach | Minimal orchestrator — Claude Code does the work | Full-featured AI assistant platform |
|
|
13
|
+
| Extensibility | Module system — add anything you need | Built-in, growing feature set |
|
|
14
|
+
| Control | You own the code, easy to audit and modify | Community-driven, fast-moving |
|
|
15
|
+
| Setup | `npx claude-telegram start` | Multi-step setup |
|
|
16
|
+
|
|
17
|
+
> Both are valid choices. claude-telegram is for those who prefer a small, predictable core that they extend themselves.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
**1. Get a server** — a VPS, a home PC, anything with internet access.
|
|
22
|
+
|
|
23
|
+
**2. Install Claude Code:**
|
|
24
|
+
```bash
|
|
25
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**3. Give Claude the link and ask it to install:**
|
|
29
|
+
```bash
|
|
30
|
+
claude "install claude-telegram from github.com/bluzir/claude-telegram and set it up for my Telegram bot"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
mkdir my-agent && cd my-agent
|
|
37
|
+
echo "You are a helpful assistant." > CLAUDE.md
|
|
38
|
+
|
|
39
|
+
cat > claude-telegram.yaml << 'EOF'
|
|
40
|
+
token: ${MY_BOT_TOKEN}
|
|
41
|
+
workspace: .
|
|
42
|
+
whitelist: [YOUR_USER_ID]
|
|
43
|
+
permission_mode: acceptEdits
|
|
44
|
+
EOF
|
|
45
|
+
|
|
46
|
+
export MY_BOT_TOKEN="123456:ABC-DEF..."
|
|
47
|
+
npx claude-telegram start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Don't know your Telegram user ID? Run `npx claude-telegram whoami` and send a message to the bot.
|
|
51
|
+
|
|
52
|
+
## Config
|
|
53
|
+
|
|
54
|
+
Create `claude-telegram.yaml` in your project root:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
token: ${TELEGRAM_BOT_TOKEN} # env var interpolation
|
|
58
|
+
workspace: /path/to/workspace # cwd for Claude CLI
|
|
59
|
+
|
|
60
|
+
# Who can use the bot (Telegram user IDs)
|
|
61
|
+
# Empty list = NO ONE (secure by default)
|
|
62
|
+
whitelist:
|
|
63
|
+
- 16643982
|
|
64
|
+
|
|
65
|
+
# What Claude can do
|
|
66
|
+
# default | acceptEdits | bypassPermissions
|
|
67
|
+
permission_mode: acceptEdits
|
|
68
|
+
|
|
69
|
+
# --- Optional ---
|
|
70
|
+
# claude_path: /usr/local/bin/claude # default: "claude" from PATH
|
|
71
|
+
# timeout: 300 # seconds, default: 300
|
|
72
|
+
# model: sonnet # model override
|
|
73
|
+
# system_prompt: "Reply in Russian" # injected into every call
|
|
74
|
+
# add_dirs: # additional dirs for Claude
|
|
75
|
+
# - /path/to/shared/data
|
|
76
|
+
|
|
77
|
+
# modules: # optional plugin modules (loaded at startup)
|
|
78
|
+
# - import: ./modules/voice.mjs # resolved relative to `workspace`
|
|
79
|
+
# options:
|
|
80
|
+
# provider: openai
|
|
81
|
+
# - import: claude-telegram-whoop-module
|
|
82
|
+
# options:
|
|
83
|
+
# client_id: ${WHOOP_CLIENT_ID}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Environment variables are interpolated with `${VAR_NAME}` syntax.
|
|
87
|
+
|
|
88
|
+
## Workspace
|
|
89
|
+
|
|
90
|
+
The `workspace` field in config is just the working directory (`cwd`) for Claude CLI. It can be any directory — claude-telegram doesn't care what's inside. Claude Code will use whatever `CLAUDE.md`, `.claude/agents/`, `.claude/skills/`, and other project files it finds there, exactly as it would in the terminal.
|
|
91
|
+
|
|
92
|
+
### Simple setup
|
|
93
|
+
|
|
94
|
+
The simplest workspace is a directory with just a `CLAUDE.md`:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
my-agent/
|
|
98
|
+
├── CLAUDE.md # Instructions for Claude
|
|
99
|
+
├── claude-telegram.yaml # Bot config
|
|
100
|
+
└── data/ # Runtime data (auto-created)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Using an existing project
|
|
104
|
+
|
|
105
|
+
Any project that works with Claude Code works with claude-telegram — just point `workspace` at it:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Clone any project that has CLAUDE.md / .claude/ setup
|
|
109
|
+
git clone https://github.com/bluzir/claude-pipe
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
# claude-telegram.yaml
|
|
114
|
+
token: ${MY_BOT_TOKEN}
|
|
115
|
+
workspace: ./claude-pipe/examples/research-pipeline
|
|
116
|
+
whitelist: [YOUR_USER_ID]
|
|
117
|
+
permission_mode: acceptEdits
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Claude will see the project's `CLAUDE.md`, agents, skills, commands, MCP servers — everything. You don't need to copy or restructure anything.
|
|
121
|
+
|
|
122
|
+
### Multi-agent setup
|
|
123
|
+
|
|
124
|
+
Run multiple bots, each pointing to its own project:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# researcher.yaml
|
|
128
|
+
token: ${RESEARCHER_BOT_TOKEN}
|
|
129
|
+
workspace: ./research-pipeline
|
|
130
|
+
whitelist: [YOUR_USER_ID]
|
|
131
|
+
permission_mode: acceptEdits
|
|
132
|
+
timeout: 600
|
|
133
|
+
system_prompt: "You are a research agent. Be thorough and cite sources."
|
|
134
|
+
add_dirs:
|
|
135
|
+
- ./shared
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
# assistant.yaml
|
|
140
|
+
token: ${ASSISTANT_BOT_TOKEN}
|
|
141
|
+
workspace: ./assistant
|
|
142
|
+
whitelist: [YOUR_USER_ID]
|
|
143
|
+
permission_mode: acceptEdits
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx claude-telegram start --config researcher.yaml
|
|
148
|
+
npx claude-telegram start --config assistant.yaml
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Each bot gets its own workspace, sessions, and Telegram token. They share nothing unless you explicitly use `add_dirs`.
|
|
152
|
+
|
|
153
|
+
## Modules
|
|
154
|
+
|
|
155
|
+
You can extend the bot without bloating the core by adding optional modules that register extra handlers (voice/video, API integrations, etc.).
|
|
156
|
+
|
|
157
|
+
Module import rules:
|
|
158
|
+
- Relative paths are resolved against `workspace`
|
|
159
|
+
- Anything else is treated as a package specifier and resolved by Node
|
|
160
|
+
- Modules must export a default object or a default factory function
|
|
161
|
+
|
|
162
|
+
Minimal module example (`{workspace}/modules/hello.mjs`):
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
export default function createModule() {
|
|
166
|
+
return {
|
|
167
|
+
name: "hello",
|
|
168
|
+
commands: [{ command: "/hi", description: "Say hi" }],
|
|
169
|
+
register({ bot, dispatchToClaude }) {
|
|
170
|
+
bot.command("hi", async (ctx) => {
|
|
171
|
+
await dispatchToClaude(ctx, "Say hi in one sentence.");
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Hooks (memory, security, post-processing)
|
|
179
|
+
|
|
180
|
+
Modules can also hook into the request pipeline:
|
|
181
|
+
|
|
182
|
+
- `beforeClaude(ctx, message)` — deny or transform the user's message before it is sent to Claude
|
|
183
|
+
- `afterClaude(ctx, result)` — observe/transform Claude result before it is sent back to Telegram
|
|
184
|
+
|
|
185
|
+
Security example (deny messages containing a secret keyword):
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
export default function createModule() {
|
|
189
|
+
return {
|
|
190
|
+
name: "security",
|
|
191
|
+
async beforeClaude(ctx, message) {
|
|
192
|
+
if (message.includes("OPENAI_API_KEY")) {
|
|
193
|
+
return { action: "deny", reply: "Denied: looks like a secret." };
|
|
194
|
+
}
|
|
195
|
+
return { action: "continue" };
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Memory-ish example (prepend extra context):
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
export default function createModule() {
|
|
205
|
+
return {
|
|
206
|
+
name: "memory",
|
|
207
|
+
async beforeClaude(ctx, message) {
|
|
208
|
+
const extraContext = "Context: you are talking to the same user as before.";
|
|
209
|
+
return { action: "continue", message: `${extraContext}\n\n${message}` };
|
|
210
|
+
},
|
|
211
|
+
async afterClaude(ctx, result) {
|
|
212
|
+
// Store result.output somewhere if you want (file/db/vector store).
|
|
213
|
+
return result;
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## CLI
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npx claude-telegram start # uses claude-telegram.yaml in CWD
|
|
223
|
+
npx claude-telegram start --config ./my.yaml # custom config path
|
|
224
|
+
npx claude-telegram check # validate config + claude CLI
|
|
225
|
+
npx claude-telegram whoami # get your Telegram user ID
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Commands
|
|
229
|
+
|
|
230
|
+
| Command | Description |
|
|
231
|
+
|---------|-------------|
|
|
232
|
+
| `/start` | Welcome message |
|
|
233
|
+
| `/cancel` | Stop current request |
|
|
234
|
+
| `/clear` | Reset conversation (new session) |
|
|
235
|
+
| `/help` | Show available commands |
|
|
236
|
+
|
|
237
|
+
## How It Works
|
|
238
|
+
|
|
239
|
+
- Each user gets a persistent Claude session via `--resume <sessionId>`
|
|
240
|
+
- Sessions survive bot restarts (stored in `~/.claude/` by Claude CLI)
|
|
241
|
+
- Session mapping stored in `{workspace}/data/.claude-telegram/sessions.json`
|
|
242
|
+
- Bot responds in private chats only (ignores group/supergroup/channel)
|
|
243
|
+
- One message at a time per user (concurrent messages get "Still working..." reply)
|
|
244
|
+
- Live activity status shows what Claude is doing (reading, editing, searching, etc.)
|
|
245
|
+
- Messages are split at 3800 chars and formatted as Telegram MarkdownV2
|
|
246
|
+
|
|
247
|
+
## Programmatic API
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { createBot, loadConfig } from "claude-telegram";
|
|
251
|
+
|
|
252
|
+
// From config file
|
|
253
|
+
const config = loadConfig("./claude-telegram.yaml");
|
|
254
|
+
|
|
255
|
+
// Or build config directly
|
|
256
|
+
const bot = createBot({
|
|
257
|
+
token: process.env.BOT_TOKEN!,
|
|
258
|
+
workspace: "/path/to/workspace",
|
|
259
|
+
whitelist: [16643982],
|
|
260
|
+
permissionMode: "acceptEdits",
|
|
261
|
+
claudePath: "claude",
|
|
262
|
+
timeout: 300,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await bot.start();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## What's NOT Included
|
|
269
|
+
|
|
270
|
+
This is intentionally minimal. Not included:
|
|
271
|
+
|
|
272
|
+
- Voice/photo messages
|
|
273
|
+
- Multi-bot routing or gateway
|
|
274
|
+
- Approval system (use Claude CLI's `--permission-mode`)
|
|
275
|
+
- Budget tracking
|
|
276
|
+
- Web dashboard
|
|
277
|
+
- Queue system (one message at a time, extras are rejected)
|
|
278
|
+
|
|
279
|
+
Any of these can be added as a [module](#modules) without touching the core.
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":""}
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { loadConfig } from "../src/config.js";
|
|
4
|
+
import { startBot } from "../src/bot.js";
|
|
5
|
+
import { Bot } from "grammy";
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const command = args[0];
|
|
8
|
+
function getConfigPath() {
|
|
9
|
+
const configIdx = args.indexOf("--config");
|
|
10
|
+
if (configIdx !== -1 && args[configIdx + 1]) {
|
|
11
|
+
return args[configIdx + 1];
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
async function cmdStart() {
|
|
16
|
+
const config = loadConfig(getConfigPath());
|
|
17
|
+
await startBot(config);
|
|
18
|
+
}
|
|
19
|
+
function cmdCheck() {
|
|
20
|
+
console.log("[check] Validating config...");
|
|
21
|
+
let config;
|
|
22
|
+
try {
|
|
23
|
+
config = loadConfig(getConfigPath());
|
|
24
|
+
console.log(` ✓ Config loaded`);
|
|
25
|
+
console.log(` ✓ Workspace: ${config.workspace}`);
|
|
26
|
+
console.log(` ✓ Whitelist: ${config.whitelist.length} user(s)`);
|
|
27
|
+
console.log(` ✓ Permission mode: ${config.permissionMode}`);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31
|
+
console.error(` ✗ Config error: ${msg}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Check Claude CLI
|
|
35
|
+
try {
|
|
36
|
+
const version = execFileSync(config.claudePath, ["--version"], {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
}).trim();
|
|
39
|
+
console.log(` ✓ Claude CLI: ${version}`);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
console.error(` ✗ Claude CLI not found or not executable: ${config.claudePath}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
// Sanity-check required flags used by this package (no API calls).
|
|
46
|
+
try {
|
|
47
|
+
const help = execFileSync(config.claudePath, ["--help"], {
|
|
48
|
+
encoding: "utf-8",
|
|
49
|
+
});
|
|
50
|
+
const required = [
|
|
51
|
+
"--output-format",
|
|
52
|
+
"stream-json",
|
|
53
|
+
"--permission-mode",
|
|
54
|
+
"--resume",
|
|
55
|
+
"--session-id",
|
|
56
|
+
];
|
|
57
|
+
const missing = required.filter((s) => !help.includes(s));
|
|
58
|
+
if (missing.length > 0) {
|
|
59
|
+
console.error(` ✗ Claude CLI is missing required flags: ${missing.join(", ")}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
console.log(" ✓ Claude CLI flags look compatible");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
console.error(" ✗ Failed to validate Claude CLI help output");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
console.log("\nAll checks passed.");
|
|
69
|
+
}
|
|
70
|
+
async function cmdWhoami() {
|
|
71
|
+
// Determine token: from --config or env
|
|
72
|
+
let token;
|
|
73
|
+
try {
|
|
74
|
+
const config = loadConfig(getConfigPath());
|
|
75
|
+
token = config.token;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// If no config, try env directly
|
|
79
|
+
token = process.env.TELEGRAM_BOT_TOKEN;
|
|
80
|
+
}
|
|
81
|
+
if (!token) {
|
|
82
|
+
console.error("No bot token found. Provide a config file or set TELEGRAM_BOT_TOKEN.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const bot = new Bot(token);
|
|
86
|
+
bot.on("message", async (ctx) => {
|
|
87
|
+
if (ctx.chat?.type !== "private") {
|
|
88
|
+
try {
|
|
89
|
+
await ctx.reply("Please message me in a private chat.");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Ignore
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const userId = ctx.from?.id;
|
|
97
|
+
const username = ctx.from?.username || "(no username)";
|
|
98
|
+
const name = [ctx.from?.first_name, ctx.from?.last_name].filter(Boolean).join(" ") ||
|
|
99
|
+
"(no name)";
|
|
100
|
+
await ctx.reply(`Your Telegram info:\n\n` +
|
|
101
|
+
`User ID: ${userId}\n` +
|
|
102
|
+
`Username: @${username}\n` +
|
|
103
|
+
`Name: ${name}\n\n` +
|
|
104
|
+
`Add ${userId} to your whitelist config.`);
|
|
105
|
+
});
|
|
106
|
+
console.log("[whoami] Bot started. Send any message to get your user ID.");
|
|
107
|
+
console.log("[whoami] Press Ctrl+C to stop.\n");
|
|
108
|
+
await bot.start();
|
|
109
|
+
}
|
|
110
|
+
// --- Main ---
|
|
111
|
+
switch (command) {
|
|
112
|
+
case "start":
|
|
113
|
+
cmdStart().catch((err) => {
|
|
114
|
+
console.error("Fatal:", err instanceof Error ? err.message : err);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
117
|
+
break;
|
|
118
|
+
case "check":
|
|
119
|
+
cmdCheck();
|
|
120
|
+
break;
|
|
121
|
+
case "whoami":
|
|
122
|
+
cmdWhoami().catch((err) => {
|
|
123
|
+
console.error("Fatal:", err instanceof Error ? err.message : err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
console.log(`claude-telegram — Telegram bot for Claude Code CLI
|
|
129
|
+
|
|
130
|
+
Usage:
|
|
131
|
+
claude-telegram start [--config path] Start the bot
|
|
132
|
+
claude-telegram check [--config path] Validate config & dependencies
|
|
133
|
+
claude-telegram whoami Get your Telegram user ID
|
|
134
|
+
`);
|
|
135
|
+
if (command && command !== "help" && command !== "--help") {
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Bot } from "grammy";
|
|
2
|
+
import type { StreamJsonEvent } from "./types.js";
|
|
3
|
+
interface ActivityStatusOptions {
|
|
4
|
+
api: Bot["api"];
|
|
5
|
+
chatId: number;
|
|
6
|
+
messageId: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create an activity status updater that edits a Telegram message
|
|
10
|
+
* with current Claude activity and elapsed time.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createActivityStatus(options: ActivityStatusOptions): {
|
|
13
|
+
onEvent: (event: StreamJsonEvent) => void;
|
|
14
|
+
stop: () => void;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,YAAY,CAAC;AAsD/D,UAAU,qBAAqB;IAC7B,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB;qBA0BzC,eAAe;;EAkBxC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const MIN_UPDATE_INTERVAL_MS = 3_000;
|
|
2
|
+
const TOOL_LABELS = {
|
|
3
|
+
Read: "reading",
|
|
4
|
+
Edit: "editing",
|
|
5
|
+
Write: "writing",
|
|
6
|
+
Bash: "command",
|
|
7
|
+
Grep: "searching",
|
|
8
|
+
Glob: "searching",
|
|
9
|
+
WebFetch: "web",
|
|
10
|
+
WebSearch: "web",
|
|
11
|
+
Task: "subagent",
|
|
12
|
+
};
|
|
13
|
+
const ACTIVITY_DISPLAY = {
|
|
14
|
+
thinking: "💭 Thinking",
|
|
15
|
+
reading: "📖 Reading",
|
|
16
|
+
editing: "✏️ Editing",
|
|
17
|
+
writing: "📝 Writing",
|
|
18
|
+
searching: "🔍 Searching",
|
|
19
|
+
command: "🔧 Running command",
|
|
20
|
+
web: "🌐 Web lookup",
|
|
21
|
+
subagent: "🧩 Sub-agent",
|
|
22
|
+
mcp: "🔌 MCP tool",
|
|
23
|
+
};
|
|
24
|
+
function formatElapsed(ms) {
|
|
25
|
+
const totalSec = Math.floor(ms / 1000);
|
|
26
|
+
const min = Math.floor(totalSec / 60);
|
|
27
|
+
const sec = totalSec % 60;
|
|
28
|
+
return `${min}:${String(sec).padStart(2, "0")}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Detect activity from a stream-json event.
|
|
32
|
+
*/
|
|
33
|
+
function detectActivity(event) {
|
|
34
|
+
if (event.type === "assistant" && event.message?.content) {
|
|
35
|
+
for (const block of event.message.content) {
|
|
36
|
+
if (block.type === "tool_use" && block.name) {
|
|
37
|
+
// Check known tools
|
|
38
|
+
const key = TOOL_LABELS[block.name];
|
|
39
|
+
if (key)
|
|
40
|
+
return key;
|
|
41
|
+
// MCP tools (mcp__*)
|
|
42
|
+
if (block.name.startsWith("mcp__"))
|
|
43
|
+
return "mcp";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create an activity status updater that edits a Telegram message
|
|
51
|
+
* with current Claude activity and elapsed time.
|
|
52
|
+
*/
|
|
53
|
+
export function createActivityStatus(options) {
|
|
54
|
+
const { api, chatId, messageId } = options;
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
let currentLabel = ACTIVITY_DISPLAY.thinking;
|
|
57
|
+
let lastSentText = "";
|
|
58
|
+
let stopped = false;
|
|
59
|
+
const timer = setInterval(sendUpdate, MIN_UPDATE_INTERVAL_MS);
|
|
60
|
+
async function sendUpdate() {
|
|
61
|
+
if (stopped)
|
|
62
|
+
return;
|
|
63
|
+
const elapsed = formatElapsed(Date.now() - startTime);
|
|
64
|
+
const text = `${currentLabel} ⏱ ${elapsed}`;
|
|
65
|
+
if (text === lastSentText)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
await api.editMessageText(chatId, messageId, text);
|
|
69
|
+
lastSentText = text;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Silently ignore edit failures (rate limit, message deleted, etc.)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function onEvent(event) {
|
|
76
|
+
if (stopped)
|
|
77
|
+
return;
|
|
78
|
+
const key = detectActivity(event);
|
|
79
|
+
if (key) {
|
|
80
|
+
currentLabel = ACTIVITY_DISPLAY[key];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function stop() {
|
|
84
|
+
stopped = true;
|
|
85
|
+
clearInterval(timer);
|
|
86
|
+
}
|
|
87
|
+
// Send first update immediately
|
|
88
|
+
void sendUpdate();
|
|
89
|
+
return { onEvent, stop };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Bot } from "grammy";
|
|
2
|
+
import type { BotConfig } from "./types.js";
|
|
3
|
+
import { type BotModule, type ModuleContext } from "./modules.js";
|
|
4
|
+
export interface CreateBotOptions {
|
|
5
|
+
modules?: BotModule[];
|
|
6
|
+
onModuleContext?: (ctx: ModuleContext) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create and configure a Grammy bot connected to Claude CLI.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createBot(config: BotConfig, options?: CreateBotOptions): Bot;
|
|
12
|
+
/**
|
|
13
|
+
* Start the bot with graceful shutdown handling.
|
|
14
|
+
*/
|
|
15
|
+
export declare function startBot(config: BotConfig): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=bot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../../src/bot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAgB,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,YAAY,CAAC;AAM1D,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD;AAsBD;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,gBAAqB,GAAG,GAAG,CA0WhF;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiD/D"}
|