mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix: keep Telegram command sync process-local (#66730) (thanks @nightq)
* fix: use process-scoped cache for Telegram command sync to fix missing menu after restart Fixes openclaw#66714, openclaw#66682 Root cause: The command hash cache was persisted to disk across gateway restarts. When the hash matched (commands unchanged), setMyCommands was skipped entirely. But Telegram bot commands can be cleared by external factors, so the cached state becomes stale after restart. Fix: Replace file-based hash cache with a process-scoped Map. This preserves the rapid-restart rate-limit protection within a single process, but ensures commands are always re-registered after a gateway restart. * fix(telegram): drop stale async command cache calls * fix: keep Telegram command sync process-local (#66730) (thanks @nightq) --------- Co-authored-by: nightq <zengwei@nightq.cn> Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
@@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram/documents: sanitize binary reply context and ZIP-like archive extraction so `.epub` and `.mobi` uploads can no longer leak raw binary into prompt context through reply metadata or archive-to-`text/plain` coercion. (#66877) Thanks @martinfrancois.
|
||||
- Telegram/native commands: restore plugin-registry-backed auto defaults for native commands and native skills so Telegram slash commands keep registering when `commands.native` and `commands.nativeSkills` stay on `auto`. (#66843) Thanks @kashevk0.
|
||||
- fix(bluebubbles): replay missed webhook messages after gateway restart via a persistent per-account cursor and `/api/v1/message/query?after=<ts>` pass, so messages delivered while the gateway was down no longer disappear. Uses the existing `processMessage` path and is deduped by #66816's inbound GUID cache. (#66857, #66721) Thanks @omarshahine.
|
||||
- Telegram/native commands: keep Telegram command-sync cache process-local so gateway restarts re-register the menu instead of trusting stale on-disk sync state after Telegram cleared commands out-of-band. (#66730) Thanks @nightq.
|
||||
|
||||
## 2026.4.14
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { Bot } from "grammy";
|
||||
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
import { normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { normalizeTelegramCommandName, TELEGRAM_COMMAND_NAME_PATTERN } from "./command-config.js";
|
||||
@@ -215,45 +211,25 @@ export function hashCommandList(commands: TelegramMenuCommand[]): string {
|
||||
return createHash("sha256").update(JSON.stringify(sorted)).digest("hex").slice(0, 16);
|
||||
}
|
||||
|
||||
function hashBotIdentity(botIdentity?: string): string {
|
||||
const normalized = botIdentity?.trim();
|
||||
if (!normalized) {
|
||||
return "no-bot";
|
||||
}
|
||||
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
||||
// Keep the sync cache process-local so restarts always re-register commands.
|
||||
const syncedCommandHashes = new Map<string, string>();
|
||||
|
||||
function getCommandHashKey(accountId?: string, botIdentity?: string): string {
|
||||
return `${accountId ?? "default"}:${botIdentity ?? ""}`;
|
||||
}
|
||||
|
||||
function resolveCommandHashPath(accountId?: string, botIdentity?: string): string {
|
||||
const stateDir = resolveStateDir(process.env, os.homedir);
|
||||
const normalizedAccount = accountId?.trim().replace(/[^a-z0-9._-]+/gi, "_") || "default";
|
||||
const botHash = hashBotIdentity(botIdentity);
|
||||
return path.join(stateDir, "telegram", `command-hash-${normalizedAccount}-${botHash}.txt`);
|
||||
function readCachedCommandHash(accountId?: string, botIdentity?: string): string | null {
|
||||
const key = getCommandHashKey(accountId, botIdentity);
|
||||
return syncedCommandHashes.get(key) ?? null;
|
||||
}
|
||||
|
||||
async function readCachedCommandHash(
|
||||
accountId?: string,
|
||||
botIdentity?: string,
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
return (await fs.readFile(resolveCommandHashPath(accountId, botIdentity), "utf-8")).trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function writeCachedCommandHash(
|
||||
function writeCachedCommandHash(
|
||||
accountId: string | undefined,
|
||||
botIdentity: string | undefined,
|
||||
hash: string,
|
||||
): Promise<void> {
|
||||
const filePath = resolveCommandHashPath(accountId, botIdentity);
|
||||
try {
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||
await fs.writeFile(filePath, hash, "utf-8");
|
||||
} catch {
|
||||
// Best-effort: failing to cache the hash just means the next restart
|
||||
// will sync commands again, which is the pre-fix behaviour.
|
||||
}
|
||||
): void {
|
||||
const key = getCommandHashKey(accountId, botIdentity);
|
||||
syncedCommandHashes.set(key, hash);
|
||||
}
|
||||
|
||||
export function syncTelegramMenuCommands(params: {
|
||||
@@ -270,7 +246,7 @@ export function syncTelegramMenuCommands(params: {
|
||||
// is restarted several times in quick succession.
|
||||
// See: openclaw/openclaw#32017
|
||||
const currentHash = hashCommandList(commandsToRegister);
|
||||
const cachedHash = await readCachedCommandHash(accountId, botIdentity);
|
||||
const cachedHash = readCachedCommandHash(accountId, botIdentity);
|
||||
if (cachedHash === currentHash) {
|
||||
logVerbose("telegram: command menu unchanged; skipping sync");
|
||||
return;
|
||||
@@ -293,7 +269,7 @@ export function syncTelegramMenuCommands(params: {
|
||||
runtime.log?.("telegram: deleteMyCommands failed; skipping empty-menu hash cache write");
|
||||
return;
|
||||
}
|
||||
await writeCachedCommandHash(accountId, botIdentity, currentHash);
|
||||
writeCachedCommandHash(accountId, botIdentity, currentHash);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -315,7 +291,7 @@ export function syncTelegramMenuCommands(params: {
|
||||
}),
|
||||
);
|
||||
}
|
||||
await writeCachedCommandHash(accountId, botIdentity, currentHash);
|
||||
writeCachedCommandHash(accountId, botIdentity, currentHash);
|
||||
return;
|
||||
} catch (err) {
|
||||
if (!isBotCommandsTooMuchError(err)) {
|
||||
|
||||
Reference in New Issue
Block a user