@marcopeg/hal 1.0.11

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 (106) hide show
  1. package/README.md +655 -0
  2. package/dist/agent/index.d.ts +17 -0
  3. package/dist/agent/index.d.ts.map +1 -0
  4. package/dist/agent/index.js +30 -0
  5. package/dist/agent/index.js.map +1 -0
  6. package/dist/bot/commands/clear.d.ts +7 -0
  7. package/dist/bot/commands/clear.d.ts.map +1 -0
  8. package/dist/bot/commands/clear.js +23 -0
  9. package/dist/bot/commands/clear.js.map +1 -0
  10. package/dist/bot/commands/help.d.ts +3 -0
  11. package/dist/bot/commands/help.d.ts.map +1 -0
  12. package/dist/bot/commands/help.js +16 -0
  13. package/dist/bot/commands/help.js.map +1 -0
  14. package/dist/bot/commands/loader.d.ts +26 -0
  15. package/dist/bot/commands/loader.d.ts.map +1 -0
  16. package/dist/bot/commands/loader.js +206 -0
  17. package/dist/bot/commands/loader.js.map +1 -0
  18. package/dist/bot/commands/start.d.ts +3 -0
  19. package/dist/bot/commands/start.d.ts.map +1 -0
  20. package/dist/bot/commands/start.js +10 -0
  21. package/dist/bot/commands/start.js.map +1 -0
  22. package/dist/bot/commands/watcher.d.ts +11 -0
  23. package/dist/bot/commands/watcher.d.ts.map +1 -0
  24. package/dist/bot/commands/watcher.js +106 -0
  25. package/dist/bot/commands/watcher.js.map +1 -0
  26. package/dist/bot/handlers/document.d.ts +7 -0
  27. package/dist/bot/handlers/document.d.ts.map +1 -0
  28. package/dist/bot/handlers/document.js +128 -0
  29. package/dist/bot/handlers/document.js.map +1 -0
  30. package/dist/bot/handlers/index.d.ts +5 -0
  31. package/dist/bot/handlers/index.d.ts.map +1 -0
  32. package/dist/bot/handlers/index.js +5 -0
  33. package/dist/bot/handlers/index.js.map +1 -0
  34. package/dist/bot/handlers/photo.d.ts +7 -0
  35. package/dist/bot/handlers/photo.d.ts.map +1 -0
  36. package/dist/bot/handlers/photo.js +87 -0
  37. package/dist/bot/handlers/photo.js.map +1 -0
  38. package/dist/bot/handlers/text.d.ts +7 -0
  39. package/dist/bot/handlers/text.d.ts.map +1 -0
  40. package/dist/bot/handlers/text.js +186 -0
  41. package/dist/bot/handlers/text.js.map +1 -0
  42. package/dist/bot/handlers/voice.d.ts +7 -0
  43. package/dist/bot/handlers/voice.d.ts.map +1 -0
  44. package/dist/bot/handlers/voice.js +147 -0
  45. package/dist/bot/handlers/voice.js.map +1 -0
  46. package/dist/bot/middleware/auth.d.ts +7 -0
  47. package/dist/bot/middleware/auth.d.ts.map +1 -0
  48. package/dist/bot/middleware/auth.js +23 -0
  49. package/dist/bot/middleware/auth.js.map +1 -0
  50. package/dist/bot/middleware/rateLimit.d.ts +11 -0
  51. package/dist/bot/middleware/rateLimit.d.ts.map +1 -0
  52. package/dist/bot/middleware/rateLimit.js +49 -0
  53. package/dist/bot/middleware/rateLimit.js.map +1 -0
  54. package/dist/bot.d.ts +11 -0
  55. package/dist/bot.d.ts.map +1 -0
  56. package/dist/bot.js +93 -0
  57. package/dist/bot.js.map +1 -0
  58. package/dist/claude/executor.d.ts +21 -0
  59. package/dist/claude/executor.d.ts.map +1 -0
  60. package/dist/claude/executor.js +185 -0
  61. package/dist/claude/executor.js.map +1 -0
  62. package/dist/claude/parser.d.ts +13 -0
  63. package/dist/claude/parser.d.ts.map +1 -0
  64. package/dist/claude/parser.js +63 -0
  65. package/dist/claude/parser.js.map +1 -0
  66. package/dist/cli.d.ts +3 -0
  67. package/dist/cli.d.ts.map +1 -0
  68. package/dist/cli.js +192 -0
  69. package/dist/cli.js.map +1 -0
  70. package/dist/config.d.ts +216 -0
  71. package/dist/config.d.ts.map +1 -0
  72. package/dist/config.js +396 -0
  73. package/dist/config.js.map +1 -0
  74. package/dist/context/resolver.d.ts +19 -0
  75. package/dist/context/resolver.d.ts.map +1 -0
  76. package/dist/context/resolver.js +171 -0
  77. package/dist/context/resolver.js.map +1 -0
  78. package/dist/index.d.ts +7 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +5 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/logger.d.ts +17 -0
  83. package/dist/logger.d.ts.map +1 -0
  84. package/dist/logger.js +44 -0
  85. package/dist/logger.js.map +1 -0
  86. package/dist/telegram/chunker.d.ts +10 -0
  87. package/dist/telegram/chunker.d.ts.map +1 -0
  88. package/dist/telegram/chunker.js +88 -0
  89. package/dist/telegram/chunker.js.map +1 -0
  90. package/dist/telegram/fileSender.d.ts +8 -0
  91. package/dist/telegram/fileSender.d.ts.map +1 -0
  92. package/dist/telegram/fileSender.js +46 -0
  93. package/dist/telegram/fileSender.js.map +1 -0
  94. package/dist/transcription/whisper.d.ts +11 -0
  95. package/dist/transcription/whisper.d.ts.map +1 -0
  96. package/dist/transcription/whisper.js +58 -0
  97. package/dist/transcription/whisper.js.map +1 -0
  98. package/dist/types.d.ts +22 -0
  99. package/dist/types.d.ts.map +1 -0
  100. package/dist/types.js +2 -0
  101. package/dist/types.js.map +1 -0
  102. package/dist/user/setup.d.ts +26 -0
  103. package/dist/user/setup.d.ts.map +1 -0
  104. package/dist/user/setup.js +73 -0
  105. package/dist/user/setup.js.map +1 -0
  106. package/package.json +56 -0
