@gonzih/cc-discord 0.1.3 → 0.1.4
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 +1 -1
- package/dist/bot.js +11 -1
- package/dist/index.js +1 -1
- package/dist/notifier.d.ts +17 -11
- package/dist/notifier.js +39 -20
- package/package.json +1 -1
package/dist/bot.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export declare class CcDiscordBot {
|
|
|
36
36
|
/** Channels created by the bot for a meta-agent namespace → skip local Claude session */
|
|
37
37
|
private channelNamespaceMap;
|
|
38
38
|
private storeSnowflake;
|
|
39
|
-
|
|
39
|
+
reverseSnowflakeLookup(n: number): string | undefined;
|
|
40
40
|
/** Session key: "channelId" or "channelId:threadId" for threads */
|
|
41
41
|
private sessionKey;
|
|
42
42
|
/** Get the channel/thread for sending messages */
|
package/dist/bot.js
CHANGED
|
@@ -120,6 +120,12 @@ export class CcDiscordBot {
|
|
|
120
120
|
this.registerSlashCommands().catch((err) => {
|
|
121
121
|
console.error("[discord] slash command registration failed:", err.message);
|
|
122
122
|
});
|
|
123
|
+
// Pre-populate snowflakeMap so reverse-lookup works for all channels visible at login
|
|
124
|
+
for (const [, guild] of readyClient.guilds.cache) {
|
|
125
|
+
for (const [, channel] of guild.channels.cache) {
|
|
126
|
+
this.storeSnowflake(channel.id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
123
129
|
});
|
|
124
130
|
this.client.on(Events.MessageCreate, (msg) => {
|
|
125
131
|
void this.handleMessage(msg);
|
|
@@ -648,7 +654,11 @@ export class CcDiscordBot {
|
|
|
648
654
|
await interaction.reply("No cron jobs for this channel.");
|
|
649
655
|
}
|
|
650
656
|
else {
|
|
651
|
-
const lines = jobs.map((j) =>
|
|
657
|
+
const lines = jobs.map((j) => {
|
|
658
|
+
const chanId = this.reverseSnowflakeLookup(j.chatId);
|
|
659
|
+
const chanMention = chanId ? ` <#${chanId}>` : "";
|
|
660
|
+
return `• **${j.id}**${chanMention} ${j.schedule}: \`${j.prompt}\``;
|
|
661
|
+
});
|
|
652
662
|
await interaction.reply(lines.join("\n"));
|
|
653
663
|
}
|
|
654
664
|
break;
|
package/dist/index.js
CHANGED
|
@@ -90,7 +90,7 @@ const bot = new CcDiscordBot({
|
|
|
90
90
|
namespace,
|
|
91
91
|
registerRoutedChannelId: (ns, channelId) => notifier.registerRoutedChannelId(ns, channelId),
|
|
92
92
|
});
|
|
93
|
-
const notifier = startNotifier(bot, notifyChannelId, namespace, sharedRedis, (channelId, text) => handleUserMessageFn?.(channelId, text), (channelId, text) => forwardNotificationFn?.(channelId, text), () => getLastActiveChannelIdFn());
|
|
93
|
+
const notifier = startNotifier(bot, notifyChannelId, namespace, sharedRedis, (channelId, text) => handleUserMessageFn?.(channelId, text), (channelId, text) => forwardNotificationFn?.(channelId, text), () => getLastActiveChannelIdFn(), (n) => bot.reverseSnowflakeLookup(n));
|
|
94
94
|
console.log(`[notifier] started for namespace=${namespace} notifyChannelId=${notifyChannelId ?? "dynamic"}`);
|
|
95
95
|
// Wire closures now that bot is constructed
|
|
96
96
|
getLastActiveChannelIdFn = () => bot.getLastActiveChannelId();
|
package/dist/notifier.d.ts
CHANGED
|
@@ -20,12 +20,17 @@ export interface ChatMessage {
|
|
|
20
20
|
timestamp: string;
|
|
21
21
|
chatId: number;
|
|
22
22
|
}
|
|
23
|
+
export interface ParsedNotification {
|
|
24
|
+
text: string;
|
|
25
|
+
chatId?: number;
|
|
26
|
+
}
|
|
23
27
|
/**
|
|
24
|
-
* Parse a notification payload
|
|
25
|
-
*
|
|
28
|
+
* Parse a notification payload.
|
|
29
|
+
* Returns the display text plus an optional chatId for per-channel routing.
|
|
30
|
+
* Appends a [driver] or [driver:model] badge when present.
|
|
26
31
|
* Appends " cost: $X.XXX" if a numeric cost field is present.
|
|
27
32
|
*/
|
|
28
|
-
export declare function parseNotification(raw: string):
|
|
33
|
+
export declare function parseNotification(raw: string): ParsedNotification;
|
|
29
34
|
/**
|
|
30
35
|
* Write a message to the chat log in Redis.
|
|
31
36
|
* Fire-and-forget — errors are logged but not thrown.
|
|
@@ -42,12 +47,13 @@ export interface NotifierHandle {
|
|
|
42
47
|
/**
|
|
43
48
|
* Start the Discord notifier.
|
|
44
49
|
*
|
|
45
|
-
* @param bot
|
|
46
|
-
* @param notifyChannelId
|
|
47
|
-
* @param namespace
|
|
48
|
-
* @param redis
|
|
49
|
-
* @param handleUserMessage
|
|
50
|
-
* @param forwardNotification
|
|
51
|
-
* @param getActiveChannelId
|
|
50
|
+
* @param bot - CcDiscordBot instance (for sending messages)
|
|
51
|
+
* @param notifyChannelId - Discord channel ID to forward notifications to. Pass null to use getActiveChannelId.
|
|
52
|
+
* @param namespace - cc-agent namespace (used to build Redis channel names)
|
|
53
|
+
* @param redis - ioredis client in normal mode (will be duplicated for pub/sub)
|
|
54
|
+
* @param handleUserMessage - Optional callback to feed UI messages into the active Claude session
|
|
55
|
+
* @param forwardNotification - Optional callback to forward job notifications
|
|
56
|
+
* @param getActiveChannelId - Optional callback to resolve channelId dynamically
|
|
57
|
+
* @param reverseSnowflakeLookup - Optional callback to resolve a chatId integer to a Discord channelId
|
|
52
58
|
*/
|
|
53
|
-
export declare function startNotifier(bot: CcDiscordBot, notifyChannelId: string | null, namespace: string, redis: Redis, handleUserMessage?: (channelId: string, text: string) => void, forwardNotification?: (channelId: string, text: string) => void, getActiveChannelId?: () => string | undefined): NotifierHandle;
|
|
59
|
+
export declare function startNotifier(bot: CcDiscordBot, notifyChannelId: string | null, namespace: string, redis: Redis, handleUserMessage?: (channelId: string, text: string) => void, forwardNotification?: (channelId: string, text: string) => void, getActiveChannelId?: () => string | undefined, reverseSnowflakeLookup?: (n: number) => string | undefined): NotifierHandle;
|
package/dist/notifier.js
CHANGED
|
@@ -31,8 +31,9 @@ function shortenModelName(model, driver) {
|
|
|
31
31
|
return model;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Parse a notification payload
|
|
35
|
-
*
|
|
34
|
+
* Parse a notification payload.
|
|
35
|
+
* Returns the display text plus an optional chatId for per-channel routing.
|
|
36
|
+
* Appends a [driver] or [driver:model] badge when present.
|
|
36
37
|
* Appends " cost: $X.XXX" if a numeric cost field is present.
|
|
37
38
|
*/
|
|
38
39
|
export function parseNotification(raw) {
|
|
@@ -40,6 +41,7 @@ export function parseNotification(raw) {
|
|
|
40
41
|
let driver;
|
|
41
42
|
let model;
|
|
42
43
|
let cost;
|
|
44
|
+
let chatId;
|
|
43
45
|
try {
|
|
44
46
|
const parsed = JSON.parse(raw);
|
|
45
47
|
if (parsed.text)
|
|
@@ -48,16 +50,18 @@ export function parseNotification(raw) {
|
|
|
48
50
|
model = parsed.model;
|
|
49
51
|
if (typeof parsed.cost === "number")
|
|
50
52
|
cost = parsed.cost;
|
|
53
|
+
if (typeof parsed.chat_id === "number" && parsed.chat_id !== 0)
|
|
54
|
+
chatId = parsed.chat_id;
|
|
51
55
|
}
|
|
52
56
|
catch {
|
|
53
|
-
return text;
|
|
57
|
+
return { text };
|
|
54
58
|
}
|
|
55
59
|
if (!driver)
|
|
56
|
-
return text;
|
|
60
|
+
return { text, chatId };
|
|
57
61
|
const shortModel = shortenModelName(model ?? "", driver);
|
|
58
62
|
const badge = shortModel ? `${driver}:${shortModel}` : driver;
|
|
59
63
|
const costStr = cost != null ? ` cost: $${cost.toFixed(3)}` : "";
|
|
60
|
-
return `${text}\n[${badge}]${costStr}
|
|
64
|
+
return { text: `${text}\n[${badge}]${costStr}`, chatId };
|
|
61
65
|
}
|
|
62
66
|
/**
|
|
63
67
|
* Write a message to the chat log in Redis.
|
|
@@ -77,18 +81,32 @@ export function writeChatLog(redis, namespace, msg) {
|
|
|
77
81
|
log("warn", "writeChatLog publish failed:", err.message);
|
|
78
82
|
});
|
|
79
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the target Discord channelId for a notification.
|
|
86
|
+
* When chatId is set and a reverse-lookup function is available, prefer the originating channel.
|
|
87
|
+
* Falls back to notifyChannelId, then getActiveChannelId.
|
|
88
|
+
*/
|
|
89
|
+
function resolveNotifyChannel(chatId, notifyChannelId, getActiveChannelId, reverseSnowflakeLookup) {
|
|
90
|
+
if (chatId != null && reverseSnowflakeLookup) {
|
|
91
|
+
const resolved = reverseSnowflakeLookup(chatId);
|
|
92
|
+
if (resolved)
|
|
93
|
+
return resolved;
|
|
94
|
+
}
|
|
95
|
+
return notifyChannelId ?? getActiveChannelId?.();
|
|
96
|
+
}
|
|
80
97
|
/**
|
|
81
98
|
* Start the Discord notifier.
|
|
82
99
|
*
|
|
83
|
-
* @param bot
|
|
84
|
-
* @param notifyChannelId
|
|
85
|
-
* @param namespace
|
|
86
|
-
* @param redis
|
|
87
|
-
* @param handleUserMessage
|
|
88
|
-
* @param forwardNotification
|
|
89
|
-
* @param getActiveChannelId
|
|
100
|
+
* @param bot - CcDiscordBot instance (for sending messages)
|
|
101
|
+
* @param notifyChannelId - Discord channel ID to forward notifications to. Pass null to use getActiveChannelId.
|
|
102
|
+
* @param namespace - cc-agent namespace (used to build Redis channel names)
|
|
103
|
+
* @param redis - ioredis client in normal mode (will be duplicated for pub/sub)
|
|
104
|
+
* @param handleUserMessage - Optional callback to feed UI messages into the active Claude session
|
|
105
|
+
* @param forwardNotification - Optional callback to forward job notifications
|
|
106
|
+
* @param getActiveChannelId - Optional callback to resolve channelId dynamically
|
|
107
|
+
* @param reverseSnowflakeLookup - Optional callback to resolve a chatId integer to a Discord channelId
|
|
90
108
|
*/
|
|
91
|
-
export function startNotifier(bot, notifyChannelId, namespace, redis, handleUserMessage, forwardNotification, getActiveChannelId) {
|
|
109
|
+
export function startNotifier(bot, notifyChannelId, namespace, redis, handleUserMessage, forwardNotification, getActiveChannelId, reverseSnowflakeLookup) {
|
|
92
110
|
// Per-namespace channelId registry
|
|
93
111
|
const routedChannelIds = new Map();
|
|
94
112
|
const sub = redis.duplicate({
|
|
@@ -210,12 +228,13 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
|
|
|
210
228
|
}
|
|
211
229
|
}
|
|
212
230
|
for (const raw of items) {
|
|
213
|
-
const
|
|
214
|
-
|
|
231
|
+
const notification = parseNotification(raw);
|
|
232
|
+
const destChannelId = resolveNotifyChannel(notification.chatId, notifyChannelId, getActiveChannelId, reverseSnowflakeLookup) ?? targetId;
|
|
233
|
+
bot.sendToChannelById(destChannelId, notification.text).catch((err) => {
|
|
215
234
|
log("warn", "notify list send failed:", err.message);
|
|
216
235
|
});
|
|
217
236
|
if (forwardNotification) {
|
|
218
|
-
forwardNotification(
|
|
237
|
+
forwardNotification(destChannelId, notification.text);
|
|
219
238
|
}
|
|
220
239
|
}
|
|
221
240
|
if (remaining > 0) {
|
|
@@ -231,14 +250,14 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
|
|
|
231
250
|
const notifyCh = notifyChannel(namespace);
|
|
232
251
|
const incomingCh = chatIncomingChannel(namespace);
|
|
233
252
|
if (channel === notifyCh) {
|
|
234
|
-
const
|
|
253
|
+
const notification = parseNotification(message);
|
|
254
|
+
const targetId = resolveNotifyChannel(notification.chatId, notifyChannelId, getActiveChannelId, reverseSnowflakeLookup);
|
|
235
255
|
if (targetId != null) {
|
|
236
|
-
|
|
237
|
-
bot.sendToChannelById(targetId, text).catch((err) => {
|
|
256
|
+
bot.sendToChannelById(targetId, notification.text).catch((err) => {
|
|
238
257
|
log("warn", "notify send failed:", err.message);
|
|
239
258
|
});
|
|
240
259
|
if (forwardNotification) {
|
|
241
|
-
forwardNotification(targetId, text);
|
|
260
|
+
forwardNotification(targetId, notification.text);
|
|
242
261
|
}
|
|
243
262
|
}
|
|
244
263
|
else {
|