@gonzih/cc-tg 0.8.1 → 0.9.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/dist/bot.d.ts CHANGED
@@ -3,12 +3,15 @@
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 { Redis } from "ioredis";
6
7
  export interface BotOptions {
7
8
  telegramToken: string;
8
9
  claudeToken?: string;
9
10
  cwd?: string;
10
11
  allowedUserIds?: number[];
11
12
  groupChatIds?: number[];
13
+ redis?: Redis;
14
+ namespace?: string;
12
15
  }
13
16
  export declare class CcTgBot {
14
17
  private bot;
@@ -18,8 +21,12 @@ export declare class CcTgBot {
18
21
  private costStore;
19
22
  private botUsername;
20
23
  private botId;
24
+ private redis?;
25
+ private namespace;
21
26
  constructor(opts: BotOptions);
22
27
  private registerBotCommands;
28
+ /** Write a message to the Redis chat log. Fire-and-forget — no-op if Redis is not configured. */
29
+ private writeChatMessage;
23
30
  /** Session key: "chatId:threadId" for topics, "chatId:main" for DMs/non-topic groups */
24
31
  private sessionKey;
25
32
  /**
package/dist/bot.js CHANGED
@@ -14,6 +14,7 @@ import { transcribeVoice, isVoiceAvailable } from "./voice.js";
14
14
  import { formatForTelegram, splitLongMessage } from "./formatter.js";
15
15
  import { detectUsageLimit } from "./usage-limit.js";
16
16
  import { getCurrentToken, rotateToken, getTokenIndex, getTokenCount } from "./tokens.js";
17
+ import { writeChatLog } from "./notifier.js";
17
18
  const BOT_COMMANDS = [
18
19
  { command: "start", description: "Reset session and start fresh" },
19
20
  { command: "reset", description: "Reset Claude session" },
@@ -153,8 +154,12 @@ export class CcTgBot {
153
154
  costStore;
154
155
  botUsername = "";
155
156
  botId = 0;
157
+ redis;
158
+ namespace;
156
159
  constructor(opts) {
157
160
  this.opts = opts;
161
+ this.redis = opts.redis;
162
+ this.namespace = opts.namespace ?? "default";
158
163
  this.bot = new TelegramBot(opts.telegramToken, { polling: true });
159
164
  this.bot.on("message", (msg) => this.handleTelegram(msg));
160
165
  this.bot.on("polling_error", (err) => console.error("[tg]", err.message));
@@ -173,6 +178,20 @@ export class CcTgBot {
173
178
  .then(() => console.log("[tg] bot commands registered"))
174
179
  .catch((err) => console.error("[tg] setMyCommands failed:", err.message));
175
180
  }
181
+ /** Write a message to the Redis chat log. Fire-and-forget — no-op if Redis is not configured. */
182
+ writeChatMessage(role, source, content, chatId) {
183
+ if (!this.redis)
184
+ return;
185
+ const msg = {
186
+ id: `${source}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
187
+ source,
188
+ role,
189
+ content,
190
+ timestamp: new Date().toISOString(),
191
+ chatId,
192
+ };
193
+ writeChatLog(this.redis, this.namespace, msg);
194
+ }
176
195
  /** Session key: "chatId:threadId" for topics, "chatId:main" for DMs/non-topic groups */
177
196
  sessionKey(chatId, threadId) {
178
197
  return `${chatId}:${threadId ?? 'main'}`;
@@ -350,6 +369,7 @@ export class CcTgBot {
350
369
  session.currentPrompt = prompt;
351
370
  session.claude.sendPrompt(prompt);
352
371
  this.startTyping(chatId, session);
372
+ this.writeChatMessage("user", "telegram", text, chatId);
353
373
  }
354
374
  catch (err) {
355
375
  await this.replyToChat(chatId, `Error sending to Claude: ${err.message}`, threadId);
@@ -367,6 +387,7 @@ export class CcTgBot {
367
387
  session.currentPrompt = enriched;
368
388
  session.claude.sendPrompt(enriched);
369
389
  this.startTyping(chatId, session);
390
+ this.writeChatMessage("user", "ui", text, chatId);
370
391
  }
371
392
  catch (err) {
372
393
  await this.replyToChat(chatId, `Error sending to Claude: ${err.message}`);
@@ -495,6 +516,20 @@ export class CcTgBot {
495
516
  console.log(logParts.join(" "));
496
517
  // Track files written by Write/Edit tool calls
497
518
  this.trackWrittenFiles(msg, session, sessionCwd);
519
+ // Publish tool call events to the chat log
520
+ if (msg.type === "assistant") {
521
+ const message = msg.payload.message;
522
+ const content = message?.content;
523
+ if (Array.isArray(content)) {
524
+ for (const block of content) {
525
+ if (block.type !== "tool_use")
526
+ continue;
527
+ const name = block.name;
528
+ const input = block.input;
529
+ this.writeChatMessage("tool", "cc-tg", `[tool] ${name}: ${JSON.stringify(input ?? {}).slice(0, 120)}`, chatId);
530
+ }
531
+ }
532
+ }
498
533
  this.handleClaudeMessage(chatId, session, msg);
499
534
  });
500
535
  claude.on("stderr", (data) => {
@@ -611,6 +646,7 @@ export class CcTgBot {
611
646
  session.flushTimer = null;
612
647
  if (!raw)
613
648
  return;
649
+ this.writeChatMessage("assistant", "cc-tg", raw, chatId);
614
650
  const text = session.isRetry ? `✅ Claude is back!\n\n${raw}` : raw;
615
651
  session.isRetry = false;
616
652
  // Format for Telegram HTML and split if needed (max 4096 chars)
@@ -13,8 +13,8 @@ import { Redis } from "ioredis";
13
13
  import TelegramBot from "node-telegram-bot-api";
14
14
  export interface ChatMessage {
15
15
  id: string;
16
- source: "telegram" | "ui" | "claude";
17
- role: "user" | "assistant";
16
+ source: "telegram" | "ui" | "claude" | "cc-tg";
17
+ role: "user" | "assistant" | "tool";
18
18
  content: string;
19
19
  timestamp: string;
20
20
  chatId: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Claude Code Telegram bot — chat with Claude Code via Telegram",
5
5
  "type": "module",
6
6
  "bin": {