@@ -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 { sendChunkedResponse } from "../../telegram/chunker.js";
6
+ import { sendDownloadFiles } from "../../telegram/fileSender.js";
7
+ import { ensureUserSetup, getDownloadsPath, getSessionId, getUploadsPath, saveSessionId, } from "../../user/setup.js";
8
+ const SUPPORTED_MIME_TYPES = [
9
+ "application/pdf",
10
+ "text/plain",
11
+ "text/markdown",
12
+ "text/csv",
13
+ "application/json",
14
+ "application/xml",
15
+ "text/html",
16
+ "image/jpeg",
17
+ "image/png",
18
+ "image/gif",
19
+ "image/webp",
20
+ ];
21
+ const SUPPORTED_EXTENSIONS = [
22
+ ".pdf",
23
+ ".txt",
24
+ ".md",
25
+ ".csv",
26
+ ".json",
27
+ ".xml",
28
+ ".html",
29
+ ".js",
30
+ ".ts",
31
+ ".py",
32
+ ".go",
33
+ ".rs",
34
+ ".java",
35
+ ".jpg",
36
+ ".jpeg",
37
+ ".png",
38
+ ".gif",
39
+ ".webp",
40
+ ];
41
+ /**
42
+ * Returns a handler for document messages (PDFs, images, code files, etc.)
43
+ */
44
+ export function createDocumentHandler(ctx) {
45
+ return async (gramCtx) => {
46
+ const { config, logger } = ctx;
47
+ const userId = gramCtx.from?.id;
48
+ const document = gramCtx.message?.document;
49
+ const caption = gramCtx.message?.caption || "Please analyze this document.";
50
+ if (!userId || !document) {
51
+ return;
52
+ }
53
+ const mimeType = document.mime_type || "";
54
+ const fileName = document.file_name || "document";
55
+ const ext = fileName.includes(".")
56
+ ? `.${fileName.split(".").pop()?.toLowerCase()}`
57
+ : "";
58
+ const isSupported = SUPPORTED_MIME_TYPES.includes(mimeType) ||
59
+ SUPPORTED_EXTENSIONS.includes(ext);
60
+ if (!isSupported) {
61
+ await gramCtx.reply("Unsupported file type. Supported: PDF, images, text, and code files.");
62
+ return;
63
+ }
64
+ logger.debug({ fileName, mimeType }, "Document received");
65
+ const userDir = resolve(join(config.dataDir, String(userId)));
66
+ try {
67
+ await ensureUserSetup(userDir);
68
+ const file = await gramCtx.api.getFile(document.file_id);
69
+ const filePath = file.file_path;
70
+ if (!filePath) {
71
+ await gramCtx.reply("Could not download the document.");
72
+ return;
73
+ }
74
+ const fileUrl = `https://api.telegram.org/file/bot${config.telegram.botToken}/${filePath}`;
75
+ const response = await fetch(fileUrl);
76
+ const buffer = Buffer.from(await response.arrayBuffer());
77
+ const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
78
+ const uploadsDir = getUploadsPath(userDir);
79
+ const docPath = join(uploadsDir, safeName);
80
+ await writeFile(docPath, buffer);
81
+ logger.debug({ path: docPath }, "Document saved");
82
+ const prompt = `Please read the file "./uploads/${safeName}" and ${caption}`;
83
+ const sessionId = await getSessionId(userDir);
84
+ const statusMsg = await gramCtx.reply("_Processing..._", {
85
+ parse_mode: "Markdown",
86
+ });
87
+ let lastProgressUpdate = Date.now();
88
+ let lastProgressText = "Processing...";
89
+ const onProgress = async (message) => {
90
+ const now = Date.now();
91
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
92
+ lastProgressUpdate = now;
93
+ lastProgressText = message;
94
+ try {
95
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
96
+ }
97
+ catch {
98
+ // Ignore edit errors
99
+ }
100
+ }
101
+ };
102
+ const downloadsPath = getDownloadsPath(userDir);
103
+ logger.debug("Executing Claude query with document");
104
+ const result = await executeClaudeQuery({ prompt, gramCtx, userDir, downloadsPath, sessionId, onProgress }, ctx);
105
+ try {
106
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
107
+ }
108
+ catch {
109
+ // Ignore delete errors
110
+ }
111
+ const parsed = parseClaudeOutput(result);
112
+ if (parsed.sessionId) {
113
+ await saveSessionId(userDir, parsed.sessionId);
114
+ }
115
+ await sendChunkedResponse(gramCtx, parsed.text);
116
+ const filesSent = await sendDownloadFiles(gramCtx, userDir, ctx);
117
+ if (filesSent > 0) {
118
+ logger.info({ filesSent }, "Sent download files to user");
119
+ }
120
+ }
121
+ catch (error) {
122
+ logger.error({ error }, "Document handler error");
123
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
124
+ await gramCtx.reply(`An error occurred processing the document: ${errorMessage}`);
125
+ }
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,5 @@
1
+ export { createDocumentHandler } from "./document.js";
2
+ export { createPhotoHandler } from "./photo.js";
3
+ export { createTextHandler } from "./text.js";
4
+ export { createVoiceHandler } from "./voice.js";
5
+ //# 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,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createDocumentHandler } from "./document.js";
2
+ export { createPhotoHandler } from "./photo.js";
3
+ export { createTextHandler } from "./text.js";
4
+ export { createVoiceHandler } from "./voice.js";
5
+ //# 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,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Context } from "grammy";
2
+ import type { ProjectContext } from "../../types.js";
3
+ /**
4
+ * Returns a handler for photo messages.
5
+ */
6
+ export declare function createPhotoHandler(ctx: ProjectContext): (gramCtx: Context) => Promise<void>;
7
+ //# 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;AAKtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AASrD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,IACtC,SAAS,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC,CAqG/C"}
@@ -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 { sendChunkedResponse } from "../../telegram/chunker.js";
6
+ import { sendDownloadFiles } from "../../telegram/fileSender.js";
7
+ import { ensureUserSetup, getDownloadsPath, getSessionId, getUploadsPath, saveSessionId, } from "../../user/setup.js";
8
+ /**
9
+ * Returns a handler for photo messages.
10
+ */
11
+ export function createPhotoHandler(ctx) {
12
+ return async (gramCtx) => {
13
+ const { config, logger } = ctx;
14
+ const userId = gramCtx.from?.id;
15
+ const photo = gramCtx.message?.photo;
16
+ const caption = gramCtx.message?.caption || "Please analyze this image.";
17
+ if (!userId || !photo || photo.length === 0) {
18
+ return;
19
+ }
20
+ logger.debug({ userId }, "Photo received");
21
+ const userDir = resolve(join(config.dataDir, String(userId)));
22
+ try {
23
+ await ensureUserSetup(userDir);
24
+ // Get the largest photo (last in array)
25
+ const largestPhoto = photo[photo.length - 1];
26
+ const file = await gramCtx.api.getFile(largestPhoto.file_id);
27
+ const filePath = file.file_path;
28
+ if (!filePath) {
29
+ await gramCtx.reply("Could not download the image.");
30
+ return;
31
+ }
32
+ const fileUrl = `https://api.telegram.org/file/bot${config.telegram.botToken}/${filePath}`;
33
+ const response = await fetch(fileUrl);
34
+ const buffer = Buffer.from(await response.arrayBuffer());
35
+ const ext = filePath.split(".").pop() || "jpg";
36
+ const imageName = `image_${Date.now()}.${ext}`;
37
+ const uploadsDir = getUploadsPath(userDir);
38
+ const imagePath = join(uploadsDir, imageName);
39
+ await writeFile(imagePath, buffer);
40
+ logger.debug({ path: imagePath }, "Image saved");
41
+ const prompt = `Please look at the image file "./uploads/${imageName}" and ${caption}`;
42
+ const sessionId = await getSessionId(userDir);
43
+ const statusMsg = await gramCtx.reply("_Processing..._", {
44
+ parse_mode: "Markdown",
45
+ });
46
+ let lastProgressUpdate = Date.now();
47
+ let lastProgressText = "Processing...";
48
+ const onProgress = async (message) => {
49
+ const now = Date.now();
50
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
51
+ lastProgressUpdate = now;
52
+ lastProgressText = message;
53
+ try {
54
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
55
+ }
56
+ catch {
57
+ // Ignore edit errors
58
+ }
59
+ }
60
+ };
61
+ const downloadsPath = getDownloadsPath(userDir);
62
+ logger.debug("Executing Claude query with image");
63
+ const result = await executeClaudeQuery({ prompt, gramCtx, userDir, downloadsPath, sessionId, onProgress }, ctx);
64
+ try {
65
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
66
+ }
67
+ catch {
68
+ // Ignore delete errors
69
+ }
70
+ const parsed = parseClaudeOutput(result);
71
+ if (parsed.sessionId) {
72
+ await saveSessionId(userDir, parsed.sessionId);
73
+ }
74
+ await sendChunkedResponse(gramCtx, parsed.text);
75
+ const filesSent = await sendDownloadFiles(gramCtx, userDir, ctx);
76
+ if (filesSent > 0) {
77
+ logger.info({ filesSent }, "Sent download files to user");
78
+ }
79
+ }
80
+ catch (error) {
81
+ logger.error({ error }, "Photo handler error");
82
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
83
+ await gramCtx.reply(`An error occurred processing the image: ${errorMessage}`);
84
+ }
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":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Context } from "grammy";
2
+ import type { ProjectContext } from "../../types.js";
3
+ /**
4
+ * Returns a handler for text messages.
5
+ */
6
+ export declare function createTextHandler(ctx: ProjectContext): (gramCtx: Context) => Promise<void>;
7
+ //# 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;AAMtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AASrD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,IACrC,SAAS,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC,CAqO/C"}
@@ -0,0 +1,186 @@
1
+ import { join, resolve } from "node:path";
2
+ import { createAgent, getSkillsDir } from "../../agent/index.js";
3
+ import { executeClaudeQuery } from "../../claude/executor.js";
4
+ import { resolveContext } from "../../context/resolver.js";
5
+ import { sendChunkedResponse } from "../../telegram/chunker.js";
6
+ import { sendDownloadFiles } from "../../telegram/fileSender.js";
7
+ import { ensureUserSetup, getDownloadsPath, getSessionId, saveSessionId, } from "../../user/setup.js";
8
+ import { resolveCommandPath, resolveSkillEntry } from "../commands/loader.js";
9
+ /**
10
+ * Returns a handler for text messages.
11
+ */
12
+ export function createTextHandler(ctx) {
13
+ return async (gramCtx) => {
14
+ const { config, logger } = ctx;
15
+ const userId = gramCtx.from?.id;
16
+ const messageText = gramCtx.message?.text;
17
+ if (!userId || !messageText) {
18
+ return;
19
+ }
20
+ logger.debug({
21
+ userId,
22
+ username: gramCtx.from?.username,
23
+ name: gramCtx.from?.first_name,
24
+ }, "Message received");
25
+ // ── Slash command interception ────────────────────────────────────────────
26
+ if (messageText.startsWith("/")) {
27
+ // Parse command name: /deploy staging → "deploy", strip @botname suffix
28
+ const firstToken = messageText.slice(1).split(/\s+/)[0] ?? "";
29
+ const commandName = firstToken.split("@")[0];
30
+ const argsText = messageText.slice(1 + firstToken.length).trim();
31
+ const args = argsText ? argsText.split(/\s+/) : [];
32
+ if (commandName) {
33
+ const filePath = resolveCommandPath(commandName, config.cwd, config.configDir);
34
+ if (filePath !== null) {
35
+ try {
36
+ const context = await resolveContext({
37
+ gramCtx,
38
+ configContext: config.context,
39
+ bootContext: ctx.bootContext,
40
+ configDir: config.configDir,
41
+ projectCwd: config.cwd,
42
+ projectName: config.name,
43
+ projectSlug: config.slug,
44
+ logger,
45
+ });
46
+ const agent = createAgent(ctx);
47
+ // Cache-bust on every dispatch call
48
+ const mod = await import(`${filePath}?t=${Date.now()}`);
49
+ const result = await mod.default({
50
+ args,
51
+ ctx: context,
52
+ gram: gramCtx,
53
+ agent,
54
+ projectCtx: ctx,
55
+ });
56
+ if (typeof result === "string") {
57
+ await sendChunkedResponse(gramCtx, result);
58
+ }
59
+ }
60
+ catch (err) {
61
+ logger.error({
62
+ commandName,
63
+ filePath,
64
+ error: err instanceof Error ? err.message : String(err),
65
+ stack: err instanceof Error ? err.stack : undefined,
66
+ }, "Command execution failed");
67
+ const errorMessage = err instanceof Error ? err.message : String(err);
68
+ await gramCtx.reply(`Command failed: ${errorMessage}`);
69
+ }
70
+ return;
71
+ }
72
+ // No .mjs handler — check if this is a registered skill
73
+ const skillEntry = await resolveSkillEntry(commandName, getSkillsDir(config.cwd), logger);
74
+ if (skillEntry?.skillPrompt) {
75
+ const prompt = args.length > 0
76
+ ? `${skillEntry.skillPrompt}\n\nUser input: ${args.join(" ")}`
77
+ : skillEntry.skillPrompt;
78
+ const agent = createAgent(ctx);
79
+ const statusMsg = await gramCtx.reply("_Processing..._", {
80
+ parse_mode: "Markdown",
81
+ });
82
+ let lastProgressUpdate = Date.now();
83
+ try {
84
+ const result = await agent.call(prompt, {
85
+ onProgress: async (message) => {
86
+ const now = Date.now();
87
+ if (now - lastProgressUpdate > 2000) {
88
+ lastProgressUpdate = now;
89
+ try {
90
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
91
+ }
92
+ catch {
93
+ // Ignore edit errors
94
+ }
95
+ }
96
+ },
97
+ });
98
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
99
+ await sendChunkedResponse(gramCtx, result);
100
+ }
101
+ catch (err) {
102
+ logger.error({
103
+ commandName,
104
+ error: err instanceof Error ? err.message : String(err),
105
+ stack: err instanceof Error ? err.stack : undefined,
106
+ }, "Skill execution failed");
107
+ try {
108
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
109
+ }
110
+ catch {
111
+ // Ignore delete errors
112
+ }
113
+ const errorMessage = err instanceof Error ? err.message : String(err);
114
+ await gramCtx.reply(`Skill failed: ${errorMessage}`);
115
+ }
116
+ return;
117
+ }
118
+ // Not a .mjs command or skill — fall through to Claude
119
+ }
120
+ }
121
+ // ── End slash command interception ────────────────────────────────────────
122
+ const userDir = resolve(join(config.dataDir, String(userId)));
123
+ try {
124
+ await ensureUserSetup(userDir);
125
+ if (!messageText.trim()) {
126
+ await gramCtx.reply("Please provide a message.");
127
+ return;
128
+ }
129
+ const sessionId = await getSessionId(userDir);
130
+ logger.debug({ sessionId: sessionId || "new" }, "Session");
131
+ const statusMsg = await gramCtx.reply("_Processing..._", {
132
+ parse_mode: "Markdown",
133
+ });
134
+ let lastProgressUpdate = Date.now();
135
+ let lastProgressText = "Processing...";
136
+ const onProgress = async (message) => {
137
+ const now = Date.now();
138
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
139
+ lastProgressUpdate = now;
140
+ lastProgressText = message;
141
+ try {
142
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
143
+ }
144
+ catch {
145
+ // Ignore edit errors
146
+ }
147
+ }
148
+ };
149
+ const downloadsPath = getDownloadsPath(userDir);
150
+ logger.debug("Executing Claude query");
151
+ const result = await executeClaudeQuery({
152
+ prompt: messageText,
153
+ gramCtx,
154
+ userDir,
155
+ downloadsPath,
156
+ sessionId,
157
+ onProgress,
158
+ }, ctx);
159
+ logger.debug({ success: result.success, error: result.error }, "Claude result");
160
+ try {
161
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
162
+ }
163
+ catch {
164
+ // Ignore delete errors
165
+ }
166
+ if (result.sessionId) {
167
+ await saveSessionId(userDir, result.sessionId);
168
+ logger.debug({ sessionId: result.sessionId }, "Session saved");
169
+ }
170
+ const responseText = result.success
171
+ ? result.output
172
+ : result.error || "An error occurred";
173
+ await sendChunkedResponse(gramCtx, responseText);
174
+ const filesSent = await sendDownloadFiles(gramCtx, userDir, ctx);
175
+ if (filesSent > 0) {
176
+ logger.info({ filesSent }, "Sent download files to user");
177
+ }
178
+ }
179
+ catch (error) {
180
+ logger.error({ error }, "Text handler error");
181
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
182
+ await gramCtx.reply(`An error occurred: ${errorMessage}`);
183
+ }
184
+ };
185
+ }
186
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/bot/handlers/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE9E;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAmB;IACnD,OAAO,KAAK,EAAE,OAAgB,EAAiB,EAAE;QAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;QAE1C,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,MAAM,CAAC,KAAK,CACV;YACE,MAAM;YACN,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU;SAC/B,EACD,kBAAkB,CACnB,CAAC;QAEF,6EAA6E;QAC7E,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,wEAAwE;YACxE,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9D,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAEnD,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,QAAQ,GAAG,kBAAkB,CACjC,WAAW,EACX,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,SAAS,CACjB,CAAC;gBAEF,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;oBACtB,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC;4BACnC,OAAO;4BACP,aAAa,EAAE,MAAM,CAAC,OAAO;4BAC7B,WAAW,EAAE,GAAG,CAAC,WAAW;4BAC5B,SAAS,EAAE,MAAM,CAAC,SAAS;4BAC3B,UAAU,EAAE,MAAM,CAAC,GAAG;4BACtB,WAAW,EAAE,MAAM,CAAC,IAAI;4BACxB,WAAW,EAAE,MAAM,CAAC,IAAI;4BACxB,MAAM;yBACP,CAAC,CAAC;wBACH,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;wBAC/B,oCAAoC;wBACpC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,QAAQ,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;wBACxD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC;4BAC/B,IAAI;4BACJ,GAAG,EAAE,OAAO;4BACZ,IAAI,EAAE,OAAO;4BACb,KAAK;4BACL,UAAU,EAAE,GAAG;yBAChB,CAAC,CAAC;wBACH,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;4BAC/B,MAAM,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;wBAC7C,CAAC;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,KAAK,CACV;4BACE,WAAW;4BACX,QAAQ;4BACR,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;4BACvD,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;yBACpD,EACD,0BAA0B,CAC3B,CAAC;wBACF,MAAM,YAAY,GAChB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;wBACnD,MAAM,OAAO,CAAC,KAAK,CAAC,mBAAmB,YAAY,EAAE,CAAC,CAAC;oBACzD,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,wDAAwD;gBACxD,MAAM,UAAU,GAAG,MAAM,iBAAiB,CACxC,WAAW,EACX,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,EACxB,MAAM,CACP,CAAC;gBAEF,IAAI,UAAU,EAAE,WAAW,EAAE,CAAC;oBAC5B,MAAM,MAAM,GACV,IAAI,CAAC,MAAM,GAAG,CAAC;wBACb,CAAC,CAAC,GAAG,UAAU,CAAC,WAAW,mBAAmB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;wBAC9D,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC;oBAE7B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;oBAC/B,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE;wBACvD,UAAU,EAAE,UAAU;qBACvB,CAAC,CAAC;oBACH,IAAI,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBAEpC,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE;4BACtC,UAAU,EAAE,KAAK,EAAE,OAAe,EAAE,EAAE;gCACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gCACvB,IAAI,GAAG,GAAG,kBAAkB,GAAG,IAAI,EAAE,CAAC;oCACpC,kBAAkB,GAAG,GAAG,CAAC;oCACzB,IAAI,CAAC;wCACH,MAAM,OAAO,CAAC,GAAG,CAAC,eAAe,CAC/B,OAAO,CAAC,IAAK,CAAC,EAAE,EAChB,SAAS,CAAC,UAAU,EACpB,IAAI,OAAO,GAAG,EACd,EAAE,UAAU,EAAE,UAAU,EAAE,CAC3B,CAAC;oCACJ,CAAC;oCAAC,MAAM,CAAC;wCACP,qBAAqB;oCACvB,CAAC;gCACH,CAAC;4BACH,CAAC;yBACF,CAAC,CAAC;wBACH,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAC7B,OAAO,CAAC,IAAK,CAAC,EAAE,EAChB,SAAS,CAAC,UAAU,CACrB,CAAC;wBACF,MAAM,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC7C,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,KAAK,CACV;4BACE,WAAW;4BACX,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;4BACvD,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;yBACpD,EACD,wBAAwB,CACzB,CAAC;wBACF,IAAI,CAAC;4BACH,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAC7B,OAAO,CAAC,IAAK,CAAC,EAAE,EAChB,SAAS,CAAC,UAAU,CACrB,CAAC;wBACJ,CAAC;wBAAC,MAAM,CAAC;4BACP,uBAAuB;wBACzB,CAAC;wBACD,MAAM,YAAY,GAChB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;wBACnD,MAAM,OAAO,CAAC,KAAK,CAAC,iBAAiB,YAAY,EAAE,CAAC,CAAC;oBACvD,CAAC;oBACD,OAAO;gBACT,CAAC;gBACD,uDAAuD;YACzD,CAAC;QACH,CAAC;QACD,6EAA6E;QAE7E,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAE9D,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;YAE/B,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;gBACxB,MAAM,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,IAAI,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;YAE3D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE;gBACvD,UAAU,EAAE,UAAU;aACvB,CAAC,CAAC;YACH,IAAI,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,IAAI,gBAAgB,GAAG,eAAe,CAAC;YAEvC,MAAM,UAAU,GAAG,KAAK,EAAE,OAAe,EAAE,EAAE;gBAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACvB,IAAI,GAAG,GAAG,kBAAkB,GAAG,IAAI,IAAI,OAAO,KAAK,gBAAgB,EAAE,CAAC;oBACpE,kBAAkB,GAAG,GAAG,CAAC;oBACzB,gBAAgB,GAAG,OAAO,CAAC;oBAC3B,IAAI,CAAC;wBACH,MAAM,OAAO,CAAC,GAAG,CAAC,eAAe,CAC/B,OAAO,CAAC,IAAK,CAAC,EAAE,EAChB,SAAS,CAAC,UAAU,EACpB,IAAI,OAAO,GAAG,EACd,EAAE,UAAU,EAAE,UAAU,EAAE,CAC3B,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,qBAAqB;oBACvB,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAEhD,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC;gBACE,MAAM,EAAE,WAAW;gBACnB,OAAO;gBACP,OAAO;gBACP,aAAa;gBACb,SAAS;gBACT,UAAU;aACX,EACD,GAAG,CACJ,CAAC;YACF,MAAM,CAAC,KAAK,CACV,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAChD,eAAe,CAChB,CAAC;YAEF,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,IAAK,CAAC,EAAE,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;YAC1E,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;YAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,MAAM,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC/C,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,eAAe,CAAC,CAAC;YACjE,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO;gBACjC,CAAC,CAAC,MAAM,CAAC,MAAM;gBACf,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,mBAAmB,CAAC;YACxC,MAAM,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAEjD,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YACjE,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,6BAA6B,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,oBAAoB,CAAC,CAAC;YAC9C,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YAC3D,MAAM,OAAO,CAAC,KAAK,CAAC,sBAAsB,YAAY,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Context } from "grammy";
2
+ import type { ProjectContext } from "../../types.js";
3
+ /**
4
+ * Returns a handler for voice messages (transcribe + route to Claude).
5
+ */
6
+ export declare function createVoiceHandler(ctx: ProjectContext): (gramCtx: Context) => Promise<void>;
7
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.ts","sourceRoot":"","sources":["../../../src/bot/handlers/voice.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiCrD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,IACtC,SAAS,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC,CAsK/C"}
@@ -0,0 +1,147 @@
1
+ import { exec } from "node:child_process";
2
+ import { unlink, writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { executeClaudeQuery } from "../../claude/executor.js";
6
+ import { parseClaudeOutput } from "../../claude/parser.js";
7
+ import { sendChunkedResponse } from "../../telegram/chunker.js";
8
+ import { sendDownloadFiles } from "../../telegram/fileSender.js";
9
+ import { transcribeAudio } from "../../transcription/whisper.js";
10
+ import { ensureUserSetup, getDownloadsPath, getSessionId, getUploadsPath, saveSessionId, } from "../../user/setup.js";
11
+ const execAsync = promisify(exec);
12
+ /**
13
+ * Convert OGA/OGG (Opus) to WAV for Whisper compatibility
14
+ */
15
+ async function convertToWav(inputPath, outputPath, ctx) {
16
+ const { logger } = ctx;
17
+ try {
18
+ await execAsync(`ffmpeg -i "${inputPath}" -ar 16000 -ac 1 -y "${outputPath}"`);
19
+ logger.debug({ inputPath, outputPath }, "Audio converted to WAV");
20
+ }
21
+ catch (error) {
22
+ logger.error({ error }, "ffmpeg conversion failed");
23
+ throw new Error("Failed to convert audio. Ensure ffmpeg is installed: brew install ffmpeg");
24
+ }
25
+ }
26
+ /**
27
+ * Returns a handler for voice messages (transcribe + route to Claude).
28
+ */
29
+ export function createVoiceHandler(ctx) {
30
+ return async (gramCtx) => {
31
+ const { config, logger } = ctx;
32
+ if (!config.transcription) {
33
+ await gramCtx.reply("Voice messages are not configured for this bot. Add a 'transcription' section to your project config.");
34
+ return;
35
+ }
36
+ const userId = gramCtx.from?.id;
37
+ const voice = gramCtx.message?.voice;
38
+ if (!userId || !voice) {
39
+ return;
40
+ }
41
+ logger.debug({ userId, duration: voice.duration, fileSize: voice.file_size }, "Voice message received");
42
+ const userDir = resolve(join(config.dataDir, String(userId)));
43
+ try {
44
+ await ensureUserSetup(userDir);
45
+ // Download voice file from Telegram
46
+ const file = await gramCtx.api.getFile(voice.file_id);
47
+ const filePath = file.file_path;
48
+ if (!filePath) {
49
+ await gramCtx.reply("Could not download the voice message.");
50
+ return;
51
+ }
52
+ const fileUrl = `https://api.telegram.org/file/bot${config.telegram.botToken}/${filePath}`;
53
+ const response = await fetch(fileUrl);
54
+ const buffer = Buffer.from(await response.arrayBuffer());
55
+ const timestamp = Date.now();
56
+ const uploadsDir = getUploadsPath(userDir);
57
+ const ogaPath = join(uploadsDir, `voice_${timestamp}.oga`);
58
+ const wavPath = join(uploadsDir, `voice_${timestamp}.wav`);
59
+ await writeFile(ogaPath, buffer);
60
+ logger.debug({ path: ogaPath }, "Voice file saved");
61
+ const statusMsg = await gramCtx.reply("_Transcribing voice message..._", {
62
+ parse_mode: "Markdown",
63
+ });
64
+ // Convert to WAV (Whisper requires WAV/MP3 input)
65
+ await convertToWav(ogaPath, wavPath, ctx);
66
+ // Transcribe with local Whisper
67
+ const transcription = await transcribeAudio(wavPath, ctx);
68
+ if (!transcription.text) {
69
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
70
+ await gramCtx.reply("Could not transcribe the voice message. Please try again.");
71
+ return;
72
+ }
73
+ // Optionally show transcription to user
74
+ if (config.transcription.showTranscription) {
75
+ try {
76
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_Transcribed: "${transcription.text}"_\n\n_Processing with Claude..._`, { parse_mode: "Markdown" });
77
+ }
78
+ catch {
79
+ // Ignore edit errors
80
+ }
81
+ }
82
+ else {
83
+ try {
84
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, "_Processing..._", { parse_mode: "Markdown" });
85
+ }
86
+ catch {
87
+ // Ignore edit errors
88
+ }
89
+ }
90
+ // Clean up temporary files
91
+ try {
92
+ await unlink(ogaPath);
93
+ await unlink(wavPath);
94
+ }
95
+ catch {
96
+ // Ignore cleanup errors
97
+ }
98
+ const sessionId = await getSessionId(userDir);
99
+ let lastProgressUpdate = Date.now();
100
+ let lastProgressText = "Processing...";
101
+ const onProgress = async (message) => {
102
+ const now = Date.now();
103
+ if (now - lastProgressUpdate > 2000 && message !== lastProgressText) {
104
+ lastProgressUpdate = now;
105
+ lastProgressText = message;
106
+ try {
107
+ await gramCtx.api.editMessageText(gramCtx.chat.id, statusMsg.message_id, `_${message}_`, { parse_mode: "Markdown" });
108
+ }
109
+ catch {
110
+ // Ignore edit errors
111
+ }
112
+ }
113
+ };
114
+ const downloadsPath = getDownloadsPath(userDir);
115
+ logger.debug({ transcription: transcription.text }, "Executing Claude query");
116
+ const result = await executeClaudeQuery({
117
+ prompt: transcription.text,
118
+ gramCtx,
119
+ userDir,
120
+ downloadsPath,
121
+ sessionId,
122
+ onProgress,
123
+ }, ctx);
124
+ try {
125
+ await gramCtx.api.deleteMessage(gramCtx.chat.id, statusMsg.message_id);
126
+ }
127
+ catch {
128
+ // Ignore delete errors
129
+ }
130
+ const parsed = parseClaudeOutput(result);
131
+ if (parsed.sessionId) {
132
+ await saveSessionId(userDir, parsed.sessionId);
133
+ }
134
+ await sendChunkedResponse(gramCtx, parsed.text);
135
+ const filesSent = await sendDownloadFiles(gramCtx, userDir, ctx);
136
+ if (filesSent > 0) {
137
+ logger.info({ filesSent }, "Sent download files to user");
138
+ }
139
+ }
140
+ catch (error) {
141
+ logger.error({ error }, "Voice handler error");
142
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
143
+ await gramCtx.reply(`An error occurred processing the voice message: ${errorMessage}`);
144
+ }
145
+ };
146
+ }
147
+ //# sourceMappingURL=voice.js.map