diff --git a/extensions/matrix/src/matrix/account-config.ts b/extensions/matrix/src/matrix/account-config.ts index ad6bf39b9f0..17af48bdafe 100644 --- a/extensions/matrix/src/matrix/account-config.ts +++ b/extensions/matrix/src/matrix/account-config.ts @@ -2,11 +2,14 @@ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id"; import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"; import { listConfiguredAccountIds, + resolveMergedAccountConfig, resolveNormalizedAccountEntry, } from "openclaw/plugin-sdk/account-resolution"; import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input"; import type { CoreConfig, MatrixAccountConfig, MatrixConfig } from "../types.js"; +type MatrixRoomEntries = Record[string]>; + export function resolveMatrixBaseConfig(cfg: CoreConfig): MatrixConfig { return cfg.channels?.matrix ?? {}; } @@ -19,6 +22,51 @@ function resolveMatrixAccountsMap(cfg: CoreConfig): Readonly { + const scopedAccount = + typeof value?.account === "string" ? normalizeAccountId(value.account) : undefined; + return scopedAccount === undefined || scopedAccount === params.accountId; + }), + ) as MatrixRoomEntries; + return Object.keys(selected).length > 0 ? selected : undefined; +} + +function mergeMatrixRoomEntries( + inherited: MatrixRoomEntries | undefined, + accountEntries: MatrixRoomEntries | undefined, + hasAccountOverride: boolean, +): MatrixRoomEntries | undefined { + if (!inherited && !accountEntries) { + return undefined; + } + if (hasAccountOverride && Object.keys(accountEntries ?? {}).length === 0) { + return undefined; + } + const merged: MatrixRoomEntries = { + ...(inherited ?? {}), + }; + for (const [key, value] of Object.entries(accountEntries ?? {})) { + const inheritedValue = merged[key]; + merged[key] = + inheritedValue && value + ? { + ...inheritedValue, + ...value, + } + : (value ?? inheritedValue); + } + return Object.keys(merged).length > 0 ? merged : undefined; +} + export function listNormalizedMatrixAccountIds(cfg: CoreConfig): string[] { return listConfiguredAccountIds({ accounts: resolveMatrixAccountsMap(cfg), @@ -58,3 +106,45 @@ export function hasExplicitMatrixAccountConfig(cfg: CoreConfig, accountId: strin typeof matrix.avatarUrl === "string" ); } + +export function resolveMatrixAccountConfig(params: { + cfg: CoreConfig; + accountId?: string | null; + env?: NodeJS.ProcessEnv; +}): MatrixConfig { + const accountId = normalizeAccountId(params.accountId); + const base = resolveMatrixBaseConfig(params.cfg); + const merged = resolveMergedAccountConfig({ + channelConfig: base, + accounts: params.cfg.channels?.matrix?.accounts as + | Record> + | undefined, + accountId, + normalizeAccountId, + nestedObjectKeys: ["dm", "actions"], + }); + const accountConfig = findMatrixAccountConfig(params.cfg, accountId); + const groups = mergeMatrixRoomEntries( + selectInheritedMatrixRoomEntries({ + entries: base.groups, + accountId, + }), + accountConfig?.groups, + Boolean(accountConfig && Object.hasOwn(accountConfig, "groups")), + ); + const rooms = mergeMatrixRoomEntries( + selectInheritedMatrixRoomEntries({ + entries: base.rooms, + accountId, + }), + accountConfig?.rooms, + Boolean(accountConfig && Object.hasOwn(accountConfig, "rooms")), + ); + // Room maps need custom scoping, so keep the generic merge for all other fields. + const { groups: _ignoredGroups, rooms: _ignoredRooms, ...rest } = merged; + return { + ...rest, + ...(groups ? { groups } : {}), + ...(rooms ? { rooms } : {}), + }; +} diff --git a/extensions/matrix/src/matrix/accounts.ts b/extensions/matrix/src/matrix/accounts.ts index a82448b4e38..5683bfa73f4 100644 --- a/extensions/matrix/src/matrix/accounts.ts +++ b/extensions/matrix/src/matrix/accounts.ts @@ -1,62 +1,18 @@ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id"; -import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution"; import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input"; import { resolveConfiguredMatrixAccountIds, resolveMatrixDefaultOrOnlyAccountId, } from "../account-selection.js"; import type { CoreConfig, MatrixConfig } from "../types.js"; -import { findMatrixAccountConfig, resolveMatrixBaseConfig } from "./account-config.js"; +import { + findMatrixAccountConfig, + resolveMatrixAccountConfig, + resolveMatrixBaseConfig, +} from "./account-config.js"; import { resolveMatrixConfigForAccount } from "./client.js"; import { credentialsMatchConfig, loadMatrixCredentials } from "./credentials-read.js"; -type MatrixRoomEntries = Record[string]>; - -function selectInheritedMatrixRoomEntries(params: { - entries: MatrixRoomEntries | undefined; - accountId: string; -}): MatrixRoomEntries | undefined { - const entries = params.entries; - if (!entries) { - return undefined; - } - const selected = Object.fromEntries( - Object.entries(entries).filter(([, value]) => { - const scopedAccount = - typeof value?.account === "string" ? normalizeAccountId(value.account) : undefined; - return scopedAccount === undefined || scopedAccount === params.accountId; - }), - ) as MatrixRoomEntries; - return Object.keys(selected).length > 0 ? selected : undefined; -} - -function mergeMatrixRoomEntries( - inherited: MatrixRoomEntries | undefined, - accountEntries: MatrixRoomEntries | undefined, - hasAccountOverride: boolean, -): MatrixRoomEntries | undefined { - if (!inherited && !accountEntries) { - return undefined; - } - if (hasAccountOverride && Object.keys(accountEntries ?? {}).length === 0) { - return undefined; - } - const merged: MatrixRoomEntries = { - ...(inherited ?? {}), - }; - for (const [key, value] of Object.entries(accountEntries ?? {})) { - const inheritedValue = merged[key]; - merged[key] = - inheritedValue && value - ? { - ...inheritedValue, - ...value, - } - : (value ?? inheritedValue); - } - return Object.keys(merged).length > 0 ? merged : undefined; -} - export type ResolvedMatrixAccount = { accountId: string; enabled: boolean; @@ -177,45 +133,4 @@ export function resolveMatrixAccount(params: { }; } -export function resolveMatrixAccountConfig(params: { - cfg: CoreConfig; - accountId?: string | null; - env?: NodeJS.ProcessEnv; -}): MatrixConfig { - const env = params.env ?? process.env; - const accountId = normalizeAccountId(params.accountId); - const base = resolveMatrixBaseConfig(params.cfg); - const merged = resolveMergedAccountConfig({ - channelConfig: base, - accounts: params.cfg.channels?.matrix?.accounts as - | Record> - | undefined, - accountId, - normalizeAccountId, - nestedObjectKeys: ["dm", "actions"], - }); - const accountConfig = findMatrixAccountConfig(params.cfg, accountId); - const groups = mergeMatrixRoomEntries( - selectInheritedMatrixRoomEntries({ - entries: base.groups, - accountId, - }), - accountConfig?.groups, - Boolean(accountConfig && Object.hasOwn(accountConfig, "groups")), - ); - const rooms = mergeMatrixRoomEntries( - selectInheritedMatrixRoomEntries({ - entries: base.rooms, - accountId, - }), - accountConfig?.rooms, - Boolean(accountConfig && Object.hasOwn(accountConfig, "rooms")), - ); - // Room maps need custom scoping, so keep the generic merge for all other fields. - const { groups: _ignoredGroups, rooms: _ignoredRooms, ...rest } = merged; - return { - ...rest, - ...(groups ? { groups } : {}), - ...(rooms ? { rooms } : {}), - }; -} +export { resolveMatrixAccountConfig } from "./account-config.js"; diff --git a/extensions/matrix/src/matrix/monitor/ack-config.ts b/extensions/matrix/src/matrix/monitor/ack-config.ts index 6a6c9de3b8c..52ed93e52bc 100644 --- a/extensions/matrix/src/matrix/monitor/ack-config.ts +++ b/extensions/matrix/src/matrix/monitor/ack-config.ts @@ -1,5 +1,5 @@ import type { CoreConfig } from "../../types.js"; -import { resolveMatrixAccountConfig } from "../accounts.js"; +import { resolveMatrixAccountConfig } from "../account-config.js"; import { resolveAckReaction, type OpenClawConfig } from "./runtime-api.js"; type MatrixAckReactionScope = "group-mentions" | "group-all" | "direct" | "all" | "none" | "off"; diff --git a/extensions/matrix/src/matrix/monitor/index.test.ts b/extensions/matrix/src/matrix/monitor/index.test.ts index bdedec3f5d2..22d4ef00e5f 100644 --- a/extensions/matrix/src/matrix/monitor/index.test.ts +++ b/extensions/matrix/src/matrix/monitor/index.test.ts @@ -177,7 +177,7 @@ vi.mock("../../runtime.js", () => ({ config: { loadConfig: () => ({ channels: { - matrix: {}, + matrix: hoisted.accountConfig, }, }), writeConfigFile: vi.fn(), diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index 1da020c3f18..0b49a9b7ba1 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -10,7 +10,8 @@ import { } from "../../runtime-api.js"; import { getMatrixRuntime } from "../../runtime.js"; import type { CoreConfig, ReplyToMode } from "../../types.js"; -import { resolveConfiguredMatrixBotUserIds, resolveMatrixAccount } from "../accounts.js"; +import { resolveConfiguredMatrixBotUserIds } from "../accounts.js"; +import { resolveMatrixAccountConfig } from "../account-config.js"; import { setActiveMatrixClient } from "../active-client.js"; import { isBunRuntime, @@ -82,8 +83,10 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const effectiveAccountId = authContext.accountId; // Resolve account-specific config for multi-account support - const account = resolveMatrixAccount({ cfg, accountId: effectiveAccountId }); - const accountConfig = account.config; + const accountConfig = resolveMatrixAccountConfig({ + cfg, + accountId: effectiveAccountId, + }); const allowlistOnly = accountConfig.allowlistOnly === true; const accountAllowBots = accountConfig.allowBots; @@ -179,7 +182,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi warnMissingProviderGroupPolicyFallbackOnce({ providerMissingFallbackApplied, providerKey: "matrix", - accountId: account.accountId, + accountId: effectiveAccountId, blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room, log: (message) => logVerboseMessage(message), }); @@ -190,18 +193,18 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const threadBindingIdleTimeoutMs = resolveThreadBindingIdleTimeoutMsForChannel({ cfg, channel: "matrix", - accountId: account.accountId, + accountId: effectiveAccountId, }); const threadBindingMaxAgeMs = resolveThreadBindingMaxAgeMsForChannel({ cfg, channel: "matrix", - accountId: account.accountId, + accountId: effectiveAccountId, }); const dmConfig = accountConfig.dm; const dmEnabled = dmConfig?.enabled ?? true; const dmPolicyRaw = dmConfig?.policy ?? "pairing"; const dmPolicy = allowlistOnly && dmPolicyRaw !== "disabled" ? "allowlist" : dmPolicyRaw; - const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix", account.accountId); + const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix", effectiveAccountId); const globalGroupChatHistoryLimit = ( cfg.messages as { groupChat?: { historyLimit?: number } } | undefined )?.groupChat?.historyLimit; @@ -252,7 +255,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi client, core, cfg, - accountId: account.accountId, + accountId: effectiveAccountId, runtime, logger, logVerboseMessage, @@ -291,7 +294,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi try { threadBindingManager = await createMatrixThreadBindingManager({ - accountId: account.accountId, + accountId: effectiveAccountId, auth, client, env: process.env, @@ -315,7 +318,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi .readAllowFromStore({ channel: "matrix", env: process.env, - accountId: account.accountId, + accountId: effectiveAccountId, }) .catch(() => []), directTracker, @@ -343,7 +346,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi await runMatrixStartupMaintenance({ client, auth, - accountId: account.accountId, + accountId: effectiveAccountId, effectiveAccountId, accountConfig, logger, diff --git a/extensions/matrix/src/matrix/monitor/reaction-events.ts b/extensions/matrix/src/matrix/monitor/reaction-events.ts index a6604682dc8..013c0741adc 100644 --- a/extensions/matrix/src/matrix/monitor/reaction-events.ts +++ b/extensions/matrix/src/matrix/monitor/reaction-events.ts @@ -1,6 +1,6 @@ import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime"; import type { CoreConfig } from "../../types.js"; -import { resolveMatrixAccountConfig } from "../accounts.js"; +import { resolveMatrixAccountConfig } from "../account-config.js"; import { extractMatrixReactionAnnotation } from "../reaction-common.js"; import type { MatrixClient } from "../sdk.js"; import { resolveMatrixInboundRoute } from "./route.js"; diff --git a/extensions/matrix/src/matrix/send/client.ts b/extensions/matrix/src/matrix/send/client.ts index 214e27294a8..5997c46e3b3 100644 --- a/extensions/matrix/src/matrix/send/client.ts +++ b/extensions/matrix/src/matrix/send/client.ts @@ -1,6 +1,6 @@ import { getMatrixRuntime } from "../../runtime.js"; import type { CoreConfig } from "../../types.js"; -import { resolveMatrixAccountConfig } from "../accounts.js"; +import { resolveMatrixAccountConfig } from "../account-config.js"; import { withResolvedRuntimeMatrixClient } from "../client-bootstrap.js"; import type { MatrixClient } from "../sdk.js"; diff --git a/extensions/telegram/src/bot-deps.ts b/extensions/telegram/src/bot-deps.ts index 237b93b9dc4..b8759ec40ee 100644 --- a/extensions/telegram/src/bot-deps.ts +++ b/extensions/telegram/src/bot-deps.ts @@ -8,7 +8,7 @@ import { loadConfig, resolveStorePath } from "openclaw/plugin-sdk/config-runtime import { loadSessionStore } from "openclaw/plugin-sdk/config-runtime"; import { readChannelAllowFromStore } from "openclaw/plugin-sdk/conversation-runtime"; import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime"; -import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-runtime"; +import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-dispatch-runtime"; import { loadWebMedia } from "openclaw/plugin-sdk/web-media"; import { syncTelegramMenuCommands } from "./bot-native-command-menu.js"; import { deliverReplies, emitInternalMessageSentHook } from "./bot/delivery.js"; diff --git a/extensions/telegram/src/bot.ts b/extensions/telegram/src/bot.ts index 2dd47d58556..e3ec75883ef 100644 --- a/extensions/telegram/src/bot.ts +++ b/extensions/telegram/src/bot.ts @@ -17,7 +17,7 @@ import { } from "openclaw/plugin-sdk/conversation-runtime"; import { formatUncaughtError } from "openclaw/plugin-sdk/error-runtime"; import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "openclaw/plugin-sdk/reply-history"; -import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime"; +import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking"; import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; import { getChildLogger } from "openclaw/plugin-sdk/runtime-env"; import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";