diff --git a/CHANGELOG.md b/CHANGELOG.md index 8412a2ab15a..b7323c5c0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Docs: https://docs.openclaw.ai - Proxy/audio: convert standard `FormData` bodies before proxy-backed undici fetches, so audio transcription and multipart uploads no longer send `[object FormData]` when `HTTP_PROXY` or `HTTPS_PROXY` is configured. Fixes #48554. Thanks @dco5. - Discord: allow explicitly configured ack reactions in tool-only guild channels while keeping automatic lifecycle/status reactions suppressed. Fixes #74922. Thanks @samvilian and @BlueBirdBack. - Discord: enable session-backed A2A announce target lookup so `sessions_send` uses the target session's `deliveryContext.accountId` or `lastAccountId` instead of falling back to the default bot in multi-account setups. Fixes #42652; refs #51626 and #44773; supersedes #73975. Thanks @irchelper, @dpalfox, and @Lanfei. +- Discord/setup: write resolved guild/channel allowlist selections to the selected guild and channel instead of falling back to the wildcard guild during setup. Supersedes #47788. Thanks @Eldersonar. - Discord: treat abort-time Carbon reconnect-exhausted events as expected shutdown during stale-socket restarts, so health-monitor restarts no longer reject the monitor lifecycle. Carries forward #58216; supersedes #73949. Thanks @Perttulands. - Discord/native commands: return an explicit warning when slash command dispatch or direct plugin execution produces no visible reply instead of a success-style completion ack. Fixes #58986; supersedes #62057. Thanks @jb510. - Discord: keep typing indicators alive during long tool runs and auto-compaction while keepalive ticks continue, so active sessions do not appear stalled before the final reply. Thanks @Squirbie. diff --git a/extensions/discord/src/setup-core.ts b/extensions/discord/src/setup-core.ts index abee14d8b41..f148a1ac534 100644 --- a/extensions/discord/src/setup-core.ts +++ b/extensions/discord/src/setup-core.ts @@ -27,13 +27,44 @@ const DISCORD_TOKEN_HELP_LINES = [ `Docs: ${formatDocsLink("/discord", "discord")}`, ]; +type DiscordGuildChannelAllowlistEntry = { + guildKey: string; + channelKey?: string; +}; + +type DiscordSetupAllowlistResolution = { + resolved?: boolean; + guildId?: string; + channelId?: string; + guildKey?: string; + channelKey?: string; +}; + +function mapDiscordSetupAllowlistEntries(resolved: unknown): DiscordGuildChannelAllowlistEntry[] { + if (!Array.isArray(resolved)) { + return []; + } + return resolved.flatMap((entry): DiscordGuildChannelAllowlistEntry[] => { + if (!entry || typeof entry !== "object") { + return []; + } + const row = entry as DiscordSetupAllowlistResolution; + if (row.resolved === false) { + return []; + } + const guildKey = normalizeOptionalString(row.guildId ?? row.guildKey); + if (!guildKey) { + return []; + } + const channelKey = normalizeOptionalString(row.channelId ?? row.channelKey); + return channelKey ? [{ guildKey, channelKey }] : [{ guildKey }]; + }); +} + function setDiscordGuildChannelAllowlist( cfg: OpenClawConfig, accountId: string, - entries: Array<{ - guildKey: string; - channelKey?: string; - }>, + entries: DiscordGuildChannelAllowlistEntry[], ): OpenClawConfig { const baseGuilds = accountId === DEFAULT_ACCOUNT_ID @@ -152,7 +183,8 @@ export function createDiscordSetupWizardBase(handlers: { cfg: OpenClawConfig; accountId: string; resolved: unknown; - }) => setDiscordGuildChannelAllowlist(cfg, accountId, resolved as never), + }) => + setDiscordGuildChannelAllowlist(cfg, accountId, mapDiscordSetupAllowlistEntries(resolved)), }), allowFrom: createAccountScopedAllowFromSection({ channel, diff --git a/extensions/discord/src/setup-surface.test.ts b/extensions/discord/src/setup-surface.test.ts index 48bc2dc1eb1..5a6f0ea414b 100644 --- a/extensions/discord/src/setup-surface.test.ts +++ b/extensions/discord/src/setup-surface.test.ts @@ -94,3 +94,44 @@ describe("discordSetupWizard.status", () => { expect(configured).toBe(false); }); }); + +describe("discordSetupWizard.groupAccess", () => { + it("writes resolved Discord channel rows to their selected guild and channel", () => { + const next = discordSetupWizard.groupAccess?.applyAllowlist?.({ + cfg: { + channels: { + discord: { + guilds: { + existing: { + channels: { + keep: { enabled: true }, + }, + }, + }, + }, + }, + } as OpenClawConfig, + accountId: "default", + resolved: [ + { + input: "OpenClaw/#triage", + resolved: true, + guildId: "guild-1", + channelId: "channel-1", + }, + { + input: "missing", + resolved: false, + }, + ], + }); + + expect(next?.channels?.discord?.guilds?.["guild-1"]?.channels?.["channel-1"]).toEqual({ + enabled: true, + }); + expect(next?.channels?.discord?.guilds?.["*"]).toBeUndefined(); + expect(next?.channels?.discord?.guilds?.existing?.channels?.keep).toEqual({ + enabled: true, + }); + }); +});