@gonzih/cc-tg 0.8.2 → 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
@@ -23,13 +23,10 @@ export declare class CcTgBot {
23
23
  private botId;
24
24
  private redis?;
25
25
  private namespace;
26
- private lastActiveChatId?;
27
26
  constructor(opts: BotOptions);
28
27
  private registerBotCommands;
29
28
  /** Write a message to the Redis chat log. Fire-and-forget — no-op if Redis is not configured. */
30
29
  private writeChatMessage;
31
- /** Returns the last chatId that sent a message — used by the chat bridge when no fixed chatId is configured. */
32
- getLastActiveChatId(): number | undefined;
33
30
  /** Session key: "chatId:threadId" for topics, "chatId:main" for DMs/non-topic groups */
34
31
  private sessionKey;
35
32
  /**
package/dist/bot.js CHANGED
@@ -156,7 +156,6 @@ export class CcTgBot {
156
156
  botId = 0;
157
157
  redis;
158
158
  namespace;
159
- lastActiveChatId;
160
159
  constructor(opts) {
161
160
  this.opts = opts;
162
161
  this.redis = opts.redis;
@@ -193,10 +192,6 @@ export class CcTgBot {
193
192
  };
194
193
  writeChatLog(this.redis, this.namespace, msg);
195
194
  }
196
- /** Returns the last chatId that sent a message — used by the chat bridge when no fixed chatId is configured. */
197
- getLastActiveChatId() {
198
- return this.lastActiveChatId;
199
- }
200
195
  /** Session key: "chatId:threadId" for topics, "chatId:main" for DMs/non-topic groups */
201
196
  sessionKey(chatId, threadId) {
202
197
  return `${chatId}:${threadId ?? 'main'}`;
@@ -248,8 +243,6 @@ export class CcTgBot {
248
243
  await this.replyToChat(chatId, "Not authorized.", threadId);
249
244
  return;
250
245
  }
251
- // Track the last chat that sent us a message for the chat bridge
252
- this.lastActiveChatId = chatId;
253
246
  // Group chat handling
254
247
  const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
255
248
  if (isGroup) {
package/dist/index.js CHANGED
@@ -113,26 +113,20 @@ const groupChatIds = process.env.GROUP_CHAT_IDS
113
113
  ? process.env.GROUP_CHAT_IDS.split(",").map((s) => parseInt(s.trim(), 10)).filter(Boolean)
114
114
  : [];
115
115
  const cwd = process.env.CWD ?? process.cwd();
116
- // agent-ops / chat bridge — Redis is always initialized so the chat bridge works
117
- // regardless of whether CC_AGENT_OPS_PORT or CC_AGENT_NOTIFY_CHAT_ID are set.
118
- const redisUrl = process.env.REDIS_URL || "redis://localhost:6379";
119
- const namespace = process.env.CC_AGENT_NAMESPACE || "default";
120
- const sharedRedis = new Redis(redisUrl);
121
- sharedRedis.on("error", (err) => {
122
- // Non-fatal — Redis features (chat bridge, ops) degrade gracefully
123
- console.warn("[redis] connection error:", err.message);
124
- });
125
116
  const bot = new CcTgBot({
126
117
  telegramToken,
127
118
  claudeToken,
128
119
  cwd,
129
120
  allowedUserIds,
130
121
  groupChatIds,
131
- redis: sharedRedis,
132
- namespace,
133
122
  });
123
+ // agent-ops: optional self-registration + HTTP control endpoint
124
+ const redisUrl = process.env.REDIS_URL || "redis://localhost:6379";
125
+ const namespace = process.env.CC_AGENT_NAMESPACE || "default";
126
+ let sharedRedis = null;
134
127
  if (process.env.CC_AGENT_OPS_PORT) {
135
128
  const botInfo = await bot.getMe();
129
+ sharedRedis = new Redis(redisUrl);
136
130
  const registry = new Registry(sharedRedis);
137
131
  await registry.register({
138
132
  namespace,
@@ -153,15 +147,17 @@ if (process.env.CC_AGENT_OPS_PORT) {
153
147
  });
154
148
  console.log(`[ops] control server on port ${process.env.CC_AGENT_OPS_PORT}`);
155
149
  }
156
- // Notifier — always subscribe to cca:notify and cca:chat:incoming channels.
157
- // CC_AGENT_NOTIFY_CHAT_ID pins a fixed Telegram chatId; without it the last
158
- // active chatId is used dynamically for the chat bridge.
150
+ // Notifier — subscribe to cca:notify:{namespace} and cca:chat:incoming:{namespace}
159
151
  const notifyChatId = process.env.CC_AGENT_NOTIFY_CHAT_ID
160
152
  ? Number(process.env.CC_AGENT_NOTIFY_CHAT_ID)
161
153
  : null;
162
- const notifierBot = new TelegramBot(telegramToken, { polling: false });
163
- startNotifier(notifierBot, notifyChatId, namespace, sharedRedis, (cid, text) => bot.handleUserMessage(cid, text), () => bot.getLastActiveChatId());
164
- console.log(`[notifier] started for namespace=${namespace} chatId=${notifyChatId ?? "dynamic"}`);
154
+ if (notifyChatId) {
155
+ if (!sharedRedis)
156
+ sharedRedis = new Redis(redisUrl);
157
+ const notifierBot = new TelegramBot(telegramToken, { polling: false });
158
+ startNotifier(notifierBot, notifyChatId, namespace, sharedRedis, (cid, text) => bot.handleUserMessage(cid, text));
159
+ console.log(`[notifier] started for namespace=${namespace} chatId=${notifyChatId}`);
160
+ }
165
161
  process.on("SIGINT", () => {
166
162
  console.log("\nShutting down...");
167
163
  bot.stop();
@@ -28,10 +28,9 @@ export declare function writeChatLog(redis: Redis, namespace: string, msg: ChatM
28
28
  * Start the notifier.
29
29
  *
30
30
  * @param bot - Telegram bot instance (for sending messages)
31
- * @param chatId - Telegram chat ID to forward notifications to. Pass null to use getActiveChatId.
31
+ * @param chatId - Telegram chat ID to forward notifications to
32
32
  * @param namespace - cc-agent namespace (used to build Redis channel names)
33
33
  * @param redis - ioredis client in normal mode (will be duplicated for pub/sub)
34
34
  * @param handleUserMessage - Optional callback to feed UI messages into the active Claude session
35
- * @param getActiveChatId - Optional callback to resolve chatId dynamically (used when chatId is null)
36
35
  */
37
- export declare function startNotifier(bot: TelegramBot, chatId: number | null, namespace: string, redis: Redis, handleUserMessage?: (chatId: number, text: string) => void, getActiveChatId?: () => number | undefined): void;
36
+ export declare function startNotifier(bot: TelegramBot, chatId: number, namespace: string, redis: Redis, handleUserMessage?: (chatId: number, text: string) => void): void;
package/dist/notifier.js CHANGED
@@ -35,13 +35,12 @@ export function writeChatLog(redis, namespace, msg) {
35
35
  * Start the notifier.
36
36
  *
37
37
  * @param bot - Telegram bot instance (for sending messages)
38
- * @param chatId - Telegram chat ID to forward notifications to. Pass null to use getActiveChatId.
38
+ * @param chatId - Telegram chat ID to forward notifications to
39
39
  * @param namespace - cc-agent namespace (used to build Redis channel names)
40
40
  * @param redis - ioredis client in normal mode (will be duplicated for pub/sub)
41
41
  * @param handleUserMessage - Optional callback to feed UI messages into the active Claude session
42
- * @param getActiveChatId - Optional callback to resolve chatId dynamically (used when chatId is null)
43
42
  */
44
- export function startNotifier(bot, chatId, namespace, redis, handleUserMessage, getActiveChatId) {
43
+ export function startNotifier(bot, chatId, namespace, redis, handleUserMessage) {
45
44
  const sub = redis.duplicate({
46
45
  retryStrategy: (times) => {
47
46
  const delay = Math.min(1000 * Math.pow(2, times - 1), 30_000);
@@ -77,11 +76,9 @@ export function startNotifier(bot, chatId, namespace, redis, handleUserMessage,
77
76
  const notifyChannel = `cca:notify:${namespace}`;
78
77
  const incomingChannel = `cca:chat:incoming:${namespace}`;
79
78
  if (channel === notifyChannel) {
80
- if (chatId !== null) {
81
- bot.sendMessage(chatId, message).catch((err) => {
82
- log("warn", "sendMessage failed:", err.message);
83
- });
84
- }
79
+ bot.sendMessage(chatId, message).catch((err) => {
80
+ log("warn", "sendMessage failed:", err.message);
81
+ });
85
82
  return;
86
83
  }
87
84
  if (channel === incomingChannel) {
@@ -94,30 +91,23 @@ export function startNotifier(bot, chatId, namespace, redis, handleUserMessage,
94
91
  catch {
95
92
  // raw string message — use as-is
96
93
  }
97
- // Resolve the target chatId: prefer the fixed chatId, fall back to last active
98
- const targetChatId = chatId ?? getActiveChatId?.();
99
- if (targetChatId !== undefined) {
100
- // Echo to Telegram so the user sees UI messages in the chat
101
- bot.sendMessage(targetChatId, `📱 [from UI]: ${content}`).catch((err) => {
102
- log("warn", "sendMessage (UI echo) failed:", err.message);
103
- });
104
- // Log the incoming message
105
- const inMsg = {
106
- id: `ui-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
107
- source: "ui",
108
- role: "user",
109
- content,
110
- timestamp: new Date().toISOString(),
111
- chatId: targetChatId,
112
- };
113
- writeChatLog(redis, namespace, inMsg);
114
- // Feed into active Claude session as if user typed it
115
- if (handleUserMessage) {
116
- handleUserMessage(targetChatId, content);
117
- }
118
- }
119
- else {
120
- log("warn", "cca:chat:incoming: no active chatId to route message to");
94
+ // Echo to Telegram so the user sees UI messages in the chat
95
+ bot.sendMessage(chatId, `📱 [from UI]: ${content}`).catch((err) => {
96
+ log("warn", "sendMessage (UI echo) failed:", err.message);
97
+ });
98
+ // Log the incoming message
99
+ const inMsg = {
100
+ id: `ui-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
101
+ source: "ui",
102
+ role: "user",
103
+ content,
104
+ timestamp: new Date().toISOString(),
105
+ chatId,
106
+ };
107
+ writeChatLog(redis, namespace, inMsg);
108
+ // Feed into active Claude session as if user typed it
109
+ if (handleUserMessage) {
110
+ handleUserMessage(chatId, content);
121
111
  }
122
112
  }
123
113
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.8.2",
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": {