ccpa-telegram 1.0.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.
Files changed (74) hide show
  1. package/README.md +169 -0
  2. package/dist/bot/commands/clear.d.ts +3 -0
  3. package/dist/bot/commands/clear.d.ts.map +1 -0
  4. package/dist/bot/commands/clear.js +20 -0
  5. package/dist/bot/commands/clear.js.map +1 -0
  6. package/dist/bot/commands/help.d.ts +3 -0
  7. package/dist/bot/commands/help.d.ts.map +1 -0
  8. package/dist/bot/commands/help.js +16 -0
  9. package/dist/bot/commands/help.js.map +1 -0
  10. package/dist/bot/commands/start.d.ts +3 -0
  11. package/dist/bot/commands/start.d.ts.map +1 -0
  12. package/dist/bot/commands/start.js +10 -0
  13. package/dist/bot/commands/start.js.map +1 -0
  14. package/dist/bot/handlers/document.d.ts +6 -0
  15. package/dist/bot/handlers/document.d.ts.map +1 -0
  16. package/dist/bot/handlers/document.js +128 -0
  17. package/dist/bot/handlers/document.js.map +1 -0
  18. package/dist/bot/handlers/index.d.ts +4 -0
  19. package/dist/bot/handlers/index.d.ts.map +1 -0
  20. package/dist/bot/handlers/index.js +4 -0
  21. package/dist/bot/handlers/index.js.map +1 -0
  22. package/dist/bot/handlers/photo.d.ts +6 -0
  23. package/dist/bot/handlers/photo.d.ts.map +1 -0
  24. package/dist/bot/handlers/photo.js +87 -0
  25. package/dist/bot/handlers/photo.js.map +1 -0
  26. package/dist/bot/handlers/text.d.ts +6 -0
  27. package/dist/bot/handlers/text.d.ts.map +1 -0
  28. package/dist/bot/handlers/text.js +84 -0
  29. package/dist/bot/handlers/text.js.map +1 -0
  30. package/dist/bot/middleware/auth.d.ts +6 -0
  31. package/dist/bot/middleware/auth.d.ts.map +1 -0
  32. package/dist/bot/middleware/auth.js +22 -0
  33. package/dist/bot/middleware/auth.js.map +1 -0
  34. package/dist/bot/middleware/rateLimit.d.ts +6 -0
  35. package/dist/bot/middleware/rateLimit.d.ts.map +1 -0
  36. package/dist/bot/middleware/rateLimit.js +54 -0
  37. package/dist/bot/middleware/rateLimit.js.map +1 -0
  38. package/dist/bot.d.ts +2 -0
  39. package/dist/bot.d.ts.map +1 -0
  40. package/dist/bot.js +73 -0
  41. package/dist/bot.js.map +1 -0
  42. package/dist/claude/executor.d.ts +17 -0
  43. package/dist/claude/executor.d.ts.map +1 -0
  44. package/dist/claude/executor.js +135 -0
  45. package/dist/claude/executor.js.map +1 -0
  46. package/dist/claude/parser.d.ts +13 -0
  47. package/dist/claude/parser.d.ts.map +1 -0
  48. package/dist/claude/parser.js +63 -0
  49. package/dist/claude/parser.js.map +1 -0
  50. package/dist/cli.d.ts +3 -0
  51. package/dist/cli.d.ts.map +1 -0
  52. package/dist/cli.js +124 -0
  53. package/dist/cli.js.map +1 -0
  54. package/dist/config.d.ts +38 -0
  55. package/dist/config.d.ts.map +1 -0
  56. package/dist/config.js +167 -0
  57. package/dist/config.js.map +1 -0
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +4 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/logger.d.ts +4 -0
  63. package/dist/logger.d.ts.map +1 -0
  64. package/dist/logger.js +31 -0
  65. package/dist/logger.js.map +1 -0
  66. package/dist/telegram/chunker.d.ts +10 -0
  67. package/dist/telegram/chunker.d.ts.map +1 -0
  68. package/dist/telegram/chunker.js +88 -0
  69. package/dist/telegram/chunker.js.map +1 -0
  70. package/dist/user/setup.d.ts +22 -0
  71. package/dist/user/setup.d.ts.map +1 -0
  72. package/dist/user/setup.js +63 -0
  73. package/dist/user/setup.js.map +1 -0
  74. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # ccpa-telegram
