diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts index a482632ebea..d6d1a3130fb 100644 --- a/extensions/bluebubbles/src/channel.ts +++ b/extensions/bluebubbles/src/channel.ts @@ -31,7 +31,8 @@ import { resolveBlueBubblesMessageId } from "./monitor.js"; import { monitorBlueBubblesProvider, resolveWebhookPathFromConfig } from "./monitor.js"; import { probeBlueBubbles, type BlueBubblesProbe } from "./probe.js"; import { sendMessageBlueBubbles } from "./send.js"; -import { blueBubblesSetupAdapter, blueBubblesSetupWizard } from "./setup-surface.js"; +import { blueBubblesSetupAdapter } from "./setup-core.js"; +import { blueBubblesSetupWizard } from "./setup-surface.js"; import { extractHandleFromChatGuid, looksLikeBlueBubblesTargetId, diff --git a/extensions/bluebubbles/src/setup-core.ts b/extensions/bluebubbles/src/setup-core.ts new file mode 100644 index 00000000000..930fa29a64e --- /dev/null +++ b/extensions/bluebubbles/src/setup-core.ts @@ -0,0 +1,84 @@ +import { setTopLevelChannelDmPolicyWithAllowFrom } from "../../../src/channels/plugins/onboarding/helpers.js"; +import { + applyAccountNameToChannelSection, + migrateBaseNameToDefaultAccount, + patchScopedAccountConfig, +} from "../../../src/channels/plugins/setup-helpers.js"; +import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js"; +import type { OpenClawConfig } from "../../../src/config/config.js"; +import type { DmPolicy } from "../../../src/config/types.js"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js"; +import { applyBlueBubblesConnectionConfig } from "./config-apply.js"; + +const channel = "bluebubbles" as const; + +export function setBlueBubblesDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig { + return setTopLevelChannelDmPolicyWithAllowFrom({ + cfg, + channel, + dmPolicy, + }); +} + +export function setBlueBubblesAllowFrom( + cfg: OpenClawConfig, + accountId: string, + allowFrom: string[], +): OpenClawConfig { + return patchScopedAccountConfig({ + cfg, + channelKey: channel, + accountId, + patch: { allowFrom }, + ensureChannelEnabled: false, + ensureAccountEnabled: false, + }); +} + +export const blueBubblesSetupAdapter: ChannelSetupAdapter = { + resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), + applyAccountName: ({ cfg, accountId, name }) => + applyAccountNameToChannelSection({ + cfg, + channelKey: channel, + accountId, + name, + }), + validateInput: ({ input }) => { + if (!input.httpUrl && !input.password) { + return "BlueBubbles requires --http-url and --password."; + } + if (!input.httpUrl) { + return "BlueBubbles requires --http-url."; + } + if (!input.password) { + return "BlueBubbles requires --password."; + } + return null; + }, + applyAccountConfig: ({ cfg, accountId, input }) => { + const namedConfig = applyAccountNameToChannelSection({ + cfg, + channelKey: channel, + accountId, + name: input.name, + }); + const next = + accountId !== DEFAULT_ACCOUNT_ID + ? migrateBaseNameToDefaultAccount({ + cfg: namedConfig, + channelKey: channel, + }) + : namedConfig; + return applyBlueBubblesConnectionConfig({ + cfg: next, + accountId, + patch: { + serverUrl: input.httpUrl, + password: input.password, + webhookPath: input.webhookPath, + }, + onlyDefinedFields: true, + }); + }, +}; diff --git a/extensions/bluebubbles/src/setup-surface.ts b/extensions/bluebubbles/src/setup-surface.ts index 0cb23998663..f4ee2d98db4 100644 --- a/extensions/bluebubbles/src/setup-surface.ts +++ b/extensions/bluebubbles/src/setup-surface.ts @@ -2,18 +2,11 @@ import type { ChannelOnboardingDmPolicy } from "../../../src/channels/plugins/on import { mergeAllowFromEntries, resolveOnboardingAccountId, - setTopLevelChannelDmPolicyWithAllowFrom, } from "../../../src/channels/plugins/onboarding/helpers.js"; -import { - applyAccountNameToChannelSection, - migrateBaseNameToDefaultAccount, - patchScopedAccountConfig, -} from "../../../src/channels/plugins/setup-helpers.js"; import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js"; -import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js"; import type { OpenClawConfig } from "../../../src/config/config.js"; import type { DmPolicy } from "../../../src/config/types.js"; -import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js"; +import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js"; import { formatDocsLink } from "../../../src/terminal/links.js"; import type { WizardPrompter } from "../../../src/wizard/prompts.js"; import { @@ -24,35 +17,17 @@ import { import { applyBlueBubblesConnectionConfig } from "./config-apply.js"; import { DEFAULT_WEBHOOK_PATH } from "./monitor-shared.js"; import { hasConfiguredSecretInput, normalizeSecretInputString } from "./secret-input.js"; +import { + blueBubblesSetupAdapter, + setBlueBubblesAllowFrom, + setBlueBubblesDmPolicy, +} from "./setup-core.js"; import { parseBlueBubblesAllowTarget } from "./targets.js"; import { normalizeBlueBubblesServerUrl } from "./types.js"; const channel = "bluebubbles" as const; const CONFIGURE_CUSTOM_WEBHOOK_FLAG = "__bluebubblesConfigureCustomWebhookPath"; -function setBlueBubblesDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig { - return setTopLevelChannelDmPolicyWithAllowFrom({ - cfg, - channel, - dmPolicy, - }); -} - -function setBlueBubblesAllowFrom( - cfg: OpenClawConfig, - accountId: string, - allowFrom: string[], -): OpenClawConfig { - return patchScopedAccountConfig({ - cfg, - channelKey: channel, - accountId, - patch: { allowFrom }, - ensureChannelEnabled: false, - ensureAccountEnabled: false, - }); -} - function parseBlueBubblesAllowFromInput(raw: string): string[] { return raw .split(/[\n,]+/g) @@ -183,54 +158,6 @@ const dmPolicy: ChannelOnboardingDmPolicy = { promptAllowFrom: promptBlueBubblesAllowFrom, }; -export const blueBubblesSetupAdapter: ChannelSetupAdapter = { - resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), - applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ - cfg, - channelKey: channel, - accountId, - name, - }), - validateInput: ({ input }) => { - if (!input.httpUrl && !input.password) { - return "BlueBubbles requires --http-url and --password."; - } - if (!input.httpUrl) { - return "BlueBubbles requires --http-url."; - } - if (!input.password) { - return "BlueBubbles requires --password."; - } - return null; - }, - applyAccountConfig: ({ cfg, accountId, input }) => { - const namedConfig = applyAccountNameToChannelSection({ - cfg, - channelKey: channel, - accountId, - name: input.name, - }); - const next = - accountId !== DEFAULT_ACCOUNT_ID - ? migrateBaseNameToDefaultAccount({ - cfg: namedConfig, - channelKey: channel, - }) - : namedConfig; - return applyBlueBubblesConnectionConfig({ - cfg: next, - accountId, - patch: { - serverUrl: input.httpUrl, - password: input.password, - webhookPath: input.webhookPath, - }, - onlyDefinedFields: true, - }); - }, -}; - export const blueBubblesSetupWizard: ChannelSetupWizard = { channel, stepOrder: "text-first", @@ -383,3 +310,5 @@ export const blueBubblesSetupWizard: ChannelSetupWizard = { }, }), }; + +export { blueBubblesSetupAdapter };