@hijarvis/plugin-telegram 0.0.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.
@@ -0,0 +1,7 @@
1
+ import { PluginFactory } from "@hijarvis/core";
2
+
3
+ //#region src/plugin.d.ts
4
+ declare const createPlugin: PluginFactory;
5
+ //#endregion
6
+ export { createPlugin };
7
+ //# sourceMappingURL=plugin.d.mts.map
@@ -0,0 +1,685 @@
1
+ import { buildThreadIdFromScope, createLogger, executeIngressCommand, getFaultEnvelope, maybeExecuteSideQuestionIngress, parseSideQuestionCommand } from "@hijarvis/core";
2
+ import process from "node:process";
3
+ import { autoRetry } from "@grammyjs/auto-retry";
4
+ import { run } from "@grammyjs/runner";
5
+ import { stream } from "@grammyjs/stream";
6
+ import { Bot, GrammyError, HttpError } from "grammy";
7
+ import { z } from "zod";
8
+ //#region src/prompt.ts
9
+ const formatTelegramReplyContextBlock = (replyTo) => {
10
+ if (!replyTo) return "";
11
+ return ["Telegram reply context for the current message:", `- [${replyTo.sentAt.toISOString()}] ${replyTo.authorName || replyTo.authorId}: ${replyTo.text}`].join("\n");
12
+ };
13
+ const formatTelegramQueuedMessagesBlock = (skipped) => {
14
+ if (skipped.length === 0) return "";
15
+ return ["Additional Telegram messages that arrived while you were still processing the previous turn:", ...skipped.map((message) => {
16
+ return `- ${message.authorName || message.authorId}: ${message.text}`;
17
+ })].join("\n");
18
+ };
19
+ const formatTelegramCurrentMessageBlock = (message) => {
20
+ return ["Current Telegram user request:", `- ${message.authorName || message.authorId}: ${message.text}`].join("\n");
21
+ };
22
+ const buildTelegramPrompt = (options) => {
23
+ const header = options.chatType === "private" ? ["You are replying inside a Telegram private chat.", "Your Jar session history is the source of truth for earlier turns."] : ["You are replying inside a Telegram group or supergroup conversation.", "Telegram Bot API does not provide full history here, so only use the explicit reply context, queued messages, and your Jar session history."];
24
+ const chatTitleLine = options.message.chatTitle ? `Telegram chat title: ${options.message.chatTitle}` : "";
25
+ return [
26
+ ...header,
27
+ chatTitleLine,
28
+ "",
29
+ formatTelegramReplyContextBlock(options.message.replyTo),
30
+ "",
31
+ formatTelegramQueuedMessagesBlock(options.skipped),
32
+ "",
33
+ formatTelegramCurrentMessageBlock(options.message)
34
+ ].filter((segment) => segment.trim().length > 0).join("\n");
35
+ };
36
+ //#endregion
37
+ //#region src/config.ts
38
+ const nonEmptyString = z.string().trim().min(1);
39
+ const telegramIdentitySchema = z.object({
40
+ entity: nonEmptyString,
41
+ bot_token: nonEmptyString.optional(),
42
+ allowed_chat_ids: z.array(z.union([z.number().int(), nonEmptyString])).optional(),
43
+ allowed_usernames: z.array(nonEmptyString).optional()
44
+ }).strict();
45
+ const telegramPlatformSchema = z.object({ identities: z.record(nonEmptyString, telegramIdentitySchema) }).strict();
46
+ const parseTelegramPlatformConfig = (platform) => {
47
+ const parsed = telegramPlatformSchema.parse(platform.telegram ?? {});
48
+ return Object.fromEntries(Object.entries(parsed.identities).map(([id, identity]) => [id, {
49
+ id,
50
+ entityId: identity.entity,
51
+ botToken: identity.bot_token,
52
+ allowedChatIds: identity.allowed_chat_ids?.map((value) => String(value)),
53
+ allowedUsernames: identity.allowed_usernames?.map((value) => value.replace(/^@/, "").toLowerCase())
54
+ }]));
55
+ };
56
+ //#endregion
57
+ //#region src/runtime.ts
58
+ const telegramGatewayEnvSchema = z.object({
59
+ TELEGRAM_BOT_TOKEN: z.string().trim().min(1).optional(),
60
+ JARVIS_TELEGRAM_ALLOWED_CHAT_IDS: z.string().trim().min(1).optional(),
61
+ JARVIS_TELEGRAM_ALLOWED_USERNAMES: z.string().trim().min(1).optional()
62
+ });
63
+ const queueEntryTtlMs = 6e4;
64
+ const maxQueueSize = 20;
65
+ const startTelegramIdentities = async (config, hooks, services, logger) => {
66
+ const env = loadTelegramGatewayEnv(process.env);
67
+ const telegramConfigs = parseTelegramPlatformConfig(config.platform);
68
+ const identities = Object.values(config.platformIdentities).filter((identity) => identity.platform === "telegram");
69
+ const stops = [];
70
+ for (const identityConfig of identities) {
71
+ const telegramConfig = telegramConfigs[identityConfig.id];
72
+ if (telegramConfig === void 0) throw new Error(`Missing Telegram runtime config for identity "${identityConfig.id}"`);
73
+ const bot = new Bot(resolveTelegramBotToken(telegramConfig, env));
74
+ bot.api.config.use(autoRetry());
75
+ bot.use(stream());
76
+ const me = await bot.api.getMe();
77
+ const allowedChatIds = resolveAllowedChatIds(telegramConfig, env);
78
+ const allowedUsernames = resolveAllowedUsernames(telegramConfig, env);
79
+ const identityLogger = logger.child({
80
+ identityId: identityConfig.id,
81
+ entityId: identityConfig.entityId
82
+ });
83
+ const state = {
84
+ identityConfig,
85
+ runtime: config,
86
+ hooks,
87
+ services,
88
+ telegramConfig,
89
+ logger: identityLogger,
90
+ identity: {
91
+ botId: me.id,
92
+ username: me.username
93
+ },
94
+ allowedChatIds,
95
+ allowedUsernames,
96
+ queues: /* @__PURE__ */ new Map()
97
+ };
98
+ identityLogger.info("telegram.gateway_initialized", {
99
+ botId: me.id,
100
+ botUsername: me.username,
101
+ allowedChatCount: allowedChatIds?.size ?? 0,
102
+ allowedUsernameCount: allowedUsernames?.size ?? 0
103
+ });
104
+ registerTelegramHandlers(bot, state);
105
+ const runner = run(bot);
106
+ process.stdout.write(`Jar Telegram identity ${identityConfig.id} running in long polling mode\n`);
107
+ identityLogger.info("telegram.gateway_started");
108
+ stops.push(async () => {
109
+ identityLogger.info("telegram.gateway_stopping");
110
+ runner.stop();
111
+ });
112
+ }
113
+ return stops;
114
+ };
115
+ const registerTelegramHandlers = (bot, state) => {
116
+ bot.command("btw", async (context) => {
117
+ const messageContext = context;
118
+ if (!isAllowedChat(messageContext.chat.id, state)) return;
119
+ if (!isAllowedUsername(messageContext.msg.from?.username, state)) return;
120
+ const text = readTelegramMessageText(messageContext.msg);
121
+ const parsed = text ? parseSideQuestionCommand({
122
+ input: text,
123
+ parentThreadId: "preview",
124
+ source: {
125
+ platform: "telegram",
126
+ identityId: state.identityConfig.id
127
+ }
128
+ }) : null;
129
+ if (parsed === null) {
130
+ await messageContext.reply("Usage: /btw <question>", createReplyOptions(messageContext));
131
+ return;
132
+ }
133
+ const normalized = normalizeTelegramMessageSeed(messageContext, state.identity);
134
+ if (!normalized) {
135
+ await messageContext.reply("I could not read that /btw question.", createReplyOptions(messageContext));
136
+ return;
137
+ }
138
+ const threadId = buildThreadIdFromScope({
139
+ platform: "telegram",
140
+ identityId: state.identityConfig.id,
141
+ scope: normalized.threadId === void 0 ? {
142
+ kind: "telegram",
143
+ chatId: normalized.chatId
144
+ } : {
145
+ kind: "telegram",
146
+ chatId: normalized.chatId,
147
+ messageThreadId: String(normalized.threadId)
148
+ }
149
+ });
150
+ const requestLogger = state.logger.child({
151
+ conversationKey: buildConversationKey(normalized.chatId, normalized.threadId),
152
+ threadId,
153
+ entityId: state.identityConfig.entityId,
154
+ chatId: normalized.chatId,
155
+ messageId: normalized.messageId,
156
+ trigger: "side_question"
157
+ });
158
+ try {
159
+ const result = await executeIngressCommand({
160
+ config: state.runtime,
161
+ hooks: state.hooks,
162
+ logger: requestLogger,
163
+ command: {
164
+ kind: "side_question",
165
+ source: {
166
+ platform: "telegram",
167
+ identityId: state.identityConfig.id
168
+ },
169
+ parentThreadId: threadId,
170
+ question: { text: parsed.question.text }
171
+ }
172
+ });
173
+ if (result.kind !== "side_question") throw new Error("Telegram /btw expected a side-question execution result.");
174
+ await messageContext.reply(result.outputText.trim().length > 0 ? result.outputText : "I do not have a side-question reply.", createReplyOptions(messageContext));
175
+ } catch (error) {
176
+ const message = error instanceof Error ? error.message : String(error);
177
+ requestLogger.error("telegram.side_question_failed", {
178
+ threadId,
179
+ chatId: normalized.chatId,
180
+ messageId: normalized.messageId,
181
+ message
182
+ });
183
+ await messageContext.reply(message.includes("No live parent thread available") ? "No active live thread is available for /btw in this chat yet." : "I ran into an error while answering that /btw question.", createReplyOptions(messageContext));
184
+ }
185
+ });
186
+ bot.command(["start", "help"], async (context) => {
187
+ const messageContext = context;
188
+ if (!isAllowedChat(messageContext.chat.id, state)) return;
189
+ await messageContext.reply("Jar Telegram gateway is ready. Send a private message, or mention/reply to the bot in a group.", createReplyOptions(messageContext));
190
+ });
191
+ bot.on("message", async (context) => {
192
+ const messageContext = context;
193
+ if (!isAllowedChat(messageContext.chat.id, state)) {
194
+ state.logger.debug("telegram.event_ignored", {
195
+ reason: "chat_not_allowed",
196
+ chatId: String(messageContext.chat.id)
197
+ });
198
+ return;
199
+ }
200
+ if (!isAllowedUsername(messageContext.msg.from?.username, state)) {
201
+ state.logger.debug("telegram.event_ignored", {
202
+ reason: "username_not_allowed",
203
+ chatId: String(messageContext.chat.id),
204
+ username: messageContext.msg.from?.username
205
+ });
206
+ return;
207
+ }
208
+ if (isTelegramSideQuestionCommand(readTelegramMessageText(messageContext.msg))) {
209
+ state.logger.debug("telegram.event_ignored", {
210
+ reason: "side_question_command",
211
+ chatId: String(messageContext.chat.id),
212
+ messageId: messageContext.msg.message_id
213
+ });
214
+ return;
215
+ }
216
+ const trigger = classifyMessageTrigger(messageContext, state.identity);
217
+ if (!trigger) {
218
+ state.logger.debug("telegram.event_ignored", {
219
+ reason: "not_triggered",
220
+ chatId: String(messageContext.chat.id),
221
+ messageId: messageContext.msg.message_id
222
+ });
223
+ return;
224
+ }
225
+ const normalized = normalizeTelegramMessageSeed(messageContext, state.identity);
226
+ if (!normalized) {
227
+ state.logger.debug("telegram.event_ignored", {
228
+ reason: "unsupported_message",
229
+ chatId: String(messageContext.chat.id),
230
+ messageId: messageContext.msg.message_id
231
+ });
232
+ return;
233
+ }
234
+ const conversationKey = buildConversationKey(normalized.chatId, normalized.threadId);
235
+ state.logger.info("telegram.event_received", {
236
+ trigger,
237
+ conversationKey,
238
+ chatId: normalized.chatId,
239
+ messageId: normalized.messageId,
240
+ textChars: normalized.text.length
241
+ });
242
+ enqueueMessage(state, conversationKey, {
243
+ context: messageContext,
244
+ kind: trigger,
245
+ message: normalized,
246
+ receivedAt: Date.now()
247
+ });
248
+ });
249
+ bot.catch((error) => {
250
+ const context = error.ctx;
251
+ if (error.error instanceof GrammyError) {
252
+ state.logger.error("telegram.middleware_failed", {
253
+ chatId: context.chat?.id !== void 0 ? String(context.chat.id) : void 0,
254
+ messageId: context.msg?.message_id,
255
+ message: error.error.description
256
+ });
257
+ return;
258
+ }
259
+ if (error.error instanceof HttpError) {
260
+ state.logger.error("telegram.middleware_failed", {
261
+ chatId: context.chat?.id !== void 0 ? String(context.chat.id) : void 0,
262
+ messageId: context.msg?.message_id,
263
+ message: error.error.message
264
+ });
265
+ return;
266
+ }
267
+ const message = error.error instanceof Error ? error.error.message : String(error.error);
268
+ state.logger.error("telegram.middleware_failed", {
269
+ chatId: context.chat?.id !== void 0 ? String(context.chat.id) : void 0,
270
+ messageId: context.msg?.message_id,
271
+ message
272
+ });
273
+ });
274
+ };
275
+ const resolveTelegramBotToken = (telegramConfig, env) => {
276
+ const botToken = env.TELEGRAM_BOT_TOKEN ?? telegramConfig.botToken ?? "";
277
+ if (!botToken) throw new Error("Missing Telegram credentials: TELEGRAM_BOT_TOKEN");
278
+ return botToken;
279
+ };
280
+ const resolveAllowedChatIds = (telegramConfig, env) => {
281
+ if (env.JARVIS_TELEGRAM_ALLOWED_CHAT_IDS) {
282
+ const values = env.JARVIS_TELEGRAM_ALLOWED_CHAT_IDS.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
283
+ return values.length > 0 ? new Set(values) : void 0;
284
+ }
285
+ const configured = telegramConfig.allowedChatIds;
286
+ if (!configured || configured.length === 0) return;
287
+ return new Set(configured);
288
+ };
289
+ const resolveAllowedUsernames = (telegramConfig, env) => {
290
+ if (env.JARVIS_TELEGRAM_ALLOWED_USERNAMES) {
291
+ const values = env.JARVIS_TELEGRAM_ALLOWED_USERNAMES.split(",").map((value) => value.trim().replace(/^@/, "").toLowerCase()).filter((value) => value.length > 0);
292
+ return values.length > 0 ? new Set(values) : void 0;
293
+ }
294
+ const configured = telegramConfig.allowedUsernames;
295
+ if (!configured || configured.length === 0) return;
296
+ return new Set(configured);
297
+ };
298
+ const isAllowedChat = (chatId, state) => {
299
+ if (!state.allowedChatIds) return true;
300
+ return state.allowedChatIds.has(String(chatId));
301
+ };
302
+ const isAllowedUsername = (username, state) => {
303
+ if (!state.allowedUsernames) return true;
304
+ if (!username) return false;
305
+ return state.allowedUsernames.has(username.toLowerCase());
306
+ };
307
+ const classifyMessageTrigger = (context, identity) => {
308
+ if (context.chat.type === "private") return "private_chat";
309
+ if (context.msg.reply_to_message?.from?.id === identity.botId) return "reply_to_bot";
310
+ const text = readTelegramMessageText(context.msg);
311
+ if (!text) return null;
312
+ const username = identity.username;
313
+ if (!username) return null;
314
+ const normalizedMention = `@${username.toLowerCase()}`;
315
+ return text.toLowerCase().includes(normalizedMention) ? "mention" : null;
316
+ };
317
+ const normalizeTelegramMessageSeed = (context, identity) => {
318
+ const text = readTelegramMessageText(context.msg);
319
+ const from = context.msg.from;
320
+ if (!text || !from) return null;
321
+ const sentAt = /* @__PURE__ */ new Date(context.msg.date * 1e3);
322
+ const normalizedText = stripBotMention(text, identity.username);
323
+ if (!normalizedText) return null;
324
+ const chatTitle = readChatTitle(context.chat);
325
+ const replyTo = readReplyContext(context.msg.reply_to_message);
326
+ return {
327
+ id: String(context.msg.message_id),
328
+ messageId: context.msg.message_id,
329
+ text: normalizedText,
330
+ authorId: String(from.id),
331
+ authorName: formatTelegramAuthorName(from),
332
+ sentAt,
333
+ chatId: String(context.chat.id),
334
+ chatType: context.chat.type === "private" ? "private" : "group",
335
+ chatTitle,
336
+ threadId: context.msg.message_thread_id,
337
+ replyTo
338
+ };
339
+ };
340
+ const readTelegramMessageText = (message) => {
341
+ return message?.text ?? message?.caption;
342
+ };
343
+ const isTelegramSideQuestionCommand = (text) => {
344
+ return text !== void 0 && parseSideQuestionCommand({
345
+ input: text,
346
+ parentThreadId: "preview",
347
+ source: { platform: "telegram" }
348
+ }) !== null;
349
+ };
350
+ const stripBotMention = (text, username) => {
351
+ if (!username) return text.trim();
352
+ return text.replaceAll(`@${username}`, "").trim();
353
+ };
354
+ const formatTelegramAuthorName = (from) => {
355
+ const parts = [from.first_name, from.last_name].filter((part) => Boolean(part));
356
+ if (parts.length > 0) return parts.join(" ");
357
+ return from.username ?? String(from.id);
358
+ };
359
+ const readChatTitle = (chat) => {
360
+ switch (chat.type) {
361
+ case "group":
362
+ case "supergroup": return chat.title;
363
+ case "private": return;
364
+ }
365
+ };
366
+ const readReplyContext = (replyTo) => {
367
+ const text = replyTo ? readTelegramMessageText(replyTo) : void 0;
368
+ const from = replyTo?.from;
369
+ if (!replyTo || !text || !from) return;
370
+ return {
371
+ authorId: String(from.id),
372
+ authorName: formatTelegramAuthorName(from),
373
+ text,
374
+ sentAt: /* @__PURE__ */ new Date(replyTo.date * 1e3)
375
+ };
376
+ };
377
+ const enqueueMessage = (state, conversationKey, entry) => {
378
+ const queue = getConversationQueue(state, conversationKey);
379
+ const now = Date.now();
380
+ queue.entries = queue.entries.filter((queued) => now - queued.receivedAt <= queueEntryTtlMs);
381
+ queue.entries.push(entry);
382
+ if (queue.entries.length > maxQueueSize) queue.entries.splice(0, queue.entries.length - maxQueueSize);
383
+ state.logger.info("telegram.queue_enqueued", {
384
+ conversationKey,
385
+ trigger: entry.kind,
386
+ queueSize: queue.entries.length,
387
+ chatId: entry.message.chatId,
388
+ messageId: entry.message.messageId
389
+ });
390
+ if (!queue.running) {
391
+ queue.running = true;
392
+ drainQueue(state, conversationKey, queue);
393
+ }
394
+ };
395
+ const drainQueue = async (state, conversationKey, queue) => {
396
+ try {
397
+ while (queue.entries.length > 0) {
398
+ const now = Date.now();
399
+ queue.entries = queue.entries.filter((queued) => now - queued.receivedAt <= queueEntryTtlMs);
400
+ if (queue.entries.length === 0) break;
401
+ const batch = queue.entries.splice(0, queue.entries.length);
402
+ if (batch.length === 0) continue;
403
+ const current = batch[batch.length - 1];
404
+ const skipped = batch.slice(0, -1).map((item) => item.message);
405
+ state.logger.info("telegram.queue_draining", {
406
+ conversationKey,
407
+ batchSize: batch.length,
408
+ skippedCount: skipped.length,
409
+ trigger: current.kind
410
+ });
411
+ try {
412
+ await handleQueueEntry(state, conversationKey, current, skipped);
413
+ } catch (error) {
414
+ const message = error instanceof Error ? error.message : String(error);
415
+ state.logger.error("telegram.queue_entry_failed", {
416
+ conversationKey,
417
+ message
418
+ });
419
+ }
420
+ }
421
+ } finally {
422
+ queue.running = false;
423
+ }
424
+ };
425
+ const handleQueueEntry = async (state, conversationKey, entry, skipped) => {
426
+ const threadId = buildThreadIdFromScope({
427
+ platform: "telegram",
428
+ identityId: state.identityConfig.id,
429
+ scope: entry.message.threadId === void 0 ? {
430
+ kind: "telegram",
431
+ chatId: entry.message.chatId
432
+ } : {
433
+ kind: "telegram",
434
+ chatId: entry.message.chatId,
435
+ messageThreadId: String(entry.message.threadId)
436
+ }
437
+ });
438
+ const requestLogger = state.logger.child({
439
+ conversationKey,
440
+ threadId,
441
+ entityId: state.identityConfig.entityId,
442
+ chatId: entry.message.chatId,
443
+ messageId: entry.message.messageId,
444
+ trigger: entry.kind
445
+ });
446
+ const current = materializeTelegramMessage(entry.message);
447
+ const skippedMessages = skipped.map(materializeTelegramMessage);
448
+ const prompt = buildTelegramPrompt({
449
+ message: current,
450
+ skipped: skippedMessages,
451
+ chatType: current.chatTitle || entry.message.chatType === "group" ? "group" : "private"
452
+ });
453
+ requestLogger.info("telegram.request_started", {
454
+ skippedCount: skippedMessages.length,
455
+ currentAuthor: current.authorName,
456
+ promptChars: prompt.length
457
+ });
458
+ const sideQuestion = await maybeExecuteSideQuestionIngress({
459
+ config: state.runtime,
460
+ parentThreadId: threadId,
461
+ input: current.text,
462
+ source: {
463
+ platform: "telegram",
464
+ identityId: state.identityConfig.id
465
+ },
466
+ logger: requestLogger
467
+ });
468
+ if (sideQuestion.handled) {
469
+ await entry.context.reply(sideQuestion.result.outputText.trim().length > 0 ? sideQuestion.result.outputText : "I do not have a side-question reply.", createReplyOptions(entry.context));
470
+ return;
471
+ }
472
+ await respondInTelegramConversation({
473
+ state,
474
+ context: entry.context,
475
+ conversationKey,
476
+ threadId,
477
+ prompt,
478
+ skillTriggerText: buildTelegramSkillTriggerText(current, skippedMessages),
479
+ logger: requestLogger
480
+ });
481
+ };
482
+ const materializeTelegramMessage = (seed) => {
483
+ return {
484
+ id: seed.id,
485
+ text: seed.text,
486
+ authorId: seed.authorId,
487
+ authorName: seed.authorName,
488
+ sentAt: seed.sentAt,
489
+ chatTitle: seed.chatTitle,
490
+ replyTo: seed.replyTo
491
+ };
492
+ };
493
+ const respondInTelegramConversation = async (options) => {
494
+ const startedAt = Date.now();
495
+ const textStream = new AsyncTextDeltaQueue();
496
+ let emittedText = false;
497
+ let turnId;
498
+ let runId;
499
+ const contributions = options.state.services.get("pluginContributions");
500
+ const responseTask = executeIngressCommand({
501
+ config: options.state.runtime,
502
+ hooks: options.state.hooks,
503
+ logger: options.logger,
504
+ pluginOverrides: contributions ?? {
505
+ skillRoots: [],
506
+ overlays: [],
507
+ tools: []
508
+ },
509
+ command: {
510
+ kind: "message",
511
+ source: { platform: "telegram" },
512
+ routing: {
513
+ platform: "telegram",
514
+ scope: options.context.msg.message_thread_id === void 0 ? {
515
+ kind: "telegram",
516
+ chatId: String(options.context.chat.id)
517
+ } : {
518
+ kind: "telegram",
519
+ chatId: String(options.context.chat.id),
520
+ messageThreadId: String(options.context.msg.message_thread_id)
521
+ }
522
+ },
523
+ message: {
524
+ text: options.skillTriggerText,
525
+ id: String(options.context.msg.message_id)
526
+ },
527
+ prompt: options.prompt,
528
+ skillTriggerText: options.skillTriggerText,
529
+ audit: {
530
+ trigger: "platform_event",
531
+ triggerKind: "telegram_message",
532
+ metadata: {
533
+ conversationKey: options.conversationKey,
534
+ chatId: String(options.context.chat.id),
535
+ messageId: options.context.msg.message_id
536
+ }
537
+ },
538
+ execution: { onEvent(event) {
539
+ if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
540
+ emittedText = true;
541
+ textStream.push(event.assistantMessageEvent.delta);
542
+ }
543
+ } }
544
+ }
545
+ }).then((result) => {
546
+ const { outputText } = result;
547
+ if (!emittedText) textStream.push(outputText.trim().length > 0 ? outputText : "I finished processing that, but I do not have a textual reply to send.");
548
+ textStream.close();
549
+ return result;
550
+ }).catch((error) => {
551
+ textStream.fail(toError(error));
552
+ throw error;
553
+ });
554
+ try {
555
+ await options.context.replyWithStream(textStream, createReplyOptions(options.context));
556
+ const result = await responseTask;
557
+ if (result.kind !== "message") throw new Error("Telegram reply generation expected a message execution result.");
558
+ turnId = result.turnId;
559
+ runId = result.runId;
560
+ const outputText = result.outputText;
561
+ options.logger.info("telegram.reply_posted", {
562
+ durationMs: Date.now() - startedAt,
563
+ replyChars: outputText.trim().length,
564
+ threadId: options.threadId,
565
+ turnId: result.turnId,
566
+ runId: result.runId,
567
+ conversationKey: options.conversationKey,
568
+ streamed: true
569
+ });
570
+ } catch (error) {
571
+ const settledResult = await responseTask.catch(() => void 0);
572
+ if (settledResult?.kind === "message") {
573
+ turnId = turnId ?? settledResult.turnId;
574
+ runId = runId ?? settledResult.runId;
575
+ }
576
+ const normalizedError = toError(error);
577
+ const envelope = getFaultEnvelope(error);
578
+ options.logger.error("telegram.reply_failed", {
579
+ durationMs: Date.now() - startedAt,
580
+ message: normalizedError.message,
581
+ threadId: options.threadId,
582
+ turnId: turnId ?? envelope?.turnId,
583
+ runId: runId ?? envelope?.runId,
584
+ conversationKey: options.conversationKey,
585
+ fault: envelope?.fault,
586
+ phase: envelope?.phase
587
+ });
588
+ if (!emittedText) await options.context.reply("I ran into an error while processing that request. Please try again in the same chat.", createReplyOptions(options.context));
589
+ }
590
+ };
591
+ const buildTelegramSkillTriggerText = (currentMessage, skipped) => {
592
+ return [...skipped, currentMessage].map((message) => message.text.trim()).filter((text) => text.length > 0).join("\n");
593
+ };
594
+ const createReplyOptions = (context) => {
595
+ const options = { reply_parameters: { message_id: context.msg.message_id } };
596
+ if (context.msg.message_thread_id !== void 0) options.message_thread_id = context.msg.message_thread_id;
597
+ return options;
598
+ };
599
+ const getConversationQueue = (state, conversationKey) => {
600
+ const existing = state.queues.get(conversationKey);
601
+ if (existing) return existing;
602
+ const created = {
603
+ running: false,
604
+ entries: []
605
+ };
606
+ state.queues.set(conversationKey, created);
607
+ return created;
608
+ };
609
+ const buildConversationKey = (chatId, threadId) => {
610
+ return threadId === void 0 ? chatId : `${chatId}:${threadId}`;
611
+ };
612
+ const loadTelegramGatewayEnv = (rawEnv) => {
613
+ return telegramGatewayEnvSchema.parse(rawEnv);
614
+ };
615
+ const toError = (error) => {
616
+ return error instanceof Error ? error : new Error(String(error));
617
+ };
618
+ var AsyncTextDeltaQueue = class {
619
+ chunks = [];
620
+ waiters = [];
621
+ completed = false;
622
+ failure = null;
623
+ push(chunk) {
624
+ if (!chunk) return;
625
+ const waiter = this.waiters.shift();
626
+ if (waiter) {
627
+ waiter.resolve({
628
+ done: false,
629
+ value: chunk
630
+ });
631
+ return;
632
+ }
633
+ this.chunks.push(chunk);
634
+ }
635
+ close() {
636
+ this.completed = true;
637
+ while (this.waiters.length > 0) this.waiters.shift()?.resolve({
638
+ done: true,
639
+ value: void 0
640
+ });
641
+ }
642
+ fail(error) {
643
+ this.failure = error;
644
+ while (this.waiters.length > 0) this.waiters.shift()?.reject(error);
645
+ }
646
+ [Symbol.asyncIterator]() {
647
+ return { next: async () => {
648
+ if (this.chunks.length > 0) return {
649
+ done: false,
650
+ value: this.chunks.shift()
651
+ };
652
+ if (this.failure) throw this.failure;
653
+ if (this.completed) return {
654
+ done: true,
655
+ value: void 0
656
+ };
657
+ return new Promise((resolve, reject) => {
658
+ this.waiters.push({
659
+ resolve,
660
+ reject
661
+ });
662
+ });
663
+ } };
664
+ }
665
+ };
666
+ //#endregion
667
+ //#region src/plugin.ts
668
+ const createPlugin = (_pluginConfig) => {
669
+ return {
670
+ name: "jar-gateway-telegram",
671
+ async install({ config, hooks, services, logger }) {
672
+ const stops = await startTelegramIdentities(config, hooks, services, logger ?? createLogger({
673
+ level: "info",
674
+ stderr: true
675
+ }));
676
+ return { cleanup: async () => {
677
+ for (const stop of stops) await stop();
678
+ } };
679
+ }
680
+ };
681
+ };
682
+ //#endregion
683
+ export { createPlugin };
684
+
685
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/prompt.ts","../src/config.ts","../src/runtime.ts","../src/plugin.ts"],"sourcesContent":["export type TelegramReplyContext = {\n authorId: string;\n authorName: string;\n text: string;\n sentAt: Date;\n};\n\nexport type TelegramMessage = {\n id: string;\n text: string;\n authorId: string;\n authorName: string;\n sentAt: Date;\n chatTitle?: string;\n replyTo?: TelegramReplyContext;\n};\n\nexport const createTelegramSessionId = (conversationId: string): string => {\n return conversationId.replaceAll(\":\", \"__\").replaceAll(\"/\", \"_\");\n};\n\nexport const formatTelegramReplyContextBlock = (\n replyTo: TelegramReplyContext | undefined,\n): string => {\n if (!replyTo) {\n return \"\";\n }\n\n return [\n \"Telegram reply context for the current message:\",\n `- [${replyTo.sentAt.toISOString()}] ${replyTo.authorName || replyTo.authorId}: ${replyTo.text}`,\n ].join(\"\\n\");\n};\n\nexport const formatTelegramQueuedMessagesBlock = (\n skipped: TelegramMessage[],\n): string => {\n if (skipped.length === 0) {\n return \"\";\n }\n\n const lines = skipped.map((message) => {\n return `- ${message.authorName || message.authorId}: ${message.text}`;\n });\n\n return [\n \"Additional Telegram messages that arrived while you were still processing the previous turn:\",\n ...lines,\n ].join(\"\\n\");\n};\n\nexport const formatTelegramCurrentMessageBlock = (\n message: TelegramMessage,\n): string => {\n return [\n \"Current Telegram user request:\",\n `- ${message.authorName || message.authorId}: ${message.text}`,\n ].join(\"\\n\");\n};\n\nexport const buildTelegramPrompt = (options: {\n message: TelegramMessage;\n skipped: TelegramMessage[];\n chatType: \"private\" | \"group\";\n}): string => {\n const header = options.chatType === \"private\"\n ? [\n \"You are replying inside a Telegram private chat.\",\n \"Your Jar session history is the source of truth for earlier turns.\",\n ]\n : [\n \"You are replying inside a Telegram group or supergroup conversation.\",\n \"Telegram Bot API does not provide full history here, so only use the explicit reply context, queued messages, and your Jar session history.\",\n ];\n\n const chatTitleLine = options.message.chatTitle\n ? `Telegram chat title: ${options.message.chatTitle}`\n : \"\";\n\n return [\n ...header,\n chatTitleLine,\n \"\",\n formatTelegramReplyContextBlock(options.message.replyTo),\n \"\",\n formatTelegramQueuedMessagesBlock(options.skipped),\n \"\",\n formatTelegramCurrentMessageBlock(options.message),\n ]\n .filter((segment) => segment.trim().length > 0)\n .join(\"\\n\");\n};\n","import { z } from \"zod\";\n\nconst nonEmptyString = z.string().trim().min(1);\n\nconst telegramIdentitySchema = z.object({\n entity: nonEmptyString,\n bot_token: nonEmptyString.optional(),\n allowed_chat_ids: z.array(z.union([z.number().int(), nonEmptyString])).optional(),\n allowed_usernames: z.array(nonEmptyString).optional(),\n}).strict();\n\nconst telegramPlatformSchema = z.object({\n identities: z.record(nonEmptyString, telegramIdentitySchema),\n}).strict();\n\nexport type TelegramPlatformIdentityConfig = {\n id: string;\n entityId: string;\n botToken?: string;\n allowedChatIds?: string[];\n allowedUsernames?: string[];\n};\n\nexport const parseTelegramPlatformConfig = (\n platform: Record<string, unknown>,\n): Record<string, TelegramPlatformIdentityConfig> => {\n const parsed = telegramPlatformSchema.parse(platform.telegram ?? {});\n\n return Object.fromEntries(Object.entries(parsed.identities).map(([id, identity]) => [id, {\n id,\n entityId: identity.entity,\n botToken: identity.bot_token,\n allowedChatIds: identity.allowed_chat_ids?.map((value) => String(value)),\n allowedUsernames: identity.allowed_usernames?.map((value) =>\n value.replace(/^@/, \"\").toLowerCase()\n ),\n } satisfies TelegramPlatformIdentityConfig]));\n};\n","import process from \"node:process\";\n\nimport { autoRetry } from \"@grammyjs/auto-retry\";\nimport { run, type RunnerHandle } from \"@grammyjs/runner\";\nimport { stream, type StreamFlavor } from \"@grammyjs/stream\";\nimport {\n buildThreadIdFromScope,\n executeIngressCommand,\n getFaultEnvelope,\n maybeExecuteSideQuestionIngress,\n parseSideQuestionCommand,\n type MessageIngressCommand,\n type LoadedRuntimeConfig,\n type Logger,\n type PlatformIdentityRef,\n type PluginContribution,\n type HookRegistry,\n type ServiceRegistry,\n} from \"@hijarvis/core\";\nimport {\n Bot,\n type Context,\n GrammyError,\n HttpError,\n} from \"grammy\";\nimport { z } from \"zod\";\n\nimport {\n buildTelegramPrompt,\n type TelegramMessage,\n type TelegramReplyContext,\n} from \"./prompt.js\";\nimport {\n parseTelegramPlatformConfig,\n type TelegramPlatformIdentityConfig,\n} from \"./config.js\";\n\nconst telegramGatewayEnvSchema = z.object({\n TELEGRAM_BOT_TOKEN: z.string().trim().min(1).optional(),\n JARVIS_TELEGRAM_ALLOWED_CHAT_IDS: z.string().trim().min(1).optional(),\n JARVIS_TELEGRAM_ALLOWED_USERNAMES: z.string().trim().min(1).optional(),\n});\n\ntype TelegramGatewayEnv = z.infer<typeof telegramGatewayEnvSchema>;\ntype TelegramGatewayContext = StreamFlavor<Context>;\ntype TelegramMessageContext = TelegramGatewayContext & {\n chat: NonNullable<TelegramGatewayContext[\"chat\"]>;\n msg: NonNullable<TelegramGatewayContext[\"msg\"]>;\n};\ntype TelegramUserLike = {\n id: number;\n first_name: string;\n last_name?: string;\n username?: string;\n};\ntype TelegramReadableMessage = {\n text?: string;\n caption?: string;\n};\n\ntype TelegramIdentity = {\n botId: number;\n username?: string;\n};\n\ntype TriggerKind = \"private_chat\" | \"reply_to_bot\" | \"mention\";\n\ntype TelegramMessageSeed = {\n id: string;\n messageId: number;\n text: string;\n authorId: string;\n authorName: string;\n sentAt: Date;\n chatId: string;\n chatType: \"private\" | \"group\";\n chatTitle?: string;\n threadId?: number;\n replyTo?: TelegramReplyContext;\n};\n\ntype QueueEntry = {\n context: TelegramMessageContext;\n kind: TriggerKind;\n message: TelegramMessageSeed;\n receivedAt: number;\n};\n\ntype ConversationQueueState = {\n running: boolean;\n entries: QueueEntry[];\n};\n\ntype TelegramGatewayState = {\n identityConfig: PlatformIdentityRef;\n runtime: LoadedRuntimeConfig;\n hooks: HookRegistry;\n services: ServiceRegistry;\n telegramConfig: TelegramPlatformIdentityConfig;\n logger: Logger;\n identity: TelegramIdentity;\n allowedChatIds?: Set<string>;\n allowedUsernames?: Set<string>;\n queues: Map<string, ConversationQueueState>;\n};\n\ntype AsyncIteratorWaiter = {\n resolve: (value: IteratorResult<string>) => void;\n reject: (error: Error) => void;\n};\n\nconst queueEntryTtlMs = 60_000;\nconst maxQueueSize = 20;\n\nexport type StopFn = () => Promise<void>;\n\nexport const startTelegramIdentities = async (\n config: LoadedRuntimeConfig,\n hooks: HookRegistry,\n services: ServiceRegistry,\n logger: Logger,\n): Promise<StopFn[]> => {\n const env = loadTelegramGatewayEnv(process.env);\n const telegramConfigs = parseTelegramPlatformConfig(config.platform);\n const identities = Object.values(config.platformIdentities).filter(\n (identity): identity is PlatformIdentityRef => identity.platform === \"telegram\",\n );\n\n const stops: StopFn[] = [];\n\n for (const identityConfig of identities) {\n const telegramConfig = telegramConfigs[identityConfig.id];\n if (telegramConfig === undefined) {\n throw new Error(`Missing Telegram runtime config for identity \"${identityConfig.id}\"`);\n }\n\n const botToken = resolveTelegramBotToken(telegramConfig, env);\n const bot = new Bot<TelegramGatewayContext>(botToken);\n bot.api.config.use(autoRetry());\n bot.use(stream());\n\n const me = await bot.api.getMe();\n const allowedChatIds = resolveAllowedChatIds(telegramConfig, env);\n const allowedUsernames = resolveAllowedUsernames(telegramConfig, env);\n const identityLogger = logger.child({ identityId: identityConfig.id, entityId: identityConfig.entityId });\n const state: TelegramGatewayState = {\n identityConfig,\n runtime: config,\n hooks,\n services,\n telegramConfig,\n logger: identityLogger,\n identity: {\n botId: me.id,\n username: me.username,\n },\n allowedChatIds,\n allowedUsernames,\n queues: new Map<string, ConversationQueueState>(),\n };\n\n identityLogger.info(\"telegram.gateway_initialized\", {\n botId: me.id,\n botUsername: me.username,\n allowedChatCount: allowedChatIds?.size ?? 0,\n allowedUsernameCount: allowedUsernames?.size ?? 0,\n });\n\n registerTelegramHandlers(bot, state);\n const runner = run(bot);\n\n process.stdout.write(\n `Jar Telegram identity ${identityConfig.id} running in long polling mode\\n`,\n );\n identityLogger.info(\"telegram.gateway_started\");\n\n stops.push(async () => {\n identityLogger.info(\"telegram.gateway_stopping\");\n runner.stop();\n });\n }\n\n return stops;\n};\n\nconst registerTelegramHandlers = (\n bot: Bot<TelegramGatewayContext>,\n state: TelegramGatewayState,\n): void => {\n bot.command(\"btw\", async (context) => {\n const messageContext = context as TelegramMessageContext;\n if (!isAllowedChat(messageContext.chat.id, state)) {\n return;\n }\n\n if (!isAllowedUsername(messageContext.msg.from?.username, state)) {\n return;\n }\n\n const text = readTelegramMessageText(messageContext.msg);\n const parsed = text ? parseSideQuestionCommand({\n input: text,\n parentThreadId: \"preview\",\n source: {\n platform: \"telegram\",\n identityId: state.identityConfig.id,\n },\n }) : null;\n if (parsed === null) {\n await messageContext.reply(\"Usage: /btw <question>\", createReplyOptions(messageContext));\n return;\n }\n\n const normalized = normalizeTelegramMessageSeed(messageContext, state.identity);\n if (!normalized) {\n await messageContext.reply(\n \"I could not read that /btw question.\",\n createReplyOptions(messageContext),\n );\n return;\n }\n\n const threadId = buildThreadIdFromScope({\n platform: \"telegram\",\n identityId: state.identityConfig.id,\n scope: normalized.threadId === undefined\n ? {\n kind: \"telegram\",\n chatId: normalized.chatId,\n }\n : {\n kind: \"telegram\",\n chatId: normalized.chatId,\n messageThreadId: String(normalized.threadId),\n },\n });\n const requestLogger = state.logger.child({\n conversationKey: buildConversationKey(normalized.chatId, normalized.threadId),\n threadId,\n entityId: state.identityConfig.entityId,\n chatId: normalized.chatId,\n messageId: normalized.messageId,\n trigger: \"side_question\",\n });\n\n try {\n const result = await executeIngressCommand({\n config: state.runtime,\n hooks: state.hooks,\n logger: requestLogger,\n command: {\n kind: \"side_question\",\n source: {\n platform: \"telegram\",\n identityId: state.identityConfig.id,\n },\n parentThreadId: threadId,\n question: {\n text: parsed.question.text,\n },\n },\n });\n if (result.kind !== \"side_question\") {\n throw new Error(\"Telegram /btw expected a side-question execution result.\");\n }\n await messageContext.reply(\n result.outputText.trim().length > 0\n ? result.outputText\n : \"I do not have a side-question reply.\",\n createReplyOptions(messageContext),\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n requestLogger.error(\"telegram.side_question_failed\", {\n threadId,\n chatId: normalized.chatId,\n messageId: normalized.messageId,\n message,\n });\n await messageContext.reply(\n message.includes(\"No live parent thread available\")\n ? \"No active live thread is available for /btw in this chat yet.\"\n : \"I ran into an error while answering that /btw question.\",\n createReplyOptions(messageContext),\n );\n }\n });\n\n bot.command([\"start\", \"help\"], async (context) => {\n const messageContext = context as TelegramMessageContext;\n if (!isAllowedChat(messageContext.chat.id, state)) {\n return;\n }\n\n await messageContext.reply(\n \"Jar Telegram gateway is ready. Send a private message, or mention/reply to the bot in a group.\",\n createReplyOptions(messageContext),\n );\n });\n\n bot.on(\"message\", async (context) => {\n const messageContext = context as TelegramMessageContext;\n\n if (!isAllowedChat(messageContext.chat.id, state)) {\n state.logger.debug(\"telegram.event_ignored\", {\n reason: \"chat_not_allowed\",\n chatId: String(messageContext.chat.id),\n });\n return;\n }\n\n if (!isAllowedUsername(messageContext.msg.from?.username, state)) {\n state.logger.debug(\"telegram.event_ignored\", {\n reason: \"username_not_allowed\",\n chatId: String(messageContext.chat.id),\n username: messageContext.msg.from?.username,\n });\n return;\n }\n\n if (isTelegramSideQuestionCommand(readTelegramMessageText(messageContext.msg))) {\n state.logger.debug(\"telegram.event_ignored\", {\n reason: \"side_question_command\",\n chatId: String(messageContext.chat.id),\n messageId: messageContext.msg.message_id,\n });\n return;\n }\n\n const trigger = classifyMessageTrigger(messageContext, state.identity);\n if (!trigger) {\n state.logger.debug(\"telegram.event_ignored\", {\n reason: \"not_triggered\",\n chatId: String(messageContext.chat.id),\n messageId: messageContext.msg.message_id,\n });\n return;\n }\n\n const normalized = normalizeTelegramMessageSeed(messageContext, state.identity);\n if (!normalized) {\n state.logger.debug(\"telegram.event_ignored\", {\n reason: \"unsupported_message\",\n chatId: String(messageContext.chat.id),\n messageId: messageContext.msg.message_id,\n });\n return;\n }\n\n const conversationKey = buildConversationKey(\n normalized.chatId,\n normalized.threadId,\n );\n\n state.logger.info(\"telegram.event_received\", {\n trigger,\n conversationKey,\n chatId: normalized.chatId,\n messageId: normalized.messageId,\n textChars: normalized.text.length,\n });\n\n enqueueMessage(state, conversationKey, {\n context: messageContext,\n kind: trigger,\n message: normalized,\n receivedAt: Date.now(),\n });\n });\n\n bot.catch((error) => {\n const context = error.ctx;\n if (error.error instanceof GrammyError) {\n state.logger.error(\"telegram.middleware_failed\", {\n chatId: context.chat?.id !== undefined ? String(context.chat.id) : undefined,\n messageId: context.msg?.message_id,\n message: error.error.description,\n });\n return;\n }\n\n if (error.error instanceof HttpError) {\n state.logger.error(\"telegram.middleware_failed\", {\n chatId: context.chat?.id !== undefined ? String(context.chat.id) : undefined,\n messageId: context.msg?.message_id,\n message: error.error.message,\n });\n return;\n }\n\n const message = error.error instanceof Error\n ? error.error.message\n : String(error.error);\n\n state.logger.error(\"telegram.middleware_failed\", {\n chatId: context.chat?.id !== undefined ? String(context.chat.id) : undefined,\n messageId: context.msg?.message_id,\n message,\n });\n });\n};\n\nconst resolveTelegramBotToken = (\n telegramConfig: TelegramPlatformIdentityConfig,\n env: TelegramGatewayEnv,\n): string => {\n const botToken =\n env.TELEGRAM_BOT_TOKEN ?? telegramConfig.botToken ?? \"\";\n\n if (!botToken) {\n throw new Error(\"Missing Telegram credentials: TELEGRAM_BOT_TOKEN\");\n }\n\n return botToken;\n};\n\nconst resolveAllowedChatIds = (\n telegramConfig: TelegramPlatformIdentityConfig,\n env: TelegramGatewayEnv,\n): Set<string> | undefined => {\n if (env.JARVIS_TELEGRAM_ALLOWED_CHAT_IDS) {\n const values = env.JARVIS_TELEGRAM_ALLOWED_CHAT_IDS\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n\n return values.length > 0 ? new Set(values) : undefined;\n }\n\n const configured = telegramConfig.allowedChatIds;\n if (!configured || configured.length === 0) {\n return undefined;\n }\n\n return new Set(configured);\n};\n\nconst resolveAllowedUsernames = (\n telegramConfig: TelegramPlatformIdentityConfig,\n env: TelegramGatewayEnv,\n): Set<string> | undefined => {\n if (env.JARVIS_TELEGRAM_ALLOWED_USERNAMES) {\n const values = env.JARVIS_TELEGRAM_ALLOWED_USERNAMES\n .split(\",\")\n .map((value) => value.trim().replace(/^@/, \"\").toLowerCase())\n .filter((value) => value.length > 0);\n\n return values.length > 0 ? new Set(values) : undefined;\n }\n\n const configured = telegramConfig.allowedUsernames;\n if (!configured || configured.length === 0) {\n return undefined;\n }\n\n return new Set(configured);\n};\n\nconst isAllowedChat = (\n chatId: number,\n state: TelegramGatewayState,\n): boolean => {\n if (!state.allowedChatIds) {\n return true;\n }\n\n return state.allowedChatIds.has(String(chatId));\n};\n\nconst isAllowedUsername = (\n username: string | undefined,\n state: TelegramGatewayState,\n): boolean => {\n if (!state.allowedUsernames) {\n return true;\n }\n\n if (!username) {\n return false;\n }\n\n return state.allowedUsernames.has(username.toLowerCase());\n};\n\nconst classifyMessageTrigger = (\n context: TelegramMessageContext,\n identity: TelegramIdentity,\n): TriggerKind | null => {\n if (context.chat.type === \"private\") {\n return \"private_chat\";\n }\n\n const replyTo = context.msg.reply_to_message;\n if (replyTo?.from?.id === identity.botId) {\n return \"reply_to_bot\";\n }\n\n const text = readTelegramMessageText(context.msg);\n if (!text) {\n return null;\n }\n\n const username = identity.username;\n if (!username) {\n return null;\n }\n\n const normalizedMention = `@${username.toLowerCase()}`;\n return text.toLowerCase().includes(normalizedMention) ? \"mention\" : null;\n};\n\nconst normalizeTelegramMessageSeed = (\n context: TelegramMessageContext,\n identity: TelegramIdentity,\n): TelegramMessageSeed | null => {\n const text = readTelegramMessageText(context.msg);\n const from = context.msg.from;\n\n if (!text || !from) {\n return null;\n }\n\n const sentAt = new Date(context.msg.date * 1000);\n const normalizedText = stripBotMention(text, identity.username);\n\n if (!normalizedText) {\n return null;\n }\n\n const chatTitle = readChatTitle(context.chat);\n const replyTo = readReplyContext(context.msg.reply_to_message);\n\n return {\n id: String(context.msg.message_id),\n messageId: context.msg.message_id,\n text: normalizedText,\n authorId: String(from.id),\n authorName: formatTelegramAuthorName(from),\n sentAt,\n chatId: String(context.chat.id),\n chatType: context.chat.type === \"private\" ? \"private\" : \"group\",\n chatTitle,\n threadId: context.msg.message_thread_id,\n replyTo,\n };\n};\n\nconst readTelegramMessageText = (\n message: TelegramReadableMessage | undefined,\n): string | undefined => {\n return message?.text ?? message?.caption;\n};\n\nexport const isTelegramSideQuestionCommand = (text: string | undefined): boolean => {\n return text !== undefined && parseSideQuestionCommand({\n input: text,\n parentThreadId: \"preview\",\n source: { platform: \"telegram\" },\n }) !== null;\n};\n\nconst stripBotMention = (\n text: string,\n username: string | undefined,\n): string => {\n if (!username) {\n return text.trim();\n }\n\n return text.replaceAll(`@${username}`, \"\").trim();\n};\n\nconst formatTelegramAuthorName = (\n from: TelegramUserLike,\n): string => {\n const parts = [from.first_name, from.last_name].filter(\n (part): part is string => Boolean(part),\n );\n\n if (parts.length > 0) {\n return parts.join(\" \");\n }\n\n return from.username ?? String(from.id);\n};\n\nconst readChatTitle = (\n chat: TelegramMessageContext[\"chat\"],\n): string | undefined => {\n switch (chat.type) {\n case \"group\":\n case \"supergroup\":\n return chat.title;\n case \"private\":\n return undefined;\n }\n};\n\nconst readReplyContext = (\n replyTo: TelegramMessageContext[\"msg\"][\"reply_to_message\"],\n): TelegramReplyContext | undefined => {\n const text = replyTo ? readTelegramMessageText(replyTo) : undefined;\n const from = replyTo?.from;\n if (!replyTo || !text || !from) {\n return undefined;\n }\n\n return {\n authorId: String(from.id),\n authorName: formatTelegramAuthorName(from),\n text,\n sentAt: new Date(replyTo.date * 1000),\n };\n};\n\nconst enqueueMessage = (\n state: TelegramGatewayState,\n conversationKey: string,\n entry: QueueEntry,\n): void => {\n const queue = getConversationQueue(state, conversationKey);\n const now = Date.now();\n\n queue.entries = queue.entries.filter(\n (queued) => now - queued.receivedAt <= queueEntryTtlMs,\n );\n queue.entries.push(entry);\n\n if (queue.entries.length > maxQueueSize) {\n queue.entries.splice(0, queue.entries.length - maxQueueSize);\n }\n\n state.logger.info(\"telegram.queue_enqueued\", {\n conversationKey,\n trigger: entry.kind,\n queueSize: queue.entries.length,\n chatId: entry.message.chatId,\n messageId: entry.message.messageId,\n });\n\n if (!queue.running) {\n queue.running = true;\n void drainQueue(state, conversationKey, queue);\n }\n};\n\nconst drainQueue = async (\n state: TelegramGatewayState,\n conversationKey: string,\n queue: ConversationQueueState,\n): Promise<void> => {\n try {\n while (queue.entries.length > 0) {\n const now = Date.now();\n queue.entries = queue.entries.filter(\n (queued) => now - queued.receivedAt <= queueEntryTtlMs,\n );\n\n if (queue.entries.length === 0) {\n break;\n }\n\n const batch = queue.entries.splice(0, queue.entries.length);\n if (batch.length === 0) {\n continue;\n }\n\n const current = batch[batch.length - 1]!;\n const skipped = batch.slice(0, -1).map((item) => item.message);\n\n state.logger.info(\"telegram.queue_draining\", {\n conversationKey,\n batchSize: batch.length,\n skippedCount: skipped.length,\n trigger: current.kind,\n });\n\n try {\n await handleQueueEntry(state, conversationKey, current, skipped);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n state.logger.error(\"telegram.queue_entry_failed\", {\n conversationKey,\n message,\n });\n }\n }\n } finally {\n queue.running = false;\n }\n};\n\nconst handleQueueEntry = async (\n state: TelegramGatewayState,\n conversationKey: string,\n entry: QueueEntry,\n skipped: TelegramMessageSeed[],\n): Promise<void> => {\n const threadId = buildThreadIdFromScope({\n platform: \"telegram\",\n identityId: state.identityConfig.id,\n scope: entry.message.threadId === undefined\n ? {\n kind: \"telegram\",\n chatId: entry.message.chatId,\n }\n : {\n kind: \"telegram\",\n chatId: entry.message.chatId,\n messageThreadId: String(entry.message.threadId),\n },\n });\n const requestLogger = state.logger.child({\n conversationKey,\n threadId,\n entityId: state.identityConfig.entityId,\n chatId: entry.message.chatId,\n messageId: entry.message.messageId,\n trigger: entry.kind,\n });\n const current = materializeTelegramMessage(entry.message);\n const skippedMessages = skipped.map(materializeTelegramMessage);\n const prompt = buildTelegramPrompt({\n message: current,\n skipped: skippedMessages,\n chatType: current.chatTitle || entry.message.chatType === \"group\"\n ? \"group\"\n : \"private\",\n });\n\n requestLogger.info(\"telegram.request_started\", {\n skippedCount: skippedMessages.length,\n currentAuthor: current.authorName,\n promptChars: prompt.length,\n });\n\n const sideQuestion = await maybeExecuteSideQuestionIngress({\n config: state.runtime,\n parentThreadId: threadId,\n input: current.text,\n source: {\n platform: \"telegram\",\n identityId: state.identityConfig.id,\n },\n logger: requestLogger,\n });\n\n if (sideQuestion.handled) {\n await entry.context.reply(\n sideQuestion.result.outputText.trim().length > 0\n ? sideQuestion.result.outputText\n : \"I do not have a side-question reply.\",\n createReplyOptions(entry.context),\n );\n return;\n }\n\n await respondInTelegramConversation({\n state,\n context: entry.context,\n conversationKey,\n threadId,\n prompt,\n skillTriggerText: buildTelegramSkillTriggerText(current, skippedMessages),\n logger: requestLogger,\n });\n};\n\nconst materializeTelegramMessage = (\n seed: TelegramMessageSeed,\n): TelegramMessage => {\n return {\n id: seed.id,\n text: seed.text,\n authorId: seed.authorId,\n authorName: seed.authorName,\n sentAt: seed.sentAt,\n chatTitle: seed.chatTitle,\n replyTo: seed.replyTo,\n };\n};\n\nconst respondInTelegramConversation = async (options: {\n state: TelegramGatewayState;\n context: TelegramMessageContext;\n conversationKey: string;\n threadId: string;\n prompt: string;\n skillTriggerText: string;\n logger: Logger;\n}): Promise<void> => {\n const startedAt = Date.now();\n const textStream = new AsyncTextDeltaQueue();\n let emittedText = false;\n let turnId: string | undefined;\n let runId: string | undefined;\n\n // Retrieve contributions lazily — by the time events fire, jar-runtime has\n // already registered them in the services registry after manager.load().\n const contributions = options.state.services.get<PluginContribution>(\"pluginContributions\");\n\n const responseTask = executeIngressCommand({\n config: options.state.runtime,\n hooks: options.state.hooks,\n logger: options.logger,\n pluginOverrides: contributions ?? { skillRoots: [], overlays: [], tools: [] },\n command: {\n kind: \"message\",\n source: {\n platform: \"telegram\",\n },\n routing: {\n platform: \"telegram\",\n scope: options.context.msg.message_thread_id === undefined\n ? {\n kind: \"telegram\",\n chatId: String(options.context.chat.id),\n }\n : {\n kind: \"telegram\",\n chatId: String(options.context.chat.id),\n messageThreadId: String(options.context.msg.message_thread_id),\n },\n },\n message: {\n text: options.skillTriggerText,\n id: String(options.context.msg.message_id),\n },\n prompt: options.prompt,\n skillTriggerText: options.skillTriggerText,\n audit: {\n trigger: \"platform_event\",\n triggerKind: \"telegram_message\",\n metadata: {\n conversationKey: options.conversationKey,\n chatId: String(options.context.chat.id),\n messageId: options.context.msg.message_id,\n },\n },\n execution: {\n onEvent(event) {\n if (\n event.type === \"message_update\" &&\n event.assistantMessageEvent.type === \"text_delta\"\n ) {\n emittedText = true;\n textStream.push(event.assistantMessageEvent.delta);\n }\n },\n },\n } satisfies MessageIngressCommand,\n })\n .then((result) => {\n const { outputText } = result;\n if (!emittedText) {\n textStream.push(\n outputText.trim().length > 0\n ? outputText\n : \"I finished processing that, but I do not have a textual reply to send.\",\n );\n }\n\n textStream.close();\n return result;\n })\n .catch((error: unknown) => {\n textStream.fail(toError(error));\n throw error;\n });\n\n try {\n await options.context.replyWithStream(\n textStream,\n createReplyOptions(options.context),\n );\n const result = await responseTask;\n if (result.kind !== \"message\") {\n throw new Error(\"Telegram reply generation expected a message execution result.\");\n }\n turnId = result.turnId;\n runId = result.runId;\n const outputText = result.outputText;\n\n options.logger.info(\"telegram.reply_posted\", {\n durationMs: Date.now() - startedAt,\n replyChars: outputText.trim().length,\n threadId: options.threadId,\n turnId: result.turnId,\n runId: result.runId,\n conversationKey: options.conversationKey,\n streamed: true,\n });\n } catch (error) {\n const settledResult = await responseTask.catch(() => undefined);\n if (settledResult?.kind === \"message\") {\n turnId = turnId ?? settledResult.turnId;\n runId = runId ?? settledResult.runId;\n }\n const normalizedError = toError(error);\n const envelope = getFaultEnvelope(error);\n options.logger.error(\"telegram.reply_failed\", {\n durationMs: Date.now() - startedAt,\n message: normalizedError.message,\n threadId: options.threadId,\n turnId: turnId ?? envelope?.turnId,\n runId: runId ?? envelope?.runId,\n conversationKey: options.conversationKey,\n fault: envelope?.fault,\n phase: envelope?.phase,\n });\n\n if (!emittedText) {\n await options.context.reply(\n \"I ran into an error while processing that request. Please try again in the same chat.\",\n createReplyOptions(options.context),\n );\n }\n }\n};\n\nconst buildTelegramSkillTriggerText = (\n currentMessage: TelegramMessage,\n skipped: TelegramMessage[],\n): string => {\n return [...skipped, currentMessage]\n .map((message) => message.text.trim())\n .filter((text) => text.length > 0)\n .join(\"\\n\");\n};\n\nconst createReplyOptions = (\n context: TelegramMessageContext,\n): Record<string, number | { message_id: number }> => {\n const options: Record<string, number | { message_id: number }> = {\n reply_parameters: {\n message_id: context.msg.message_id,\n },\n };\n\n if (context.msg.message_thread_id !== undefined) {\n options.message_thread_id = context.msg.message_thread_id;\n }\n\n return options;\n};\n\nconst getConversationQueue = (\n state: TelegramGatewayState,\n conversationKey: string,\n): ConversationQueueState => {\n const existing = state.queues.get(conversationKey);\n if (existing) {\n return existing;\n }\n\n const created: ConversationQueueState = {\n running: false,\n entries: [],\n };\n state.queues.set(conversationKey, created);\n return created;\n};\n\nconst buildConversationKey = (\n chatId: string,\n threadId: number | undefined,\n): string => {\n return threadId === undefined ? chatId : `${chatId}:${threadId}`;\n};\n\nconst loadTelegramGatewayEnv = (\n rawEnv: NodeJS.ProcessEnv,\n): TelegramGatewayEnv => {\n return telegramGatewayEnvSchema.parse(rawEnv);\n};\n\nconst toError = (error: unknown): Error => {\n return error instanceof Error ? error : new Error(String(error));\n};\n\nclass AsyncTextDeltaQueue implements AsyncIterable<string> {\n private readonly chunks: string[] = [];\n private readonly waiters: AsyncIteratorWaiter[] = [];\n private completed = false;\n private failure: Error | null = null;\n\n push(chunk: string): void {\n if (!chunk) {\n return;\n }\n\n const waiter = this.waiters.shift();\n if (waiter) {\n waiter.resolve({\n done: false,\n value: chunk,\n });\n return;\n }\n\n this.chunks.push(chunk);\n }\n\n close(): void {\n this.completed = true;\n\n while (this.waiters.length > 0) {\n const waiter = this.waiters.shift();\n waiter?.resolve({\n done: true,\n value: undefined,\n });\n }\n }\n\n fail(error: Error): void {\n this.failure = error;\n\n while (this.waiters.length > 0) {\n this.waiters.shift()?.reject(error);\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<string> {\n return {\n next: async (): Promise<IteratorResult<string>> => {\n if (this.chunks.length > 0) {\n return {\n done: false,\n value: this.chunks.shift()!,\n };\n }\n\n if (this.failure) {\n throw this.failure;\n }\n\n if (this.completed) {\n return {\n done: true,\n value: undefined,\n };\n }\n\n return new Promise<IteratorResult<string>>((resolve, reject) => {\n this.waiters.push({\n resolve,\n reject,\n });\n });\n },\n };\n }\n}\n","import type {\n JarPlugin,\n PluginFactory,\n PluginInstallContext,\n PluginInstallResult,\n Logger,\n} from \"@hijarvis/core\";\nimport { createLogger } from \"@hijarvis/core\";\nimport { startTelegramIdentities, type StopFn } from \"./runtime.js\";\n\nexport const createPlugin: PluginFactory = (_pluginConfig) => {\n const plugin: JarPlugin = {\n name: \"jar-gateway-telegram\",\n async install({ config, hooks, services, logger }: PluginInstallContext): Promise<PluginInstallResult> {\n const effectiveLogger: Logger = logger ?? createLogger({ level: \"info\", stderr: true });\n const stops: StopFn[] = await startTelegramIdentities(config, hooks, services, effectiveLogger);\n return {\n cleanup: async () => {\n for (const stop of stops) {\n await stop();\n }\n },\n };\n },\n };\n return plugin;\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@hijarvis/plugin-telegram",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "author": "wibus-wee",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/wibus-wee/HiJarvis.git",
10
+ "directory": "packages/jar-plugin-telegram"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/plugin.d.mts",
18
+ "import": "./dist/plugin.mjs"
19
+ }
20
+ },
21
+ "main": "./dist/plugin.mjs",
22
+ "types": "./dist/plugin.d.mts",
23
+ "dependencies": {
24
+ "@grammyjs/auto-retry": "^2.0.2",
25
+ "@grammyjs/runner": "^2.0.3",
26
+ "@grammyjs/stream": "^1.0.1",
27
+ "grammy": "^1.42.0",
28
+ "zod": "^4.3.6",
29
+ "@hijarvis/core": "0.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "tsx": "^4.21.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsdown",
36
+ "check": "tsc --noEmit",
37
+ "test": "tsx --test src/*.test.ts src/**/*.test.ts"
38
+ }
39
+ }