2
+
3
+ A Telegram bot that provides access to Claude Code as a personal assistant. Run Claude Code in any directory and interact with it through Telegram.
4
+
5
+ ## Features
6
+
7
+ - Chat with Claude Code via Telegram
8
+ - Send images and documents for analysis
9
+ - Persistent conversation sessions per user
10
+ - Configurable Claude settings per project
11
+ - Multi-user support with access control
12
+
13
+ ## How It Works
14
+
15
+ This bot runs Claude Code as a subprocess in your chosen working directory. Claude Code reads all its standard configuration files from that directory, exactly as it would when running directly in a terminal:
16
+
17
+ - `CLAUDE.md` - Project-specific instructions and context
18
+ - `.claude/settings.json` - Permissions and tool settings
19
+ - `.claude/commands/` - Custom slash commands
20
+ - `.mcp.json` - MCP server configurations
21
+
22
+ This means you get the full power of Claude Code - including file access, code execution, and any configured MCP tools - all accessible through Telegram.
23
+
24
+ For complete documentation on Claude Code configuration, see the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-code).
25
+
26
+ ## Prerequisites
27
+
28
+ - Node.js 18+
29
+ - [Claude Code CLI](https://github.com/anthropics/claude-code) installed and authenticated.
30
+ - A Telegram bot token (from [@BotFather](https://t.me/BotFather)). See [Creating a Telegram Bot](#creating-a-telegram-bot) for instructions.
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ # Initialize a new project
36
+ npx ccpa-telegram init
37
+
38
+ # Edit ccpa.config.json with your bot token and allowed user IDs
39
+
40
+ # Start the bot
41
+ npx ccpa-telegram
42
+ ```
43
+
44
+ ## Installation
45
+
46
+ ### Using npx (recommended)
47
+
48
+ ```bash
49
+ npx ccpa-telegram init --cwd ./my-project
50
+ npx ccpa-telegram --cwd ./my-project
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ ### ccpa.config.json
56
+
57
+ Create a `ccpa.config.json` file in your project directory:
58
+
59
+ ```json
60
+ {
61
+ "telegram": {
62
+ "botToken": "YOUR_BOT_TOKEN_HERE"
63
+ },
64
+ "access": {
65
+ "allowedUserIds": [123456789]
66
+ },
67
+ "claude": {
68
+ "command": "claude"
69
+ },
70
+ "logging": {
71
+ "level": "info"
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Configuration Options
77
+
78
+ | Option | Description | Default |
79
+ | ----------------------- | ------------------------------------------------- | ---------- |
80
+ | `telegram.botToken` | Telegram bot token from BotFather | Required |
81
+ | `access.allowedUserIds` | Array of Telegram user IDs allowed to use the bot | `[]` |
82
+ | `claude.command` | Claude CLI command | `"claude"` |
83
+ | `logging.level` | Log level: debug, info, warn, error | `"info"` |
84
+
85
+ ### Environment Variables
86
+
87
+ Environment variables override config file values:
88
+
89
+ | Variable | Description |
90
+ | -------------------- | ------------------------ |
91
+ | `TELEGRAM_BOT_TOKEN` | Telegram bot token |
92
+ | `ALLOWED_USER_IDS` | Comma-separated user IDs |
93
+ | `CLAUDE_COMMAND` | Claude CLI command |
94
+ | `LOG_LEVEL` | Logging level |
95
+
96
+ ## Directory Structure
97
+
98
+ ```
99
+ my-project/
100
+ ├── ccpa.config.json # Bot configuration
101
+ ├── CLAUDE.md # Claude system prompt
102
+ ├── .claude/
103
+ │ └── settings.json # Claude settings
104
+ └── .ccpa/
105
+ └── users/
106
+ └── {userId}/
107
+ ├── uploads/ # Uploaded files
108
+ └── session.json # Session data
109
+ ```
110
+
111
+ ## CLI Commands
112
+
113
+ ```bash
114
+ # Show help
115
+ npx ccpa-telegram --help
116
+
117
+ # Initialize config file
118
+ npx ccpa-telegram init
119
+ npx ccpa-telegram init --cwd ./my-project
120
+
121
+ # Start the bot
122
+ npx ccpa-telegram
123
+ npx ccpa-telegram --cwd ./my-project
124
+ ```
125
+
126
+ ## Bot Commands
127
+
128
+ | Command | Description |
129
+ | -------- | -------------------------- |
130
+ | `/start` | Welcome message |
131
+ | `/help` | Show help information |
132
+ | `/clear` | Clear conversation history |
133
+
134
+ ## Creating a Telegram Bot
135
+
136
+ To create a new Telegram bot and get your bot token:
137
+
138
+ 1. Open Telegram and message [@BotFather](https://t.me/BotFather)
139
+ 2. Send `/newbot` command
140
+ 3. Choose a **display name** for your bot (e.g., "My Claude Assistant")
141
+ 4. Choose a **username** - must be unique and end with `bot` (e.g., `my_claude_assistant_bot`). The length of the username must be between 5 and 32 characters.
142
+ 5. BotFather will reply with your bot token (looks like `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`)
143
+ 6. Copy this token to your `ccpa.config.json`
144
+
145
+ For detailed instructions, see the [Telegram Bot API documentation](https://core.telegram.org/bots#how-do-i-create-a-bot).
146
+
147
+ ## Finding Your Telegram User ID
148
+
149
+ To find your Telegram user ID:
150
+
151
+ 1. Message [@userinfobot](https://t.me/userinfobot) on Telegram
152
+ 2. It will reply with your user ID
153
+ 3. Add this ID to `allowedUserIds` in your config
154
+
155
+ ## Security Notice
156
+
157
+ **Important**: Conversations with this bot are not end-to-end encrypted. Messages pass through Telegram's servers and are processed by the Claude API. Do not share sensitive information such as:
158
+
159
+ - Passwords or API keys
160
+ - Personal identification numbers
161
+ - Financial information
162
+ - Confidential business data
163
+ - Any other private or sensitive data
164
+
165
+ This bot is intended for development assistance and general queries only. Treat all conversations as potentially visible to third parties.
166
+
167
+ ## License
168
+
169
+ ISC
@@ -0,0 +1,3 @@
1
+ import type { Context } from "grammy";
2
+ export declare function clearHandler(ctx: Context): Promise<void>;
3
+ //# sourceMappingURL=clear.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../../src/bot/commands/clear.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAItC,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9D"}
@@ -0,0 +1,20 @@
1
+ import { join } from "node:path";
2
+ import { getConfig } from "../../config.js";
3
+ import { clearUserData } from "../../user/setup.js";
4
+ export async function clearHandler(ctx) {
5
+ const config = getConfig();
6
+ const userId = ctx.from?.id;
7
+ if (!userId) {
8
+ await ctx.reply("Could not identify user.");
9
+ return;
10
+ }
11
+ const userDir = join(config.dataDir, String(userId));
12
+ try {
13
+ await clearUserData(userDir);
14
+ await ctx.reply("Conversation history cleared. Your next message will start a fresh conversation.");
15
+ }
16
+ catch (_error) {
17
+ await ctx.reply("Failed to clear conversation history. Please try again.");
18
+ }
19
+ }
20
+ //# sourceMappingURL=clear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.js","sourceRoot":"","sources":["../../../src/bot/commands/clear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAY;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,GAAG,CAAC,KAAK,CACb,kFAAkF,CACnF,CAAC;IACJ,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,MAAM,GAAG,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Context } from "grammy";
2
+ export declare function helpHandler(ctx: Context): Promise<void>;
3
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/bot/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB7D"}
@@ -0,0 +1,16 @@
1
+ export async function helpHandler(ctx) {
2
+ await ctx.reply(`*Claude Code Telegram Bot*\n\n` +
3
+ `*Commands:*\n` +
4
+ `/start - Welcome message\n` +
5
+ `/help - Show this help\n` +
6
+ `/clear - Clear conversation history\n\n` +
7
+ `*Usage:*\n` +
8
+ `Just send any message to chat with Claude.\n` +
9
+ `You can also send images and documents for analysis.\n\n` +
10
+ `Your conversation history is preserved between messages. ` +
11
+ `Use /clear to start a fresh conversation.\n\n` +
12
+ `*Configuration:*\n` +
13
+ `Claude reads configuration from your .claude folder.\n` +
14
+ `Edit CLAUDE.md for system prompts and .claude/settings.json for permissions.`, { parse_mode: "Markdown" });
15
+ }
16
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/bot/commands/help.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAY;IAC5C,MAAM,GAAG,CAAC,KAAK,CACb,gCAAgC;QAC9B,eAAe;QACf,4BAA4B;QAC5B,0BAA0B;QAC1B,yCAAyC;QACzC,YAAY;QACZ,8CAA8C;QAC9C,0DAA0D;QAC1D,2DAA2D;QAC3D,+CAA+C;QAC/C,oBAAoB;QACpB,wDAAwD;QACxD,8EAA8E,EAChF,EAAE,UAAU,EAAE,UAAU,EAAE,CAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Context } from "grammy";
2
+ export declare function startHandler(ctx: Context): Promise<void>;
3
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/bot/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9D"}
@@ -0,0 +1,10 @@
1
+ export async function startHandler(ctx) {
2
+ const username = ctx.from?.first_name || "there";
3
+ await ctx.reply(`Hello ${username}! I'm a Claude Code assistant bot.\n\n` +
4
+ `You can:\n` +
5
+ `- Send any message to chat with me\n` +
6
+ `- Send images or documents for analysis\n` +
7
+ `- Use /clear to start a new conversation\n\n` +
8
+ `Type /help for more information.`);
9
+ }
10
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/bot/commands/start.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAY;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAE,UAAU,IAAI,OAAO,CAAC;IAEjD,MAAM,GAAG,CAAC,KAAK,CACb,SAAS,QAAQ,wCAAwC;QACvD,YAAY;QACZ,sCAAsC;QACtC,2CAA2C;QAC3C,8CAA8C;QAC9C,kCAAkC,CACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Context } from "grammy";
2
+ /**
3
+ * Handle document messages (PDFs, images, code files, etc.)
4
+ */
5
+ export declare function documentHandler(ctx: Context): Promise<void>;
6
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../src/bot/handlers/document.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAgDtC;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GjE"}
@@ -0,0 +1,128 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { executeClaudeQuery } from "../../claude/executor.js";
4
+ import { parseClaudeOutput } from "../../claude/parser.js";
5
+ import { getConfig } from "../../config.js";
6
+ import { getLogger } from "../../logger.js";
7
+ import { sendChunkedResponse } from "../../telegram/chunker.js";
8
+ import { ensureUserSetup, getSessionId, getUploadsPath, saveSessionId, } from "../../user/setup.js";
9
+ const SUPPORTED_MIME_TYPES = [
10
+ "application/pdf",
11
+ "text/plain",
12
+ "text/markdown",
13
+ "text/csv",
14
+ "application/json",
15
+ "application/xml",
16
+ "text/html",
17
+ "image/jpeg",
18
+ "image/png",
19
+ "image/gif",
20
+ "image/webp",
21
+ ];
22
+ const SUPPORTED_EXTENSIONS = [
23
+ ".pdf",
24
+ ".txt",
25
+ ".md",
26
+ ".csv",
27
+ ".json",
28
+ ".xml",
29
+ ".html",
30
+ ".js",
31
+ ".ts",
32
+ ".py",
33
+ ".go",
34
+ ".rs",
35
+ ".java",
36
+ ".jpg",
37
+ ".jpeg",
38
+ ".png",
39
+ ".gif",
40
+ ".webp",
41
+ ];
42
+ /**
43
+ * Handle document messages (PDFs, images, code files, etc.)
44
+ */
45
+ export async function documentHandler(ctx) {
46
+ const config = getConfig();
47
+ const logger = getLogger();
48
+ const userId = ctx.from?.id;
49
+ const document = ctx.message?.document;
50
+ const caption = ctx.message?.caption || "Please analyze this document.";
51
+ if (!userId || !document) {
52
+ return;
53
+ }
54
+ const mimeType = document.mime_type || "";
55
+ const fileName = document.file_name || "document";
56
+ const ext = fileName.includes(".")
57
+ ? `.${fileName.split(".").pop()?.toLowerCase()}`
58
+ : "";
59
+ const isSupported = SUPPORTED_MIME_TYPES.includes(mimeType) ||
60
+ SUPPORTED_EXTENSIONS.includes(ext);
61
+ if (!isSupported) {
62
+ await ctx.reply(`Unsupported file type. Supported: PDF, images, text, and code files.`);
63
+ return;
64
+ }
65
+ logger.debug({ fileName, mimeType }, "Document received");
66
+ const userDir = resolve(join(config.dataDir, String(userId)));
67
+ try {
68
+ await ensureUserSetup(userDir);
69
+ const file = await ctx.api.getFile(document.file_id);
70
+ const filePath = file.file_path;
71
+ if (!filePath) {
72
+ await ctx.reply("Could not download the document.");
73
+ return;
74
+ }
75
+ const fileUrl = `https://api.telegram.org/file/bot${config.telegram.botToken}/${filePath}`;
76
+ const response = await fetch(fileUrl);
77
+ const buffer = Buffer.from(await response.arrayBuffer());
78
+ const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
79
+ const uploadsDir = getUploadsPath(userDir);
80
+ const docPath = join(uploadsDir, safeName);
81
+ await writeFile(docPath, buffer);
82
+ logger.debug({ path: docPath }, "Document saved");
83
+ const prompt = `Please read the file "./uploads/${safeName}" and ${caption}`;
84
+ const sessionId = await getSessionId(userDir);
85
+ const statusMsg = await ctx.reply("_Processing..._", {
86
+ parse_mode: "Markdown",
87
+ });
88
+ let lastProgressUpdate = Date.now();
89
+ let lastProgressText = "Processing...";
90
+ const onProgress = async (message) => {
91
+ const now = Date.now();
92
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
93
+ lastProgressUpdate = now;
94
+ lastProgressText = message;
95
+ try {
96
+ await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
97
+ }
98
+ catch {
99
+ // Ignore edit errors
100
+ }
101
+ }
102
+ };
103
+ logger.debug("Executing Claude query with document");
104
+ const result = await executeClaudeQuery({
105
+ prompt,
106
+ userDir,
107
+ sessionId,
108
+ onProgress,
109
+ });
110
+ try {
111
+ await ctx.api.deleteMessage(ctx.chat.id, statusMsg.message_id);
112
+ }
113
+ catch {
114
+ // Ignore delete errors
115
+ }
116
+ const parsed = parseClaudeOutput(result);
117
+ if (parsed.sessionId) {
118
+ await saveSessionId(userDir, parsed.sessionId);
119
+ }
120
+ await sendChunkedResponse(ctx, parsed.text);
121
+ }
122
+ catch (error) {
123
+ logger.error({ error }, "Document handler error");
124
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
125
+ await ctx.reply(`An error occurred processing the document: ${errorMessage}`);
126
+ }
127
+ }
128
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../../src/bot/handlers/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { documentHandler } from "./document.js";
2
+ export { photoHandler } from "./photo.js";
3
+ export { textHandler } from "./text.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bot/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { documentHandler } from "./document.js";
2
+ export { photoHandler } from "./photo.js";
3
+ export { textHandler } from "./text.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/bot/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Context } from "grammy";
2
+ /**
3
+ * Handle photo messages
4
+ */
5
+ export declare function photoHandler(ctx: Context): Promise<void>;
6
+ //# sourceMappingURL=photo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photo.d.ts","sourceRoot":"","sources":["../../../src/bot/handlers/photo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAatC;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8F9D"}
@@ -0,0 +1,87 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { executeClaudeQuery } from "../../claude/executor.js";
4
+ import { parseClaudeOutput } from "../../claude/parser.js";
5
+ import { getConfig } from "../../config.js";
6
+ import { getLogger } from "../../logger.js";
7
+ import { sendChunkedResponse } from "../../telegram/chunker.js";
8
+ import { ensureUserSetup, getSessionId, getUploadsPath, saveSessionId, } from "../../user/setup.js";
9
+ /**
10
+ * Handle photo messages
11
+ */
12
+ export async function photoHandler(ctx) {
13
+ const config = getConfig();
14
+ const logger = getLogger();
15
+ const userId = ctx.from?.id;
16
+ const photo = ctx.message?.photo;
17
+ const caption = ctx.message?.caption || "Please analyze this image.";
18
+ if (!userId || !photo || photo.length === 0) {
19
+ return;
20
+ }
21
+ logger.debug({ userId }, "Photo received");
22
+ const userDir = resolve(join(config.dataDir, String(userId)));
23
+ try {
24
+ await ensureUserSetup(userDir);
25
+ // Get the largest photo (last in array)
26
+ const largestPhoto = photo[photo.length - 1];
27
+ const file = await ctx.api.getFile(largestPhoto.file_id);
28
+ const filePath = file.file_path;
29
+ if (!filePath) {
30
+ await ctx.reply("Could not download the image.");
31
+ return;
32
+ }
33
+ const fileUrl = `https://api.telegram.org/file/bot${config.telegram.botToken}/${filePath}`;
34
+ const response = await fetch(fileUrl);
35
+ const buffer = Buffer.from(await response.arrayBuffer());
36
+ const ext = filePath.split(".").pop() || "jpg";
37
+ const imageName = `image_${Date.now()}.${ext}`;
38
+ const uploadsDir = getUploadsPath(userDir);
39
+ const imagePath = join(uploadsDir, imageName);
40
+ await writeFile(imagePath, buffer);
41
+ logger.debug({ path: imagePath }, "Image saved");
42
+ const prompt = `Please look at the image file "./uploads/${imageName}" and ${caption}`;
43
+ const sessionId = await getSessionId(userDir);
44
+ const statusMsg = await ctx.reply("_Processing..._", {
45
+ parse_mode: "Markdown",
46
+ });
47
+ let lastProgressUpdate = Date.now();
48
+ let lastProgressText = "Processing...";
49
+ const onProgress = async (message) => {
50
+ const now = Date.now();
51
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
52
+ lastProgressUpdate = now;
53
+ lastProgressText = message;
54
+ try {
55
+ await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
56
+ }
57
+ catch {
58
+ // Ignore edit errors
59
+ }
60
+ }
61
+ };
62
+ logger.debug("Executing Claude query with image");
63
+ const result = await executeClaudeQuery({
64
+ prompt,
65
+ userDir,
66
+ sessionId,
67
+ onProgress,
68
+ });
69
+ try {
70
+ await ctx.api.deleteMessage(ctx.chat.id, statusMsg.message_id);
71
+ }
72
+ catch {
73
+ // Ignore delete errors
74
+ }
75
+ const parsed = parseClaudeOutput(result);
76
+ if (parsed.sessionId) {
77
+ await saveSessionId(userDir, parsed.sessionId);
78
+ }
79
+ await sendChunkedResponse(ctx, parsed.text);
80
+ }
81
+ catch (error) {
82
+ logger.error({ error }, "Photo handler error");
83
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
84
+ await ctx.reply(`An error occurred processing the image: ${errorMessage}`);
85
+ }
86
+ }
87
+ //# sourceMappingURL=photo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photo.js","sourceRoot":"","sources":["../../../src/bot/handlers/photo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,eAAe,EACf,YAAY,EACZ,cAAc,EACd,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAY;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC;IACjC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,4BAA4B,CAAC;IAErE,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAE/B,wCAAwC;QACxC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAEhC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,GAAG,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACjD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,oCAAoC,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAC3F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAEzD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC;QAC/C,MAAM,SAAS,GAAG,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC9C,MAAM,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAEnC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,4CAA4C,SAAS,SAAS,OAAO,EAAE,CAAC;QACvF,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;QAE9C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE;YACnD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,IAAI,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,gBAAgB,GAAG,eAAe,CAAC;QAEvC,MAAM,UAAU,GAAG,KAAK,EAAE,OAAe,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,IAAI,GAAG,GAAG,kBAAkB,GAAG,IAAI,IAAI,OAAO,KAAK,gBAAgB,EAAE,CAAC;gBACpE,kBAAkB,GAAG,GAAG,CAAC;gBACzB,gBAAgB,GAAG,OAAO,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,GAAG,CAAC,GAAG,CAAC,eAAe,CAC3B,GAAG,CAAC,IAAK,CAAC,EAAE,EACZ,SAAS,CAAC,UAAU,EACpB,IAAI,OAAO,GAAG,EACd,EAAE,UAAU,EAAE,UAAU,EAAE,CAC3B,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,qBAAqB;gBACvB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;YACtC,MAAM;YACN,OAAO;YACP,SAAS;YACT,UAAU;SACX,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QAClE,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;QAED,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAEzC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC/C,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QAC3D,MAAM,GAAG,CAAC,KAAK,CAAC,2CAA2C,YAAY,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Context } from "grammy";
2
+ /**
3
+ * Handle text messages - routes to Claude
4
+ */
5
+ export declare function textHandler(ctx: Context): Promise<void>;
6
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/bot/handlers/text.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAWtC;;GAEG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8F7D"}
@@ -0,0 +1,84 @@
1
+ import { join, resolve } from "node:path";
2
+ import { executeClaudeQuery } from "../../claude/executor.js";
3
+ import { getConfig } from "../../config.js";
4
+ import { getLogger } from "../../logger.js";
5
+ import { sendChunkedResponse } from "../../telegram/chunker.js";
6
+ import { ensureUserSetup, getSessionId, saveSessionId, } from "../../user/setup.js";
7
+ /**
8
+ * Handle text messages - routes to Claude
9
+ */
10
+ export async function textHandler(ctx) {
11
+ const config = getConfig();
12
+ const logger = getLogger();
13
+ const userId = ctx.from?.id;
14
+ const messageText = ctx.message?.text;
15
+ if (!userId || !messageText) {
16
+ return;
17
+ }
18
+ logger.debug({
19
+ userId,
20
+ username: ctx.from?.username,
21
+ name: ctx.from?.first_name,
22
+ }, "Message received");
23
+ const userDir = resolve(join(config.dataDir, String(userId)));
24
+ try {
25
+ logger.debug({ userDir }, "Setting up user directory");
26
+ await ensureUserSetup(userDir);
27
+ if (!messageText.trim()) {
28
+ await ctx.reply("Please provide a message.");
29
+ return;
30
+ }
31
+ const sessionId = await getSessionId(userDir);
32
+ logger.debug({ sessionId: sessionId || "new" }, "Session");
33
+ // Send initial status message
34
+ const statusMsg = await ctx.reply("_Processing..._", {
35
+ parse_mode: "Markdown",
36
+ });
37
+ let lastProgressUpdate = Date.now();
38
+ let lastProgressText = "Processing...";
39
+ // Progress callback - updates status message
40
+ const onProgress = async (message) => {
41
+ const now = Date.now();
42
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
43
+ lastProgressUpdate = now;
44
+ lastProgressText = message;
45
+ try {
46
+ await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
47
+ }
48
+ catch {
49
+ // Ignore edit errors
50
+ }
51
+ }
52
+ };
53
+ logger.debug("Executing Claude query");
54
+ const result = await executeClaudeQuery({
55
+ prompt: messageText,
56
+ userDir,
57
+ sessionId,
58
+ onProgress,
59
+ });
60
+ logger.debug({ success: result.success, error: result.error }, "Claude result");
61
+ // Delete status message
62
+ try {
63
+ await ctx.api.deleteMessage(ctx.chat.id, statusMsg.message_id);
64
+ }
65
+ catch {
66
+ // Ignore delete errors
67
+ }
68
+ if (result.sessionId) {
69
+ await saveSessionId(userDir, result.sessionId);
70
+ logger.debug({ sessionId: result.sessionId }, "Session saved");
71
+ }
72
+ const responseText = result.success
73
+ ? result.output
74
+ : result.error || "An error occurred";
75
+ await sendChunkedResponse(ctx, responseText);
76
+ logger.debug("Response sent");
77
+ }
78
+ catch (error) {
79
+ logger.error({ error }, "Text handler error");
80
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
81
+ await ctx.reply(`An error occurred: ${errorMessage}`);
82
+ }
83
+ }
84
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/bot/handlers/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Context, NextFunction } from "grammy";
2
+ /**
3
+ * Middleware to check if user is in the whitelist
4
+ */
5
+ export declare function authMiddleware(ctx: Context, next: NextFunction): Promise<void>;
6
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/bot/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGpD;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAqBf"}