fix(telegram): ignore unsafe cached message ids

This commit is contained in:
Peter Steinberger
2026-05-29 05:44:15 -04:00
parent e890d7ea4f
commit 656c238295
2 changed files with 39 additions and 10 deletions

View File

@@ -405,6 +405,30 @@ describe("telegram message cache", () => {
expect(recent.map((entry) => entry.messageId)).toEqual(["9122", "9123", "9124"]);
});
it("does not use unsafe message ids as recent-before cutoffs", async () => {
const cache = createTelegramMessageCache();
await cache.record({
accountId: "default",
chatId: 7,
msg: {
chat: { id: 7, type: "private", first_name: "Nora" },
message_id: 9124,
date: 1736380700,
text: "State message",
from: { id: 1, is_bot: false, first_name: "Nora" },
} as Message,
});
const recent = await cache.recentBefore({
accountId: "default",
chatId: 7,
messageId: "9007199254740992",
limit: 10,
});
expect(recent).toEqual([]);
});
it("reads legacy sidecar records as a persistent-store fallback", async () => {
const storePath = `/tmp/openclaw-telegram-message-cache-state-migrate-${process.pid}-${Date.now()}.json`;
const persistedPath = resolveTelegramMessageCachePath(storePath);

View File

@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
import fs from "node:fs";
import type { Message } from "grammy/types";
import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
import type { MsgContext } from "openclaw/plugin-sdk/reply-runtime";
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
import { appendRegularFileSync, replaceFileAtomicSync } from "openclaw/plugin-sdk/security-runtime";
@@ -240,6 +241,10 @@ function readOptionalString(record: Record<string, unknown>, key: string): strin
return isString(value) ? value : undefined;
}
function parseSafeMessageId(value: string | undefined): number | undefined {
return value === undefined ? undefined : parseStrictPositiveInteger(value);
}
function isTelegramSourceMessage(value: unknown): value is Message {
return (
isRecord(value) &&
@@ -788,14 +793,14 @@ export function createTelegramMessageCache(params?: {
if (!messageId || limit <= 0) {
return [];
}
const targetId = Number(messageId);
if (!Number.isFinite(targetId)) {
const targetId = parseSafeMessageId(messageId);
if (targetId === undefined) {
return [];
}
return (await listChatMessages({ accountId, chatId, threadId }))
.filter((entry) => {
const entryId = Number(entry.messageId);
return Number.isFinite(entryId) && entryId < targetId;
const entryId = parseSafeMessageId(entry.messageId);
return entryId !== undefined && entryId < targetId;
})
.slice(-limit);
},
@@ -820,9 +825,9 @@ function compareCachedMessageNodes(
left: TelegramCachedMessageNode,
right: TelegramCachedMessageNode,
) {
const leftId = Number(left.messageId);
const rightId = Number(right.messageId);
if (Number.isFinite(leftId) && Number.isFinite(rightId)) {
const leftId = parseSafeMessageId(left.messageId);
const rightId = parseSafeMessageId(right.messageId);
if (leftId !== undefined && rightId !== undefined) {
return leftId - rightId;
}
return (left.messageId ?? "").localeCompare(right.messageId ?? "");
@@ -845,9 +850,9 @@ function isAfterSessionBoundary(
if (!boundary) {
return true;
}
const nodeId = Number(node.messageId);
const boundaryId = Number(boundary.messageId);
if (Number.isFinite(nodeId) && Number.isFinite(boundaryId)) {
const nodeId = parseSafeMessageId(node.messageId);
const boundaryId = parseSafeMessageId(boundary.messageId);
if (nodeId !== undefined && boundaryId !== undefined) {
return nodeId > boundaryId;
}
if (