@gonzih/cc-tg 0.2.12 → 0.2.14
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/dist/bot.js +61 -10
- package/package.json +1 -1
package/dist/bot.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* One ClaudeProcess per chat_id — sessions are isolated per user.
|
|
4
4
|
*/
|
|
5
5
|
import TelegramBot from "node-telegram-bot-api";
|
|
6
|
-
import { existsSync, createWriteStream, mkdirSync } from "fs";
|
|
6
|
+
import { existsSync, createWriteStream, mkdirSync, statSync, readdirSync } from "fs";
|
|
7
7
|
import { resolve, basename, join } from "path";
|
|
8
8
|
import os from "os";
|
|
9
9
|
import { execSync } from "child_process";
|
|
@@ -23,7 +23,7 @@ const BOT_COMMANDS = [
|
|
|
23
23
|
{ command: "mcp_version", description: "Show cc-agent npm version and npx cache info" },
|
|
24
24
|
{ command: "clear_npx_cache", description: "Clear npx cache and restart MCP to pick up latest version" },
|
|
25
25
|
{ command: "restart", description: "Restart the bot process in-place" },
|
|
26
|
-
{ command: "get_file", description: "
|
|
26
|
+
{ command: "get_file", description: "Send a file from the server to this chat" },
|
|
27
27
|
];
|
|
28
28
|
const FLUSH_DELAY_MS = 800; // debounce streaming chunks into one Telegram message
|
|
29
29
|
const TYPING_INTERVAL_MS = 4000; // re-send typing action before Telegram's 5s expiry
|
|
@@ -358,10 +358,45 @@ export class CcTgBot {
|
|
|
358
358
|
}
|
|
359
359
|
else if (name === "Bash") {
|
|
360
360
|
const cmd = input.command ?? "";
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
361
|
+
if (/\byt-dlp\b|\bffmpeg\b/.test(cmd)) {
|
|
362
|
+
// Scan output dir for recently modified media files (template paths like /tmp/%(title)s.%(ext)s
|
|
363
|
+
// make the actual filename unknowable at tracking time)
|
|
364
|
+
const oFlagMatch = cmd.match(/-o\s+["']?([^\s"']+)/);
|
|
365
|
+
let scanDir = "/tmp/";
|
|
366
|
+
if (oFlagMatch) {
|
|
367
|
+
const oPath = oFlagMatch[1].replace(/["'].*$/, "");
|
|
368
|
+
const dirEnd = oPath.lastIndexOf("/");
|
|
369
|
+
if (dirEnd > 0)
|
|
370
|
+
scanDir = oPath.slice(0, dirEnd + 1);
|
|
371
|
+
}
|
|
372
|
+
const MEDIA_EXTS = new Set([".mp3", ".mp4", ".wav", ".ogg", ".flac", ".webm", ".m4a", ".aac"]);
|
|
373
|
+
const nowMs = Date.now();
|
|
374
|
+
try {
|
|
375
|
+
for (const entry of readdirSync(scanDir)) {
|
|
376
|
+
const dotIdx = entry.lastIndexOf(".");
|
|
377
|
+
if (dotIdx < 0)
|
|
378
|
+
continue;
|
|
379
|
+
const ext = entry.slice(dotIdx).toLowerCase();
|
|
380
|
+
if (!MEDIA_EXTS.has(ext))
|
|
381
|
+
continue;
|
|
382
|
+
const full = join(scanDir, entry);
|
|
383
|
+
try {
|
|
384
|
+
if (nowMs - statSync(full).mtimeMs <= 90_000) {
|
|
385
|
+
console.log(`[claude:files] tracked yt-dlp/ffmpeg output: ${full}`);
|
|
386
|
+
session.writtenFiles.add(full);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch { /* skip unreadable entries */ }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch { /* scanDir doesn't exist or unreadable */ }
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// Other bash commands: try to extract output path from -o flag
|
|
396
|
+
const oFlag = cmd.match(/-o\s+["']?([^\s"']+\.[\w]{1,10})["']?/);
|
|
397
|
+
if (oFlag)
|
|
398
|
+
session.writtenFiles.add(resolve(cwd ?? process.cwd(), oFlag[1]));
|
|
399
|
+
}
|
|
365
400
|
// mv source dest — track dest
|
|
366
401
|
const mvMatch = cmd.match(/\bmv\s+\S+\s+["']?([^\s"']+)["']?$/);
|
|
367
402
|
if (mvMatch)
|
|
@@ -387,7 +422,7 @@ export class CcTgBot {
|
|
|
387
422
|
/credential/i, /secret/i, /password/i, /passwd/i, /\.env/i,
|
|
388
423
|
/api[_-]?key/i, /token/i, /private[_-]?key/i, /id_rsa/i,
|
|
389
424
|
/\.pem$/i, /\.key$/i, /\.pfx$/i, /\.p12$/i,
|
|
390
|
-
/gmail/i, /oauth/i, /
|
|
425
|
+
/gmail/i, /oauth/i, /\bauth\b/i,
|
|
391
426
|
];
|
|
392
427
|
return sensitivePatterns.some((p) => p.test(name));
|
|
393
428
|
}
|
|
@@ -395,11 +430,15 @@ export class CcTgBot {
|
|
|
395
430
|
// Extract file path candidates from result text
|
|
396
431
|
// Match: /absolute/path/file.ext or relative like ./foo/bar.csv or just foo.pdf
|
|
397
432
|
const pathPattern = /(?:^|[\s`'"(])(\/?[\w.\-/]+\.[\w]{1,10})(?:[\s`'")\n]|$)/gm;
|
|
433
|
+
const quotedPattern = /"([^"]+\.[a-zA-Z0-9]{1,10})"|'([^']+\.[a-zA-Z0-9]{1,10})'/g;
|
|
398
434
|
const candidates = new Set();
|
|
399
435
|
let match;
|
|
400
436
|
while ((match = pathPattern.exec(resultText)) !== null) {
|
|
401
437
|
candidates.add(match[1]);
|
|
402
438
|
}
|
|
439
|
+
while ((match = quotedPattern.exec(resultText)) !== null) {
|
|
440
|
+
candidates.add(match[1] ?? match[2]);
|
|
441
|
+
}
|
|
403
442
|
const safeDirs = ["/tmp/", "/var/folders/", os.homedir() + "/Downloads/"];
|
|
404
443
|
const isSafeDir = (p) => safeDirs.some(d => p.startsWith(d)) || p.startsWith(this.opts.cwd ?? process.cwd());
|
|
405
444
|
const toUpload = [];
|
|
@@ -440,6 +479,13 @@ export class CcTgBot {
|
|
|
440
479
|
console.log(`[claude:files] skipping sensitive file: ${filePath}`);
|
|
441
480
|
continue;
|
|
442
481
|
}
|
|
482
|
+
const fileSize = statSync(filePath).size;
|
|
483
|
+
const MAX_TG_FILE_BYTES = 50 * 1024 * 1024;
|
|
484
|
+
if (fileSize > MAX_TG_FILE_BYTES) {
|
|
485
|
+
const mb = (fileSize / (1024 * 1024)).toFixed(1);
|
|
486
|
+
this.bot.sendMessage(chatId, `File too large for Telegram (${mb}mb). Find it at: ${filePath}`).catch(() => { });
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
443
489
|
console.log(`[claude:files] uploading to telegram: ${filePath}`);
|
|
444
490
|
this.bot.sendDocument(chatId, filePath).catch((err) => console.error(`[tg:${chatId}] sendDocument failed for ${filePath}:`, err.message));
|
|
445
491
|
}
|
|
@@ -676,9 +722,7 @@ export class CcTgBot {
|
|
|
676
722
|
await this.bot.sendMessage(chatId, `File not found: ${filePath}`);
|
|
677
723
|
return;
|
|
678
724
|
}
|
|
679
|
-
|
|
680
|
-
const stat = statSync(filePath);
|
|
681
|
-
if (!stat.isFile()) {
|
|
725
|
+
if (!statSync(filePath).isFile()) {
|
|
682
726
|
await this.bot.sendMessage(chatId, `Not a file: ${filePath}`);
|
|
683
727
|
return;
|
|
684
728
|
}
|
|
@@ -686,6 +730,13 @@ export class CcTgBot {
|
|
|
686
730
|
await this.bot.sendMessage(chatId, "Access denied: sensitive file");
|
|
687
731
|
return;
|
|
688
732
|
}
|
|
733
|
+
const MAX_TG_FILE_BYTES = 50 * 1024 * 1024;
|
|
734
|
+
const fileSize = statSync(filePath).size;
|
|
735
|
+
if (fileSize > MAX_TG_FILE_BYTES) {
|
|
736
|
+
const mb = (fileSize / (1024 * 1024)).toFixed(1);
|
|
737
|
+
await this.bot.sendMessage(chatId, `File too large for Telegram (${mb}mb). Find it at: ${filePath}`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
689
740
|
await this.bot.sendDocument(chatId, filePath);
|
|
690
741
|
}
|
|
691
742
|
killSession(chatId, keepCrons = true) {
|