mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 09:12:42 +00:00
fix: persist resolved telegram delivery targets at runtime
This commit is contained in:
193
src/telegram/target-writeback.ts
Normal file
193
src/telegram/target-writeback.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { readConfigFileSnapshotForWrite, writeConfigFile } from "../config/config.js";
|
||||
import { loadCronStore, resolveCronStorePath, saveCronStore } from "../cron/store.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import {
|
||||
normalizeTelegramChatId,
|
||||
normalizeTelegramLookupTarget,
|
||||
parseTelegramTarget,
|
||||
} from "./targets.js";
|
||||
|
||||
const writebackLogger = createSubsystemLogger("telegram/target-writeback");
|
||||
|
||||
function asObjectRecord(value: unknown): Record<string, unknown> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return null;
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function normalizeTelegramTargetForMatch(raw: string): string | undefined {
|
||||
const parsed = parseTelegramTarget(raw);
|
||||
const normalized = normalizeTelegramLookupTarget(parsed.chatId);
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
const threadKey = parsed.messageThreadId == null ? "" : String(parsed.messageThreadId);
|
||||
return `${normalized}|${threadKey}`;
|
||||
}
|
||||
|
||||
function buildResolvedTelegramTarget(params: {
|
||||
raw: string;
|
||||
parsed: ReturnType<typeof parseTelegramTarget>;
|
||||
resolvedChatId: string;
|
||||
}): string {
|
||||
const { raw, parsed, resolvedChatId } = params;
|
||||
if (parsed.messageThreadId == null) {
|
||||
return resolvedChatId;
|
||||
}
|
||||
return raw.includes(":topic:")
|
||||
? `${resolvedChatId}:topic:${parsed.messageThreadId}`
|
||||
: `${resolvedChatId}:${parsed.messageThreadId}`;
|
||||
}
|
||||
|
||||
function resolveLegacyRewrite(params: {
|
||||
raw: string;
|
||||
resolvedChatId: string;
|
||||
}): { matchKey: string; resolvedTarget: string } | null {
|
||||
const parsed = parseTelegramTarget(params.raw);
|
||||
if (normalizeTelegramChatId(parsed.chatId)) {
|
||||
return null;
|
||||
}
|
||||
const normalized = normalizeTelegramLookupTarget(parsed.chatId);
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
const threadKey = parsed.messageThreadId == null ? "" : String(parsed.messageThreadId);
|
||||
return {
|
||||
matchKey: `${normalized}|${threadKey}`,
|
||||
resolvedTarget: buildResolvedTelegramTarget({
|
||||
raw: params.raw,
|
||||
parsed,
|
||||
resolvedChatId: params.resolvedChatId,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function rewriteTargetIfMatch(params: {
|
||||
rawValue: unknown;
|
||||
matchKey: string;
|
||||
resolvedTarget: string;
|
||||
}): string | null {
|
||||
if (typeof params.rawValue !== "string" && typeof params.rawValue !== "number") {
|
||||
return null;
|
||||
}
|
||||
const value = String(params.rawValue).trim();
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
if (normalizeTelegramTargetForMatch(value) !== params.matchKey) {
|
||||
return null;
|
||||
}
|
||||
return params.resolvedTarget;
|
||||
}
|
||||
|
||||
function replaceTelegramDefaultToTargets(params: {
|
||||
cfg: OpenClawConfig;
|
||||
matchKey: string;
|
||||
resolvedTarget: string;
|
||||
}): boolean {
|
||||
let changed = false;
|
||||
const telegram = asObjectRecord(params.cfg.channels?.telegram);
|
||||
if (!telegram) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
const maybeReplace = (holder: Record<string, unknown>, key: string) => {
|
||||
const nextTarget = rewriteTargetIfMatch({
|
||||
rawValue: holder[key],
|
||||
matchKey: params.matchKey,
|
||||
resolvedTarget: params.resolvedTarget,
|
||||
});
|
||||
if (!nextTarget) {
|
||||
return;
|
||||
}
|
||||
holder[key] = nextTarget;
|
||||
changed = true;
|
||||
};
|
||||
|
||||
maybeReplace(telegram, "defaultTo");
|
||||
const accounts = asObjectRecord(telegram.accounts);
|
||||
if (!accounts) {
|
||||
return changed;
|
||||
}
|
||||
for (const accountId of Object.keys(accounts)) {
|
||||
const account = asObjectRecord(accounts[accountId]);
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
maybeReplace(account, "defaultTo");
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
export async function maybePersistResolvedTelegramTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
rawTarget: string;
|
||||
resolvedChatId: string;
|
||||
verbose?: boolean;
|
||||
}): Promise<void> {
|
||||
const raw = params.rawTarget.trim();
|
||||
if (!raw) {
|
||||
return;
|
||||
}
|
||||
const rewrite = resolveLegacyRewrite({
|
||||
raw,
|
||||
resolvedChatId: params.resolvedChatId,
|
||||
});
|
||||
if (!rewrite) {
|
||||
return;
|
||||
}
|
||||
const { matchKey, resolvedTarget } = rewrite;
|
||||
|
||||
try {
|
||||
const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
|
||||
const nextConfig = structuredClone(snapshot.config ?? {});
|
||||
const configChanged = replaceTelegramDefaultToTargets({
|
||||
cfg: nextConfig,
|
||||
matchKey,
|
||||
resolvedTarget,
|
||||
});
|
||||
if (configChanged) {
|
||||
await writeConfigFile(nextConfig, writeOptions);
|
||||
if (params.verbose) {
|
||||
writebackLogger.warn(`resolved Telegram defaultTo target ${raw} -> ${resolvedTarget}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (params.verbose) {
|
||||
writebackLogger.warn(`failed to persist Telegram defaultTo target ${raw}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const storePath = resolveCronStorePath(params.cfg.cron?.store);
|
||||
const store = await loadCronStore(storePath);
|
||||
let cronChanged = false;
|
||||
for (const job of store.jobs) {
|
||||
if (job.delivery?.channel !== "telegram") {
|
||||
continue;
|
||||
}
|
||||
const nextTarget = rewriteTargetIfMatch({
|
||||
rawValue: job.delivery.to,
|
||||
matchKey,
|
||||
resolvedTarget,
|
||||
});
|
||||
if (!nextTarget) {
|
||||
continue;
|
||||
}
|
||||
job.delivery.to = nextTarget;
|
||||
cronChanged = true;
|
||||
}
|
||||
if (cronChanged) {
|
||||
await saveCronStore(storePath, store);
|
||||
if (params.verbose) {
|
||||
writebackLogger.warn(`resolved Telegram cron delivery target ${raw} -> ${resolvedTarget}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (params.verbose) {
|
||||
writebackLogger.warn(`failed to persist Telegram cron target ${raw}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user