fix: preserve discord setup channel allowlists (#47788) (thanks @Eldersonar)

This commit is contained in:
Peter Steinberger
2026-05-02 13:01:02 +01:00
parent 084c4beb2e
commit 7ed58a3efb
3 changed files with 79 additions and 5 deletions

View File

@@ -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.

View File

@@ -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,

View File

@@ -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,
});
});
});