@diegoaltoworks/chatter 0.58.0 → 0.60.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/channels/index.js +81 -0
- package/dist/channels/index.mjs +81 -0
- package/dist/channels/pipeline.d.ts +8 -0
- package/dist/channels/pipeline.d.ts.map +1 -1
- package/dist/channels/telegram/api.d.ts +17 -0
- package/dist/channels/telegram/api.d.ts.map +1 -1
- package/dist/channels/telegram/channel.d.ts +11 -1
- package/dist/channels/telegram/channel.d.ts.map +1 -1
- package/dist/channels/telegram/handler.d.ts +31 -0
- package/dist/channels/telegram/handler.d.ts.map +1 -0
- package/dist/channels/telegram/index.d.ts +7 -0
- package/dist/channels/telegram/index.d.ts.map +1 -1
- package/dist/channels/telegram/index.js +382 -79
- package/dist/channels/telegram/index.mjs +379 -79
- package/dist/channels/telegram/webhook.d.ts +102 -0
- package/dist/channels/telegram/webhook.d.ts.map +1 -0
- package/dist/channels/whatsapp/inbound.d.ts +7 -0
- package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.js +81 -0
- package/dist/channels/whatsapp/index.mjs +81 -0
- package/dist/history/compaction.d.ts +61 -0
- package/dist/history/compaction.d.ts.map +1 -0
- package/dist/history/index.d.ts +7 -0
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +143 -0
- package/dist/history/index.mjs +141 -0
- package/package.json +1 -1
|
@@ -146,6 +146,17 @@ function createTelegramApi(config) {
|
|
|
146
146
|
message_id: messageId,
|
|
147
147
|
reaction: [{ type: "emoji", emoji }]
|
|
148
148
|
});
|
|
149
|
+
},
|
|
150
|
+
async setWebhook(url, options) {
|
|
151
|
+
await call("setWebhook", {
|
|
152
|
+
url,
|
|
153
|
+
// Only what this channel interprets — see the matching comment on getUpdates.
|
|
154
|
+
allowed_updates: ["message"],
|
|
155
|
+
...options?.secretToken ? { secret_token: options.secretToken } : {}
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
async deleteWebhook() {
|
|
159
|
+
await call("deleteWebhook");
|
|
149
160
|
}
|
|
150
161
|
};
|
|
151
162
|
}
|
|
@@ -166,56 +177,6 @@ function createConsoleLogger(level = "info") {
|
|
|
166
177
|
};
|
|
167
178
|
}
|
|
168
179
|
|
|
169
|
-
// src/channels/gates.ts
|
|
170
|
-
function matches(re, text) {
|
|
171
|
-
return new RegExp(re.source, re.flags.replace(/[gy]/g, "")).test(text);
|
|
172
|
-
}
|
|
173
|
-
function isBlockedByAllowlist(msg, config) {
|
|
174
|
-
return !msg.fromBot && msg.text.trim().length > 0 && !msg.isDirectMessage && config.allowedChats.length > 0 && !config.allowedChats.includes(msg.chatId);
|
|
175
|
-
}
|
|
176
|
-
function decideChannelAction(msg, config) {
|
|
177
|
-
if (msg.fromBot || !msg.text.trim()) return "ignore";
|
|
178
|
-
if (!msg.isDirectMessage) {
|
|
179
|
-
if (isBlockedByAllowlist(msg, config)) return "ignore";
|
|
180
|
-
if (config.muteRegex && matches(config.muteRegex, msg.text)) return "mute";
|
|
181
|
-
if (config.unmuteRegex && matches(config.unmuteRegex, msg.text)) return "unmute";
|
|
182
|
-
if (config.mutedChats.has(msg.chatId)) return "ignore";
|
|
183
|
-
return msg.mentionsBot || msg.isReplyToBot ? "reply" : "ignore";
|
|
184
|
-
}
|
|
185
|
-
return "reply";
|
|
186
|
-
}
|
|
187
|
-
function createSlidingWindowRateLimiter(maxPerWindow, windowMs, now = Date.now) {
|
|
188
|
-
const log = /* @__PURE__ */ new Map();
|
|
189
|
-
let lastSweep = now();
|
|
190
|
-
function sweep(current) {
|
|
191
|
-
if (current - lastSweep < windowMs) return;
|
|
192
|
-
lastSweep = current;
|
|
193
|
-
const cutoff = current - windowMs;
|
|
194
|
-
for (const [key, entries] of log) {
|
|
195
|
-
if (!entries.some((t) => t > cutoff)) log.delete(key);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function allow(key) {
|
|
199
|
-
const current = now();
|
|
200
|
-
sweep(current);
|
|
201
|
-
const cutoff = current - windowMs;
|
|
202
|
-
const entries = (log.get(key) ?? []).filter((t) => t > cutoff);
|
|
203
|
-
if (entries.length >= maxPerWindow) {
|
|
204
|
-
if (entries.length > 0) log.set(key, entries);
|
|
205
|
-
else log.delete(key);
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
entries.push(current);
|
|
209
|
-
log.set(key, entries);
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
allow.size = () => log.size;
|
|
213
|
-
return allow;
|
|
214
|
-
}
|
|
215
|
-
function underReplyRateLimit(msg, limiters) {
|
|
216
|
-
return msg.isDirectMessage ? limiters.dm(msg.chatId) : limiters.group(msg.chatId);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
180
|
// src/core/guardrails.ts
|
|
220
181
|
function detectLeakage(text) {
|
|
221
182
|
const flags = [
|
|
@@ -387,6 +348,125 @@ ${ctx.join("\n\n")}`
|
|
|
387
348
|
return { system, messages, context: ctx };
|
|
388
349
|
}
|
|
389
350
|
|
|
351
|
+
// src/history/compaction.ts
|
|
352
|
+
var DEFAULT_TIMEOUT_MS = 8e3;
|
|
353
|
+
var LOAD_ALL = Number.MAX_SAFE_INTEGER;
|
|
354
|
+
var SUMMARY_PREFIX = "Summary of earlier conversation: ";
|
|
355
|
+
function defaultSummarize(client, model) {
|
|
356
|
+
return async ({ turns }) => {
|
|
357
|
+
const transcript = turns.map((turn) => `${turn.role}: ${turn.content}`).join("\n");
|
|
358
|
+
const { content } = await answerOnce({
|
|
359
|
+
client,
|
|
360
|
+
system: "Summarize the conversation turns below into a short paragraph a reader would need to continue the conversation. Preserve names, facts, and decisions; drop pleasantries. Reply with the summary only, no preamble.",
|
|
361
|
+
messages: [{ role: "user", content: transcript }],
|
|
362
|
+
// No `answerFn`: compaction is an internal chatter operation, not a
|
|
363
|
+
// chat surface, so it must not conscript a caller's brain hook, which
|
|
364
|
+
// is shaped to answer the user, not to summarize on the engine's behalf.
|
|
365
|
+
mode: "public",
|
|
366
|
+
model
|
|
367
|
+
});
|
|
368
|
+
return content;
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function createHistoryCompactor(deps, options) {
|
|
372
|
+
const { threshold, keep, logger } = options;
|
|
373
|
+
if (!Number.isInteger(threshold) || threshold < 1) {
|
|
374
|
+
throw new Error(`Invalid history compaction threshold: ${threshold}`);
|
|
375
|
+
}
|
|
376
|
+
if (!Number.isInteger(keep) || keep < 0 || keep >= threshold) {
|
|
377
|
+
throw new Error(`Invalid history compaction keep: ${keep}`);
|
|
378
|
+
}
|
|
379
|
+
const summarize = options.summarize ?? defaultSummarize(deps.client, options.model);
|
|
380
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
381
|
+
return {
|
|
382
|
+
async maybeCompact(store, conversationId) {
|
|
383
|
+
const turns = await store.load(conversationId, LOAD_ALL);
|
|
384
|
+
if (turns.length < threshold) return;
|
|
385
|
+
const older = turns.slice(0, turns.length - keep);
|
|
386
|
+
let summary;
|
|
387
|
+
let timer;
|
|
388
|
+
try {
|
|
389
|
+
summary = await Promise.race([
|
|
390
|
+
summarize({ conversationId, turns: older }),
|
|
391
|
+
new Promise((_, reject) => {
|
|
392
|
+
timer = setTimeout(() => reject(new Error("history compaction timed out")), timeoutMs);
|
|
393
|
+
})
|
|
394
|
+
]);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
logger?.error(
|
|
397
|
+
`history compaction summarize failed for "${conversationId}"; history left untouched`,
|
|
398
|
+
error
|
|
399
|
+
);
|
|
400
|
+
return;
|
|
401
|
+
} finally {
|
|
402
|
+
clearTimeout(timer);
|
|
403
|
+
}
|
|
404
|
+
const trimmed = summary.trim();
|
|
405
|
+
if (!trimmed) return;
|
|
406
|
+
const fresh = await store.load(conversationId, LOAD_ALL);
|
|
407
|
+
const recent = fresh.slice(older.length);
|
|
408
|
+
await store.clear(conversationId);
|
|
409
|
+
await store.append(conversationId, {
|
|
410
|
+
role: "assistant",
|
|
411
|
+
content: `${SUMMARY_PREFIX}${trimmed}`
|
|
412
|
+
});
|
|
413
|
+
for (const turn of recent) {
|
|
414
|
+
await store.append(conversationId, turn);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/channels/gates.ts
|
|
421
|
+
function matches(re, text) {
|
|
422
|
+
return new RegExp(re.source, re.flags.replace(/[gy]/g, "")).test(text);
|
|
423
|
+
}
|
|
424
|
+
function isBlockedByAllowlist(msg, config) {
|
|
425
|
+
return !msg.fromBot && msg.text.trim().length > 0 && !msg.isDirectMessage && config.allowedChats.length > 0 && !config.allowedChats.includes(msg.chatId);
|
|
426
|
+
}
|
|
427
|
+
function decideChannelAction(msg, config) {
|
|
428
|
+
if (msg.fromBot || !msg.text.trim()) return "ignore";
|
|
429
|
+
if (!msg.isDirectMessage) {
|
|
430
|
+
if (isBlockedByAllowlist(msg, config)) return "ignore";
|
|
431
|
+
if (config.muteRegex && matches(config.muteRegex, msg.text)) return "mute";
|
|
432
|
+
if (config.unmuteRegex && matches(config.unmuteRegex, msg.text)) return "unmute";
|
|
433
|
+
if (config.mutedChats.has(msg.chatId)) return "ignore";
|
|
434
|
+
return msg.mentionsBot || msg.isReplyToBot ? "reply" : "ignore";
|
|
435
|
+
}
|
|
436
|
+
return "reply";
|
|
437
|
+
}
|
|
438
|
+
function createSlidingWindowRateLimiter(maxPerWindow, windowMs, now = Date.now) {
|
|
439
|
+
const log = /* @__PURE__ */ new Map();
|
|
440
|
+
let lastSweep = now();
|
|
441
|
+
function sweep(current) {
|
|
442
|
+
if (current - lastSweep < windowMs) return;
|
|
443
|
+
lastSweep = current;
|
|
444
|
+
const cutoff = current - windowMs;
|
|
445
|
+
for (const [key, entries] of log) {
|
|
446
|
+
if (!entries.some((t) => t > cutoff)) log.delete(key);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function allow(key) {
|
|
450
|
+
const current = now();
|
|
451
|
+
sweep(current);
|
|
452
|
+
const cutoff = current - windowMs;
|
|
453
|
+
const entries = (log.get(key) ?? []).filter((t) => t > cutoff);
|
|
454
|
+
if (entries.length >= maxPerWindow) {
|
|
455
|
+
if (entries.length > 0) log.set(key, entries);
|
|
456
|
+
else log.delete(key);
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
entries.push(current);
|
|
460
|
+
log.set(key, entries);
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
allow.size = () => log.size;
|
|
464
|
+
return allow;
|
|
465
|
+
}
|
|
466
|
+
function underReplyRateLimit(msg, limiters) {
|
|
467
|
+
return msg.isDirectMessage ? limiters.dm(msg.chatId) : limiters.group(msg.chatId);
|
|
468
|
+
}
|
|
469
|
+
|
|
390
470
|
// src/channels/pipeline.ts
|
|
391
471
|
var DEFAULT_HISTORY_LIMIT = 20;
|
|
392
472
|
var DEFAULT_GROUP_RATE_LIMIT = { max: 30, windowMs: 60 * 60 * 1e3 };
|
|
@@ -417,6 +497,10 @@ function createInboundPipeline(deps, config) {
|
|
|
417
497
|
const underGroupRateLimit = createSlidingWindowRateLimiter(group.max, group.windowMs, now);
|
|
418
498
|
const underDmRateLimit = createSlidingWindowRateLimiter(dm.max, dm.windowMs, now);
|
|
419
499
|
const mode = "public";
|
|
500
|
+
const compactor = config.history?.compaction ? createHistoryCompactor(
|
|
501
|
+
{ client: deps.client },
|
|
502
|
+
{ model: config.model, ...config.history.compaction }
|
|
503
|
+
) : void 0;
|
|
420
504
|
return async function handleInboundMessage(msg, turn) {
|
|
421
505
|
const allowedChats = config.allowedChats ?? [];
|
|
422
506
|
const action = decideChannelAction(msg, {
|
|
@@ -492,6 +576,14 @@ function createInboundPipeline(deps, config) {
|
|
|
492
576
|
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
493
577
|
}
|
|
494
578
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
579
|
+
const history = config.history;
|
|
580
|
+
if (compactor && historyEnabled && history) {
|
|
581
|
+
try {
|
|
582
|
+
await compactor.maybeCompact(history.store, conversationId);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
config.logger?.error(`history compaction failed for "${conversationId}"`, error);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
495
587
|
return { action: "reply", content };
|
|
496
588
|
};
|
|
497
589
|
}
|
|
@@ -539,6 +631,41 @@ function telegramSenderKey(senderId) {
|
|
|
539
631
|
return `tg:${senderId}`;
|
|
540
632
|
}
|
|
541
633
|
|
|
634
|
+
// src/channels/telegram/handler.ts
|
|
635
|
+
function createTelegramUpdateHandler(deps) {
|
|
636
|
+
const { api, me, pipeline, allowedChats, logger, label } = deps;
|
|
637
|
+
const loggedUnallowedChats = /* @__PURE__ */ new Set();
|
|
638
|
+
return async function handleUpdate(update) {
|
|
639
|
+
const message = update.message;
|
|
640
|
+
const msg = toChannelMessage(update, me);
|
|
641
|
+
if (!msg || !message) return;
|
|
642
|
+
if (isBlockedByAllowlist(msg, { allowedChats })) {
|
|
643
|
+
if (!loggedUnallowedChats.has(msg.chatId)) {
|
|
644
|
+
loggedUnallowedChats.add(msg.chatId);
|
|
645
|
+
logger.warn(`${label}: skipped chat ${msg.chatId} - not in allowedChats`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const reply = {
|
|
649
|
+
// Threaded onto the incoming message: in a busy group an untethered
|
|
650
|
+
// answer reads as a non-sequitur.
|
|
651
|
+
sendAnswer: (chatId, text) => api.sendMessage(chatId, text, { replyToMessageId: message.message_id }),
|
|
652
|
+
sendGateReply: (chatId, text) => api.sendMessage(chatId, text)
|
|
653
|
+
};
|
|
654
|
+
await pipeline(msg, {
|
|
655
|
+
reply,
|
|
656
|
+
conversationId: msg.chatId,
|
|
657
|
+
sender: telegramSenderKey(msg.senderId)
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function createTelegramSender(api) {
|
|
662
|
+
return {
|
|
663
|
+
sendText: (chatId, text) => api.sendMessage(chatId, text),
|
|
664
|
+
sendMedia: (chatId, payload) => api.sendMedia(chatId, payload),
|
|
665
|
+
sendReaction: (chatId, messageRef, emoji) => api.setMessageReaction(chatId, Number(messageRef), emoji)
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
542
669
|
// src/channels/telegram/poll.ts
|
|
543
670
|
var BASE_DELAY_MS = 1e3;
|
|
544
671
|
var MAX_DELAY_MS = 6e4;
|
|
@@ -609,12 +736,7 @@ function createTelegramChannel(config) {
|
|
|
609
736
|
});
|
|
610
737
|
const me = await api.getMe();
|
|
611
738
|
const label = `Telegram[${me.username ?? me.id}]`;
|
|
612
|
-
|
|
613
|
-
sendText: (chatId, text) => api.sendMessage(chatId, text),
|
|
614
|
-
sendMedia: (chatId, payload) => api.sendMedia(chatId, payload),
|
|
615
|
-
sendReaction: (chatId, messageRef, emoji) => api.setMessageReaction(chatId, Number(messageRef), emoji)
|
|
616
|
-
};
|
|
617
|
-
deps.senders.register(channelName, sender);
|
|
739
|
+
deps.senders.register(channelName, createTelegramSender(api));
|
|
618
740
|
senders = deps.senders;
|
|
619
741
|
registered = true;
|
|
620
742
|
const pipeline = createInboundPipeline(
|
|
@@ -641,29 +763,14 @@ function createTelegramChannel(config) {
|
|
|
641
763
|
logger: log
|
|
642
764
|
}
|
|
643
765
|
);
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
log.warn(`${label}: skipped chat ${msg.chatId} - not in allowedChats`);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
const reply = {
|
|
656
|
-
// Threaded onto the incoming message: in a busy group an untethered
|
|
657
|
-
// answer reads as a non-sequitur.
|
|
658
|
-
sendAnswer: (chatId, text) => api.sendMessage(chatId, text, { replyToMessageId: message.message_id }),
|
|
659
|
-
sendGateReply: (chatId, text) => api.sendMessage(chatId, text)
|
|
660
|
-
};
|
|
661
|
-
await pipeline(msg, {
|
|
662
|
-
reply,
|
|
663
|
-
conversationId: msg.chatId,
|
|
664
|
-
sender: telegramSenderKey(msg.senderId)
|
|
665
|
-
});
|
|
666
|
-
}
|
|
766
|
+
const handleUpdate = createTelegramUpdateHandler({
|
|
767
|
+
api,
|
|
768
|
+
me,
|
|
769
|
+
pipeline,
|
|
770
|
+
allowedChats,
|
|
771
|
+
logger: log,
|
|
772
|
+
label
|
|
773
|
+
});
|
|
667
774
|
abort = new AbortController();
|
|
668
775
|
const signal = abort.signal;
|
|
669
776
|
void runLongPoll({
|
|
@@ -688,12 +795,205 @@ function createTelegramChannel(config) {
|
|
|
688
795
|
}
|
|
689
796
|
};
|
|
690
797
|
}
|
|
798
|
+
|
|
799
|
+
// src/channels/telegram/webhook.ts
|
|
800
|
+
import { timingSafeEqual } from "node:crypto";
|
|
801
|
+
|
|
802
|
+
// node_modules/hono/dist/http-exception.js
|
|
803
|
+
var HTTPException = class extends Error {
|
|
804
|
+
res;
|
|
805
|
+
status;
|
|
806
|
+
/**
|
|
807
|
+
* Creates an instance of `HTTPException`.
|
|
808
|
+
* @param status - HTTP status code for the exception. Defaults to 500.
|
|
809
|
+
* @param options - Additional options for the exception.
|
|
810
|
+
*/
|
|
811
|
+
constructor(status = 500, options) {
|
|
812
|
+
super(options?.message, { cause: options?.cause });
|
|
813
|
+
this.res = options?.res;
|
|
814
|
+
this.status = status;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Returns the response object associated with the exception.
|
|
818
|
+
* If a response object is not provided, a new response is created with the error message and status code.
|
|
819
|
+
* @returns The response object.
|
|
820
|
+
*/
|
|
821
|
+
getResponse() {
|
|
822
|
+
if (this.res) {
|
|
823
|
+
const newResponse = new Response(this.res.body, {
|
|
824
|
+
status: this.status,
|
|
825
|
+
headers: this.res.headers
|
|
826
|
+
});
|
|
827
|
+
return newResponse;
|
|
828
|
+
}
|
|
829
|
+
return new Response(this.message, {
|
|
830
|
+
status: this.status
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
// node_modules/hono/dist/middleware/body-limit/index.js
|
|
836
|
+
var ERROR_MESSAGE = "Payload Too Large";
|
|
837
|
+
var bodyLimit = (options) => {
|
|
838
|
+
const onError = options.onError || (() => {
|
|
839
|
+
const res = new Response(ERROR_MESSAGE, {
|
|
840
|
+
status: 413
|
|
841
|
+
});
|
|
842
|
+
throw new HTTPException(413, { res });
|
|
843
|
+
});
|
|
844
|
+
const maxSize = options.maxSize;
|
|
845
|
+
return async function bodyLimit2(c, next) {
|
|
846
|
+
if (!c.req.raw.body) {
|
|
847
|
+
return next();
|
|
848
|
+
}
|
|
849
|
+
const hasTransferEncoding = c.req.raw.headers.has("transfer-encoding");
|
|
850
|
+
const hasContentLength = c.req.raw.headers.has("content-length");
|
|
851
|
+
if (hasContentLength && !hasTransferEncoding) {
|
|
852
|
+
const contentLength = parseInt(c.req.raw.headers.get("content-length") || "0", 10);
|
|
853
|
+
return contentLength > maxSize ? onError(c) : next();
|
|
854
|
+
}
|
|
855
|
+
let size = 0;
|
|
856
|
+
const chunks = [];
|
|
857
|
+
const rawReader = c.req.raw.body.getReader();
|
|
858
|
+
for (; ; ) {
|
|
859
|
+
const { done, value } = await rawReader.read();
|
|
860
|
+
if (done) {
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
size += value.length;
|
|
864
|
+
if (size > maxSize) {
|
|
865
|
+
return onError(c);
|
|
866
|
+
}
|
|
867
|
+
chunks.push(value);
|
|
868
|
+
}
|
|
869
|
+
const requestInit = {
|
|
870
|
+
body: new ReadableStream({
|
|
871
|
+
start(controller) {
|
|
872
|
+
for (const chunk of chunks) {
|
|
873
|
+
controller.enqueue(chunk);
|
|
874
|
+
}
|
|
875
|
+
controller.close();
|
|
876
|
+
}
|
|
877
|
+
}),
|
|
878
|
+
duplex: "half"
|
|
879
|
+
};
|
|
880
|
+
c.req.raw = new Request(c.req.raw, requestInit);
|
|
881
|
+
return next();
|
|
882
|
+
};
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
// src/middleware/bodyLimit.ts
|
|
886
|
+
var DEFAULT_MAX_REQUEST_BYTES = 262144;
|
|
887
|
+
function chatBodyLimit(maxSize = DEFAULT_MAX_REQUEST_BYTES, toError = (message) => ({ error: message })) {
|
|
888
|
+
return bodyLimit({
|
|
889
|
+
maxSize,
|
|
890
|
+
onError: (c) => c.json(toError("Request body too large"), 413)
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// src/channels/telegram/webhook.ts
|
|
895
|
+
var SECRET_TOKEN_HEADER = "x-telegram-bot-api-secret-token";
|
|
896
|
+
function secureCompare(a, b) {
|
|
897
|
+
const bufA = Buffer.from(a);
|
|
898
|
+
const bufB = Buffer.from(b);
|
|
899
|
+
return bufA.length === bufB.length && timingSafeEqual(bufA, bufB);
|
|
900
|
+
}
|
|
901
|
+
var SECRET_TOKEN_PATTERN = /^[A-Za-z0-9_-]{1,256}$/;
|
|
902
|
+
function createTelegramWebhookRoute(config) {
|
|
903
|
+
const webhookSecret = config.webhookSecret;
|
|
904
|
+
if (!webhookSecret?.trim()) {
|
|
905
|
+
throw new Error("Telegram webhook requires webhookSecret (it fails closed without one)");
|
|
906
|
+
}
|
|
907
|
+
if (!SECRET_TOKEN_PATTERN.test(webhookSecret)) {
|
|
908
|
+
throw new Error(
|
|
909
|
+
"Telegram webhookSecret must match /^[A-Za-z0-9_-]{1,256}$/ (Telegram's own secret_token charset)"
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
const channelName = config.name ?? "telegram";
|
|
913
|
+
const allowedChats = config.allowedChats ?? [];
|
|
914
|
+
const path = config.path ?? "/webhooks/telegram";
|
|
915
|
+
return async (app, deps) => {
|
|
916
|
+
const log = config.logger ?? deps.logger ?? createConsoleLogger();
|
|
917
|
+
const api = config.api ?? createTelegramApi({
|
|
918
|
+
botToken: config.botToken,
|
|
919
|
+
fetch: config.fetch,
|
|
920
|
+
baseUrl: config.apiBaseUrl
|
|
921
|
+
});
|
|
922
|
+
const me = await api.getMe();
|
|
923
|
+
const label = `Telegram[${me.username ?? me.id}] (webhook)`;
|
|
924
|
+
deps.senders.register(channelName, createTelegramSender(api));
|
|
925
|
+
const pipeline = createInboundPipeline(
|
|
926
|
+
{ client: deps.client, store: deps.store, prompts: deps.prompts },
|
|
927
|
+
{
|
|
928
|
+
channel: channelName,
|
|
929
|
+
answerFn: config.answerFn ?? deps.config.answerFn,
|
|
930
|
+
bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
|
|
931
|
+
rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
|
|
932
|
+
rerankContext: config.rerankContext ?? deps.config.rerankContext,
|
|
933
|
+
transformReply: config.transformReply ?? deps.config.transformReply,
|
|
934
|
+
model: config.model,
|
|
935
|
+
channelHint: config.channelHint ?? "Channel: Telegram.",
|
|
936
|
+
personaResolver: config.personaResolver,
|
|
937
|
+
history: config.history,
|
|
938
|
+
allowedChats,
|
|
939
|
+
muteRegex: config.muteRegex,
|
|
940
|
+
unmuteRegex: config.unmuteRegex,
|
|
941
|
+
muteReply: config.muteReply,
|
|
942
|
+
unmuteReply: config.unmuteReply,
|
|
943
|
+
dmRateLimit: config.dmRateLimit,
|
|
944
|
+
groupRateLimit: config.groupRateLimit,
|
|
945
|
+
now: config.now,
|
|
946
|
+
logger: log
|
|
947
|
+
}
|
|
948
|
+
);
|
|
949
|
+
const handleUpdate = createTelegramUpdateHandler({
|
|
950
|
+
api,
|
|
951
|
+
me,
|
|
952
|
+
pipeline,
|
|
953
|
+
allowedChats,
|
|
954
|
+
logger: log,
|
|
955
|
+
label
|
|
956
|
+
});
|
|
957
|
+
app.post(
|
|
958
|
+
path,
|
|
959
|
+
chatBodyLimit(deps.config.server?.maxRequestBytes),
|
|
960
|
+
// Reject oversized bodies first
|
|
961
|
+
async (c) => {
|
|
962
|
+
const provided = c.req.header(SECRET_TOKEN_HEADER);
|
|
963
|
+
if (!provided || !secureCompare(provided, webhookSecret)) {
|
|
964
|
+
log.warn(`${label}: rejected webhook POST - missing or invalid secret token`);
|
|
965
|
+
return c.text("", 403);
|
|
966
|
+
}
|
|
967
|
+
let body;
|
|
968
|
+
try {
|
|
969
|
+
body = await c.req.json();
|
|
970
|
+
} catch {
|
|
971
|
+
return c.text("", 400);
|
|
972
|
+
}
|
|
973
|
+
if (typeof body !== "object" || body === null) {
|
|
974
|
+
return c.text("", 400);
|
|
975
|
+
}
|
|
976
|
+
const update = body;
|
|
977
|
+
try {
|
|
978
|
+
await handleUpdate(update);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
log.warn(`${label}: update handling failed:`, error);
|
|
981
|
+
}
|
|
982
|
+
return c.text("", 200);
|
|
983
|
+
}
|
|
984
|
+
);
|
|
985
|
+
log.info(`${label}: webhook mounted at ${path} as @${me.username ?? me.id}`);
|
|
986
|
+
};
|
|
987
|
+
}
|
|
691
988
|
export {
|
|
692
989
|
TELEGRAM_API_BASE,
|
|
693
990
|
TELEGRAM_TEXT_LIMIT,
|
|
694
991
|
TelegramApiError,
|
|
695
992
|
createTelegramApi,
|
|
696
993
|
createTelegramChannel,
|
|
994
|
+
createTelegramSender,
|
|
995
|
+
createTelegramUpdateHandler,
|
|
996
|
+
createTelegramWebhookRoute,
|
|
697
997
|
mentionsBot,
|
|
698
998
|
messageEntities,
|
|
699
999
|
messageText,
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot API transport: a `CustomRoutes` mount that receives updates
|
|
3
|
+
* over HTTPS instead of long-polling `getUpdates` (see `./channel` for that
|
|
4
|
+
* transport). Update handling is identical between the two — both build on
|
|
5
|
+
* `./handler`'s shared `createTelegramUpdateHandler`/`createTelegramSender` —
|
|
6
|
+
* so choosing one over the other is purely an infrastructure decision: a
|
|
7
|
+
* webhook needs a publicly reachable HTTPS endpoint and no open connection,
|
|
8
|
+
* long-polling needs neither but holds one request open at a time. See
|
|
9
|
+
* docs/telegram.md for the full comparison and `TelegramApi.setWebhook` for
|
|
10
|
+
* registering the URL with Telegram once this route is mounted.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* await createServer({
|
|
14
|
+
* ...,
|
|
15
|
+
* customRoutes: createTelegramWebhookRoute({
|
|
16
|
+
* botToken: process.env.TELEGRAM_BOT_TOKEN as string,
|
|
17
|
+
* webhookSecret: process.env.TELEGRAM_WEBHOOK_SECRET as string,
|
|
18
|
+
* }),
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import type { AnswerFn, TransformReply } from "../../core/answer";
|
|
23
|
+
import type { BucketsFor } from "../../core/buckets";
|
|
24
|
+
import { type Logger } from "../../core/logger";
|
|
25
|
+
import type { RerankContext, RewriteQuery } from "../../core/pipeline";
|
|
26
|
+
import type { HistoryCompactionOptions } from "../../history/compaction";
|
|
27
|
+
import type { HistoryStore } from "../../history/types";
|
|
28
|
+
import type { CustomRoutes } from "../../types";
|
|
29
|
+
import { type TelegramApi } from "./api";
|
|
30
|
+
export interface TelegramWebhookConfig {
|
|
31
|
+
/** From @BotFather. A credential — pass it from the environment, never commit it. */
|
|
32
|
+
botToken: string;
|
|
33
|
+
/**
|
|
34
|
+
* Shared secret Telegram is told about via `setWebhook({ secretToken })`
|
|
35
|
+
* and then echoes back on every POST as `X-Telegram-Bot-Api-Secret-Token`.
|
|
36
|
+
* Required: without one there is no way to tell a genuine Telegram POST
|
|
37
|
+
* from a forged one, so this fails closed by construction — a missing or
|
|
38
|
+
* blank value throws rather than falling back to accepting every request.
|
|
39
|
+
*/
|
|
40
|
+
webhookSecret: string;
|
|
41
|
+
/** Where the route is mounted. @default "/webhooks/telegram" */
|
|
42
|
+
path?: string;
|
|
43
|
+
/** Channel and sender-registry name. Override to run more than one bot in one process. @default "telegram" */
|
|
44
|
+
name?: string;
|
|
45
|
+
/** Group chats eligible for a reply. Empty (default) = every group. Has no effect on DMs, which always reply. */
|
|
46
|
+
allowedChats?: string[];
|
|
47
|
+
answerFn?: AnswerFn;
|
|
48
|
+
bucketsFor?: BucketsFor;
|
|
49
|
+
rewriteQuery?: RewriteQuery;
|
|
50
|
+
rerankContext?: RerankContext;
|
|
51
|
+
transformReply?: TransformReply;
|
|
52
|
+
model?: string;
|
|
53
|
+
/** Extra system-prompt section describing the delivery channel; passed through to `prepareChat`. @default "Channel: Telegram." */
|
|
54
|
+
channelHint?: string;
|
|
55
|
+
/** A throw/rejection is treated as "no persona" for that turn. `sender` is the namespaced `tg:<id>` key. */
|
|
56
|
+
personaResolver?: (ctx: {
|
|
57
|
+
sender: string;
|
|
58
|
+
text: string;
|
|
59
|
+
}) => string | undefined | Promise<string | undefined>;
|
|
60
|
+
/** Off by default — the channel stays single-turn until a store is configured. */
|
|
61
|
+
history?: {
|
|
62
|
+
store: HistoryStore;
|
|
63
|
+
/** Most recent turns to load per reply. @default 20 */
|
|
64
|
+
limit?: number;
|
|
65
|
+
historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Summarize-then-truncate compaction — see
|
|
68
|
+
* `InboundPipelineConfig.history.compaction` in `./channels`.
|
|
69
|
+
* @default off
|
|
70
|
+
*/
|
|
71
|
+
compaction?: HistoryCompactionOptions;
|
|
72
|
+
};
|
|
73
|
+
muteRegex?: RegExp;
|
|
74
|
+
unmuteRegex?: RegExp;
|
|
75
|
+
muteReply?: string;
|
|
76
|
+
unmuteReply?: string;
|
|
77
|
+
dmRateLimit?: {
|
|
78
|
+
max: number;
|
|
79
|
+
windowMs: number;
|
|
80
|
+
};
|
|
81
|
+
groupRateLimit?: {
|
|
82
|
+
max: number;
|
|
83
|
+
windowMs: number;
|
|
84
|
+
};
|
|
85
|
+
now?: () => number;
|
|
86
|
+
/** Overridable for tests and for hosts routing through a proxy; defaults to `globalThis.fetch`. */
|
|
87
|
+
fetch?: typeof fetch;
|
|
88
|
+
/** Self-hosted Bot API server origin. */
|
|
89
|
+
apiBaseUrl?: string;
|
|
90
|
+
/** Overridable for tests, which fake the Bot API instead of calling it; defaults to a `fetch` client over {@link TelegramWebhookConfig.botToken}. */
|
|
91
|
+
api?: TelegramApi;
|
|
92
|
+
logger?: Logger;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Builds a `CustomRoutes` mount that receives Telegram updates over HTTPS.
|
|
96
|
+
* Throws synchronously if `webhookSecret` is missing, blank, or outside the
|
|
97
|
+
* charset Telegram accepts for it — before the route ever exists, rather
|
|
98
|
+
* than mounting an endpoint that would silently accept everything or reject
|
|
99
|
+
* every request.
|
|
100
|
+
*/
|
|
101
|
+
export declare function createTelegramWebhookRoute(config: TelegramWebhookConfig): CustomRoutes;
|
|
102
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAqB,KAAK,WAAW,EAAuB,MAAM,OAAO,CAAC;AAMjF,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,YAAY,CAAC;QACpB,uDAAuD;QACvD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE;;;;WAIG;QACH,UAAU,CAAC,EAAE,wBAAwB,CAAC;KACvC,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,qBAAqB,GAAG,YAAY,CAkHtF"}
|
|
@@ -54,6 +54,7 @@ import { type Logger } from "../../core/logger";
|
|
|
54
54
|
import type { RerankContext, RewriteQuery } from "../../core/pipeline";
|
|
55
55
|
import type { PromptLoader } from "../../core/prompts";
|
|
56
56
|
import type { VectorStore } from "../../core/retrieval";
|
|
57
|
+
import type { HistoryCompactionOptions } from "../../history/compaction";
|
|
57
58
|
import type { HistoryStore } from "../../history/types";
|
|
58
59
|
import { type ChannelMessage, type SessionIdentityRegistry } from "../gates";
|
|
59
60
|
import type { WhatsAppMessageEvent } from "./channel";
|
|
@@ -199,6 +200,12 @@ export interface WhatsAppInboundConfig {
|
|
|
199
200
|
* @default every sender is enabled
|
|
200
201
|
*/
|
|
201
202
|
historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
|
|
203
|
+
/**
|
|
204
|
+
* Summarize-then-truncate compaction — see
|
|
205
|
+
* `InboundPipelineConfig.history.compaction` in `./channels`.
|
|
206
|
+
* @default off
|
|
207
|
+
*/
|
|
208
|
+
compaction?: HistoryCompactionOptions;
|
|
202
209
|
};
|
|
203
210
|
now?: () => number;
|
|
204
211
|
/** Logger for gate/dispatch diagnostics. Default: a console logger writing to stderr. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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"}
|