/** * Count QQBot accounts that could actually deliver a native approval * message — i.e. accounts that are enabled and have resolvable secrets. * Disabled or unconfigured accounts never spawn a handler, so they * must not contribute to the single-account shortcut in the fallback * ownership check below.
*/ function countQQBotFallbackEligibleAccounts(cfg: OpenClawConfig): number { return listQQBotAccountIds(cfg).filter((accountId) => { const account = resolveQQBotAccount(cfg, accountId); return account.enabled && account.secretSource !== "none";
}).length;
}
/** * Fallback account-ownership check — applied when `execApprovals` is NOT * configured for any QQBot account. In this mode every enabled account * handler would otherwise race to deliver the same approval to its own * openid namespace, so we must enforce per-account isolation. * * Rules: * - If the request carries a bound account (via `turnSourceAccountId` * or session binding), only the handler whose `accountId` matches it * delivers the approval. This is strict: a handler with an unknown * `accountId` (null/undefined) must not claim a bound request. * - If no account is bound, only deliver when there is a single * *eligible* QQBot account (enabled + secret resolved). Disabled or * unconfigured accounts never deliver anyway, so they shouldn't * block the remaining single account from handling the approval. * Multiple eligible accounts cannot safely race because openids are * account-scoped — cross-account delivery hits the QQ Bot API with * a mismatched token and fails.
*/ function matchesQQBotFallbackRequestAccount(params: {
cfg: OpenClawConfig;
accountId?: string | null;
request: ExecApprovalRequest | PluginApprovalRequest;
}): boolean { const boundAccountId = resolveApprovalRequestChannelAccountId({
cfg: params.cfg,
request: params.request,
channel: "qqbot",
});
if (boundAccountId) { if (!params.accountId) { returnfalse;
} return normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId);
}
/** * Minimal structural shape required to evaluate per-account ownership. * * The SDK types (`ExecApprovalRequest` / `PluginApprovalRequest`) and the * channel-local approval request types (see `engine/approval/index.ts`) * share the same logical fields but differ on bookkeeping metadata * (e.g. `createdAtMs`), so we accept any object exposing the relevant * routing fields. Consumers can pass either flavor safely.
*/
type QQBotApprovalAccountOwnershipRequest = {
request: {
sessionKey?: string | null;
turnSourceChannel?: string | null;
turnSourceTo?: string | null;
turnSourceAccountId?: string | null;
};
};
/** * Unified per-account ownership check used by both the profile and * fallback approval paths. Dispatches to the profile rules when the * current account has `execApprovals` configured, otherwise uses the * fallback rules. * * This is the single source of truth for "does this QQBot handler own * this approval request?" and is consumed by both the capability * gate (shouldHandle) and the lazy native runtime adapter.
*/
export function matchesQQBotApprovalAccount(params: {
cfg: OpenClawConfig;
accountId?: string | null;
request: QQBotApprovalAccountOwnershipRequest;
}): boolean { const normalized = {
cfg: params.cfg,
accountId: params.accountId,
request: params.request as unknown as ExecApprovalRequest | PluginApprovalRequest,
}; if (resolveQQBotExecApprovalConfig(normalized) !== undefined) { return matchesQQBotRequestAccount(normalized);
} return matchesQQBotFallbackRequestAccount(normalized);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.