@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.
Files changed (2) hide show
  1. package/dist/bot.js +61 -10
  2. 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: "Get a file from the server by path" },
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
- // yt-dlp / ffmpeg -o "path"
362
- const oFlag = cmd.match(/-o\s+["']?([^\s"']+\.[\w]{1,10})["']?/);
363
- if (oFlag)
364
- session.writtenFiles.add(resolve(cwd ?? process.cwd(), oFlag[1]));
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, /auth/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
- const { statSync } = await import("fs");
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Claude Code Telegram bot — chat with Claude Code via Telegram",
5
5
  "type": "module",
6
6
  "bin": {