Telegram: persist polling update offsets

Closes #739
This commit is contained in:
Shadow
2026-01-12 21:52:13 -06:00
parent 980f274fc9
commit c08441c42c
5 changed files with 188 additions and 1 deletions

View File

@@ -109,8 +109,11 @@ type TelegramUpdateKeyContext = {
callbackQuery?: { id?: string; message?: TelegramMessage };
};
const resolveTelegramUpdateId = (ctx: TelegramUpdateKeyContext) =>
ctx.update?.update_id ?? ctx.update_id;
const buildTelegramUpdateKey = (ctx: TelegramUpdateKeyContext) => {
const updateId = ctx.update?.update_id ?? ctx.update_id;
const updateId = resolveTelegramUpdateId(ctx);
if (typeof updateId === "number") return `update:${updateId}`;
const callbackId = ctx.callbackQuery?.id;
if (callbackId) return `callback:${callbackId}`;
@@ -172,6 +175,10 @@ export type TelegramBotOptions = {
replyToMode?: ReplyToMode;
proxyFetch?: typeof fetch;
config?: ClawdbotConfig;
updateOffset?: {
lastUpdateId?: number | null;
onUpdateId?: (updateId: number) => void | Promise<void>;
};
};
export function getTelegramSequentialKey(ctx: {
@@ -220,7 +227,24 @@ export function createTelegramBot(opts: TelegramBotOptions) {
bot.use(sequentialize(getTelegramSequentialKey));
const recentUpdates = createTelegramUpdateDedupe();
let lastUpdateId =
typeof opts.updateOffset?.lastUpdateId === "number"
? opts.updateOffset.lastUpdateId
: null;
const recordUpdateId = (ctx: TelegramUpdateKeyContext) => {
const updateId = resolveTelegramUpdateId(ctx);
if (typeof updateId !== "number") return;
if (lastUpdateId !== null && updateId <= lastUpdateId) return;
lastUpdateId = updateId;
void opts.updateOffset?.onUpdateId?.(updateId);
};
const shouldSkipUpdate = (ctx: TelegramUpdateKeyContext) => {
const updateId = resolveTelegramUpdateId(ctx);
if (typeof updateId === "number" && lastUpdateId !== null) {
if (updateId <= lastUpdateId) return true;
}
const key = buildTelegramUpdateKey(ctx);
const skipped = recentUpdates.check(key);
if (skipped && key && shouldLogVerbose()) {
@@ -229,6 +253,11 @@ export function createTelegramBot(opts: TelegramBotOptions) {
return skipped;
};
bot.use(async (ctx, next) => {
await next();
recordUpdateId(ctx);
});
const mediaGroupBuffer = new Map<string, MediaGroupEntry>();
let mediaGroupProcessing: Promise<void> = Promise.resolve();