mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-05 09:42:54 +00:00
* refactor: share talk event metric extraction * refactor: reuse shared coercion helpers * refactor: reuse shared primitive guards * refactor: reuse shared record guard * refactor: reuse shared primitive helpers * refactor: reuse shared string guards * refactor: reuse shared non-empty string guard * refactor: share plugin primitive coercion helpers * refactor: reuse plugin coercion helpers * refactor: reuse plugin coercion helpers in more plugins * refactor: reuse channel coercion helpers * refactor: reuse monitor coercion helpers * refactor: reuse provider coercion helpers * refactor: reuse core coercion helpers * refactor: reuse runtime coercion helpers * refactor: reuse helper coercion in codex paths * refactor: reuse helper coercion in runtime paths * refactor: reuse codex app-server coercion helpers * refactor: reuse codex record helpers * refactor: reuse migration and qa record helpers * refactor: reuse feishu and core helper guards * refactor: reuse browser and policy coercion helpers * refactor: reuse memory wiki record helper * refactor: share boolean coercion helpers * refactor: reuse finite number coercion * refactor: reuse trimmed string list helpers * refactor: reuse string list normalization * refactor: reuse remaining string list helpers * refactor: reuse string entry normalizer * refactor: share sorted string helpers * refactor: share string list normalization * test: preserve command registry browser imports * refactor: reuse trimmed list helpers * refactor: reuse string dedupe helpers * refactor: reuse local dedupe helpers * refactor: reuse more string dedupe helpers * refactor: reuse command string dedupe helpers * refactor: dedupe memory path lists with helper * refactor: expose string dedupe helpers to plugins * refactor: reuse core string dedupe helpers * refactor: reuse shared unique value helpers * refactor: reuse unique helpers in agent utilities * refactor: reuse unique helpers in config plumbing * refactor: reuse unique helpers in extensions * refactor: reuse unique helpers in core utilities * refactor: reuse unique helpers in qa plugins * refactor: reuse unique helpers in memory plugins * refactor: reuse unique helpers in channel plugins * refactor: reuse unique helpers in core tails * refactor: reuse unique helper in comfy workflow * refactor: reuse unique helpers in test utilities * refactor: expose unique value helper to plugins * refactor: reuse unique helpers for numeric lists * refactor: replace index dedupe filters * refactor: reuse string entry normalization * refactor: reuse string normalization in plugin helpers * refactor: reuse string normalization in extension helpers * refactor: reuse string normalization in channel parsers * refactor: reuse string normalization in memory search * refactor: reuse string normalization in provider parsers * refactor: reuse string normalization in qa helpers * refactor: reuse string normalization in infra parsers * refactor: reuse string normalization in messaging parsers * refactor: reuse string normalization in core parsers * refactor: reuse string normalization in extension parsers * refactor: reuse string normalization in remaining parsers * refactor: reuse string normalization in final parser spots * refactor: reuse string normalization in qa media helpers * refactor: reuse normalization in provider and media lists * refactor: reuse normalization for remaining set filters * refactor: reuse normalization in policy allowlists * refactor: reuse normalization in session and owner lists * refactor: centralize primitive string lists * refactor: reuse lowercase entry helpers * refactor: reuse sorted string helpers * refactor: reuse unique trimmed helpers * refactor: reuse string normalization helpers * refactor: reuse catalog string helpers * refactor: reuse remaining string helpers * refactor: simplify remaining list normalization * refactor: reuse codex auth order normalization * chore: refresh plugin sdk api baseline * fix: make shared string sorting deterministic * chore: refresh plugin sdk api baseline * fix: align host env security ordering
108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import path from "node:path";
|
|
import type { Message } from "grammy/types";
|
|
import { createClaimableDedupe, type ClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
import { normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
|
|
const TELEGRAM_MESSAGE_DISPATCH_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
const TELEGRAM_MESSAGE_DISPATCH_MEMORY_MAX = 5000;
|
|
const TELEGRAM_MESSAGE_DISPATCH_FILE_MAX = 50_000;
|
|
|
|
export type TelegramMessageDispatchReplayGuard = ClaimableDedupe;
|
|
|
|
export type TelegramMessageDispatchClaim =
|
|
| { kind: "claimed"; key: string }
|
|
| { kind: "duplicate" }
|
|
| { kind: "invalid" };
|
|
|
|
function sanitizeFileSegment(value: string): string {
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return "default";
|
|
}
|
|
return trimmed.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
}
|
|
|
|
export function buildTelegramMessageDispatchReplayKey(msg: Message): string | null {
|
|
const chatId = msg.chat?.id;
|
|
const messageId = msg.message_id;
|
|
if (chatId == null || typeof messageId !== "number" || messageId <= 0) {
|
|
return null;
|
|
}
|
|
return JSON.stringify(["message", String(chatId), messageId]);
|
|
}
|
|
|
|
export function createTelegramMessageDispatchReplayGuard(params: {
|
|
storePath: string;
|
|
onDiskError?: (error: unknown) => void;
|
|
}): TelegramMessageDispatchReplayGuard {
|
|
return createClaimableDedupe({
|
|
ttlMs: TELEGRAM_MESSAGE_DISPATCH_TTL_MS,
|
|
memoryMaxSize: TELEGRAM_MESSAGE_DISPATCH_MEMORY_MAX,
|
|
fileMaxEntries: TELEGRAM_MESSAGE_DISPATCH_FILE_MAX,
|
|
resolveFilePath: (namespace) =>
|
|
path.join(
|
|
path.dirname(params.storePath),
|
|
`${path.basename(params.storePath)}.telegram-message-dispatch-${sanitizeFileSegment(
|
|
namespace,
|
|
)}.json`,
|
|
),
|
|
onDiskError: params.onDiskError,
|
|
});
|
|
}
|
|
|
|
export async function claimTelegramMessageDispatchReplay(params: {
|
|
guard: TelegramMessageDispatchReplayGuard;
|
|
accountId: string;
|
|
msg: Message;
|
|
}): Promise<TelegramMessageDispatchClaim> {
|
|
const key = buildTelegramMessageDispatchReplayKey(params.msg);
|
|
if (!key) {
|
|
return { kind: "invalid" };
|
|
}
|
|
|
|
let releaseRetries = 0;
|
|
while (true) {
|
|
const claim = await params.guard.claim(key, { namespace: params.accountId });
|
|
if (claim.kind === "claimed") {
|
|
return { kind: "claimed", key };
|
|
}
|
|
if (claim.kind === "duplicate") {
|
|
return { kind: "duplicate" };
|
|
}
|
|
try {
|
|
await claim.pending;
|
|
return { kind: "duplicate" };
|
|
} catch {
|
|
releaseRetries += 1;
|
|
if (releaseRetries > 1) {
|
|
return { kind: "duplicate" };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function normalizeReplayKeys(keys?: readonly string[]): string[] {
|
|
return uniqueStrings(normalizeStringEntries(keys ?? []));
|
|
}
|
|
|
|
export async function commitTelegramMessageDispatchReplay(params: {
|
|
guard: TelegramMessageDispatchReplayGuard;
|
|
accountId: string;
|
|
keys?: readonly string[];
|
|
}): Promise<void> {
|
|
const keys = normalizeReplayKeys(params.keys);
|
|
await Promise.all(keys.map((key) => params.guard.commit(key, { namespace: params.accountId })));
|
|
}
|
|
|
|
export function releaseTelegramMessageDispatchReplay(params: {
|
|
guard: TelegramMessageDispatchReplayGuard;
|
|
accountId: string;
|
|
keys?: readonly string[];
|
|
error?: unknown;
|
|
}): void {
|
|
const keys = normalizeReplayKeys(params.keys);
|
|
for (const key of keys) {
|
|
params.guard.release(key, { namespace: params.accountId, error: params.error });
|
|
}
|
|
}
|