@nextclaw/channel-runtime 0.1.1 → 0.1.2
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/index.d.ts +2 -2
- package/dist/index.js +106 -62
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ declare class DingTalkChannel extends BaseChannel<Config["channels"]["dingtalk"]
|
|
|
57
57
|
declare class DiscordChannel extends BaseChannel<Config["channels"]["discord"]> {
|
|
58
58
|
name: string;
|
|
59
59
|
private client;
|
|
60
|
-
private
|
|
60
|
+
private readonly typingController;
|
|
61
61
|
constructor(config: Config["channels"]["discord"], bus: MessageBus);
|
|
62
62
|
start(): Promise<void>;
|
|
63
63
|
stop(): Promise<void>;
|
|
@@ -192,7 +192,7 @@ declare class TelegramChannel extends BaseChannel<Config["channels"]["telegram"]
|
|
|
192
192
|
private sessionManager?;
|
|
193
193
|
name: string;
|
|
194
194
|
private bot;
|
|
195
|
-
private
|
|
195
|
+
private readonly typingController;
|
|
196
196
|
private transcriber;
|
|
197
197
|
constructor(config: Config["channels"]["telegram"], bus: MessageBus, groqApiKey?: string, sessionManager?: SessionManager | undefined);
|
|
198
198
|
start(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -182,15 +182,73 @@ import { mkdirSync, writeFileSync } from "fs";
|
|
|
182
182
|
// src/utils/helpers.ts
|
|
183
183
|
import { getDataPath } from "@nextclaw/core";
|
|
184
184
|
|
|
185
|
+
// src/channels/typing-controller.ts
|
|
186
|
+
var ChannelTypingController = class {
|
|
187
|
+
heartbeatMs;
|
|
188
|
+
autoStopMs;
|
|
189
|
+
sendTyping;
|
|
190
|
+
tasks = /* @__PURE__ */ new Map();
|
|
191
|
+
constructor(options) {
|
|
192
|
+
this.heartbeatMs = Math.max(1e3, Math.floor(options.heartbeatMs));
|
|
193
|
+
this.autoStopMs = Math.max(this.heartbeatMs, Math.floor(options.autoStopMs));
|
|
194
|
+
this.sendTyping = options.sendTyping;
|
|
195
|
+
}
|
|
196
|
+
start(targetId) {
|
|
197
|
+
this.stop(targetId);
|
|
198
|
+
void this.sendTyping(targetId);
|
|
199
|
+
const heartbeat = setInterval(() => {
|
|
200
|
+
void this.sendTyping(targetId);
|
|
201
|
+
}, this.heartbeatMs);
|
|
202
|
+
const autoStop = setTimeout(() => {
|
|
203
|
+
this.stop(targetId);
|
|
204
|
+
}, this.autoStopMs);
|
|
205
|
+
this.tasks.set(targetId, {
|
|
206
|
+
heartbeat,
|
|
207
|
+
autoStop
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
stop(targetId) {
|
|
211
|
+
const task = this.tasks.get(targetId);
|
|
212
|
+
if (!task) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
clearInterval(task.heartbeat);
|
|
216
|
+
clearTimeout(task.autoStop);
|
|
217
|
+
this.tasks.delete(targetId);
|
|
218
|
+
}
|
|
219
|
+
stopAll() {
|
|
220
|
+
for (const targetId of this.tasks.keys()) {
|
|
221
|
+
this.stop(targetId);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
185
226
|
// src/channels/discord.ts
|
|
186
227
|
var DEFAULT_MEDIA_MAX_MB = 8;
|
|
187
228
|
var MEDIA_FETCH_TIMEOUT_MS = 15e3;
|
|
229
|
+
var TYPING_HEARTBEAT_MS = 8e3;
|
|
230
|
+
var TYPING_AUTO_STOP_MS = 45e3;
|
|
188
231
|
var DiscordChannel = class extends BaseChannel {
|
|
189
232
|
name = "discord";
|
|
190
233
|
client = null;
|
|
191
|
-
|
|
234
|
+
typingController;
|
|
192
235
|
constructor(config, bus) {
|
|
193
236
|
super(config, bus);
|
|
237
|
+
this.typingController = new ChannelTypingController({
|
|
238
|
+
heartbeatMs: TYPING_HEARTBEAT_MS,
|
|
239
|
+
autoStopMs: TYPING_AUTO_STOP_MS,
|
|
240
|
+
sendTyping: async (channelId) => {
|
|
241
|
+
if (!this.client) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const channel = this.client.channels.cache.get(channelId);
|
|
245
|
+
if (!channel || !channel.isTextBased()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const textChannel = channel;
|
|
249
|
+
await textChannel.sendTyping();
|
|
250
|
+
}
|
|
251
|
+
});
|
|
194
252
|
}
|
|
195
253
|
async start() {
|
|
196
254
|
if (!this.config.token) {
|
|
@@ -211,10 +269,7 @@ var DiscordChannel = class extends BaseChannel {
|
|
|
211
269
|
}
|
|
212
270
|
async stop() {
|
|
213
271
|
this.running = false;
|
|
214
|
-
|
|
215
|
-
clearInterval(task);
|
|
216
|
-
}
|
|
217
|
-
this.typingTasks.clear();
|
|
272
|
+
this.typingController.stopAll();
|
|
218
273
|
if (this.client) {
|
|
219
274
|
await this.client.destroy();
|
|
220
275
|
this.client = null;
|
|
@@ -285,18 +340,23 @@ var DiscordChannel = class extends BaseChannel {
|
|
|
285
340
|
}
|
|
286
341
|
const replyTo = message.reference?.messageId ?? null;
|
|
287
342
|
this.startTyping(channelId);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
343
|
+
try {
|
|
344
|
+
await this.handleMessage({
|
|
345
|
+
senderId,
|
|
346
|
+
chatId: channelId,
|
|
347
|
+
content: contentParts.length ? contentParts.join("\n") : "[empty message]",
|
|
348
|
+
attachments,
|
|
349
|
+
metadata: {
|
|
350
|
+
message_id: message.id,
|
|
351
|
+
guild_id: message.guildId,
|
|
352
|
+
reply_to: replyTo,
|
|
353
|
+
...attachmentIssues.length ? { attachment_issues: attachmentIssues } : {}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
} catch (err) {
|
|
357
|
+
this.stopTyping(channelId);
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
300
360
|
}
|
|
301
361
|
resolveProxyAgent() {
|
|
302
362
|
const proxy = this.config.proxy?.trim();
|
|
@@ -437,26 +497,10 @@ var DiscordChannel = class extends BaseChannel {
|
|
|
437
497
|
}
|
|
438
498
|
}
|
|
439
499
|
startTyping(channelId) {
|
|
440
|
-
this.
|
|
441
|
-
if (!this.client) {
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
const channel = this.client.channels.cache.get(channelId);
|
|
445
|
-
if (!channel || !channel.isTextBased()) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
const textChannel = channel;
|
|
449
|
-
const task = setInterval(() => {
|
|
450
|
-
void textChannel.sendTyping();
|
|
451
|
-
}, 8e3);
|
|
452
|
-
this.typingTasks.set(channelId, task);
|
|
500
|
+
this.typingController.start(channelId);
|
|
453
501
|
}
|
|
454
502
|
stopTyping(channelId) {
|
|
455
|
-
|
|
456
|
-
if (task) {
|
|
457
|
-
clearInterval(task);
|
|
458
|
-
this.typingTasks.delete(channelId);
|
|
459
|
-
}
|
|
503
|
+
this.typingController.stop(channelId);
|
|
460
504
|
}
|
|
461
505
|
};
|
|
462
506
|
function sanitizeAttachmentName(name) {
|
|
@@ -2167,6 +2211,8 @@ var GroqTranscriptionProvider = class {
|
|
|
2167
2211
|
// src/channels/telegram.ts
|
|
2168
2212
|
import { join as join3 } from "path";
|
|
2169
2213
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
2214
|
+
var TYPING_HEARTBEAT_MS2 = 4e3;
|
|
2215
|
+
var TYPING_AUTO_STOP_MS2 = 45e3;
|
|
2170
2216
|
var BOT_COMMANDS = [
|
|
2171
2217
|
{ command: "start", description: "Start the bot" },
|
|
2172
2218
|
{ command: "reset", description: "Reset conversation history" },
|
|
@@ -2177,10 +2223,17 @@ var TelegramChannel = class extends BaseChannel {
|
|
|
2177
2223
|
super(config, bus);
|
|
2178
2224
|
this.sessionManager = sessionManager;
|
|
2179
2225
|
this.transcriber = new GroqTranscriptionProvider(groqApiKey ?? null);
|
|
2226
|
+
this.typingController = new ChannelTypingController({
|
|
2227
|
+
heartbeatMs: TYPING_HEARTBEAT_MS2,
|
|
2228
|
+
autoStopMs: TYPING_AUTO_STOP_MS2,
|
|
2229
|
+
sendTyping: async (chatId) => {
|
|
2230
|
+
await this.bot?.sendChatAction(Number(chatId), "typing");
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2180
2233
|
}
|
|
2181
2234
|
name = "telegram";
|
|
2182
2235
|
bot = null;
|
|
2183
|
-
|
|
2236
|
+
typingController;
|
|
2184
2237
|
transcriber;
|
|
2185
2238
|
async start() {
|
|
2186
2239
|
if (!this.config.token) {
|
|
@@ -2246,10 +2299,7 @@ Just send me a text message to chat!`;
|
|
|
2246
2299
|
}
|
|
2247
2300
|
async stop() {
|
|
2248
2301
|
this.running = false;
|
|
2249
|
-
|
|
2250
|
-
clearInterval(task);
|
|
2251
|
-
}
|
|
2252
|
-
this.typingTasks.clear();
|
|
2302
|
+
this.typingController.stopAll();
|
|
2253
2303
|
if (this.bot) {
|
|
2254
2304
|
await this.bot.stopPolling();
|
|
2255
2305
|
this.bot = null;
|
|
@@ -2326,35 +2376,29 @@ Just send me a text message to chat!`;
|
|
|
2326
2376
|
}
|
|
2327
2377
|
const content = contentParts.length ? contentParts.join("\n") : "[empty message]";
|
|
2328
2378
|
this.startTyping(chatId);
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2379
|
+
try {
|
|
2380
|
+
await this.dispatchToBus(senderId, chatId, content, attachments, {
|
|
2381
|
+
message_id: message.message_id,
|
|
2382
|
+
user_id: sender.id,
|
|
2383
|
+
username: sender.username,
|
|
2384
|
+
first_name: sender.firstName,
|
|
2385
|
+
sender_type: sender.type,
|
|
2386
|
+
is_bot: sender.isBot,
|
|
2387
|
+
is_group: message.chat.type !== "private"
|
|
2388
|
+
});
|
|
2389
|
+
} catch (err) {
|
|
2390
|
+
this.stopTyping(chatId);
|
|
2391
|
+
throw err;
|
|
2392
|
+
}
|
|
2338
2393
|
}
|
|
2339
2394
|
async dispatchToBus(senderId, chatId, content, attachments, metadata) {
|
|
2340
2395
|
await this.handleMessage({ senderId, chatId, content, attachments, metadata });
|
|
2341
2396
|
}
|
|
2342
2397
|
startTyping(chatId) {
|
|
2343
|
-
this.
|
|
2344
|
-
if (!this.bot) {
|
|
2345
|
-
return;
|
|
2346
|
-
}
|
|
2347
|
-
const task = setInterval(() => {
|
|
2348
|
-
void this.bot?.sendChatAction(Number(chatId), "typing");
|
|
2349
|
-
}, 4e3);
|
|
2350
|
-
this.typingTasks.set(chatId, task);
|
|
2398
|
+
this.typingController.start(chatId);
|
|
2351
2399
|
}
|
|
2352
2400
|
stopTyping(chatId) {
|
|
2353
|
-
|
|
2354
|
-
if (task) {
|
|
2355
|
-
clearInterval(task);
|
|
2356
|
-
this.typingTasks.delete(chatId);
|
|
2357
|
-
}
|
|
2401
|
+
this.typingController.stop(chatId);
|
|
2358
2402
|
}
|
|
2359
2403
|
};
|
|
2360
2404
|
function resolveSender(message) {
|