fix(discord): accept shipped nested DM access config (#125359)

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Peter Steinberger
2026-08-17 11:49:26 -07:00
committed by GitHub
parent 45e6784338
commit 0cc4d42f3f
2 changed files with 67 additions and 6 deletions
+33 -4
View File
@@ -75,12 +75,41 @@ describe("discord config schema", () => {
expectInvalidDiscordConfig({ mentionAliases: { opslead: "not-a-user-id" } });
});
it("rejects legacy nested DM access keys", () => {
const issues = expectInvalidDiscordConfig({
dm: { policy: "open", allowFrom: [] },
it("normalizes shipped nested DM access keys at root and account scope", () => {
const cfg = expectValidDiscordConfig({
dmPolicy: "pairing",
allowFrom: ["canonical-root"],
dm: { enabled: false, policy: "open", allowFrom: ["legacy-root"] },
accounts: {
work: {
dmPolicy: "allowlist",
allowFrom: ["canonical-account"],
dm: { groupEnabled: true, policy: "disabled", allowFrom: ["legacy-account"] },
},
personal: {
dm: { enabled: true, policy: "open", allowFrom: ["*"] },
},
},
});
expect(issues[0]?.path.join(".")).toBe("dm");
expect(cfg).toMatchObject({
dmPolicy: "pairing",
allowFrom: ["canonical-root"],
dm: { enabled: false },
accounts: {
work: {
dmPolicy: "allowlist",
allowFrom: ["canonical-account"],
dm: { groupEnabled: true },
},
personal: {
dmPolicy: "open",
allowFrom: ["*"],
dm: { enabled: true },
},
},
});
expectInvalidDiscordConfig({ dm: { enabled: false, unexpected: true } });
});
it("accepts textChunkLimit without reviving legacy message limits", () => {
+34 -2
View File
@@ -1,3 +1,4 @@
import { normalizeLegacyDmAliases } from "openclaw/plugin-sdk/channel-config-helpers";
// Discord helper module supports config schema behavior.
import {
buildChannelAllowBotsSchema,
@@ -15,6 +16,7 @@ import {
requireOpenAllowFrom,
TtsConfigSchema,
} from "openclaw/plugin-sdk/channel-config-schema";
import { asObjectRecord } from "openclaw/plugin-sdk/runtime-doctor-migrations";
import {
buildSecretInputSchema,
registerSensitiveConfigSchema,
@@ -196,7 +198,7 @@ const DiscordVoiceSchema = z
.strict()
.optional();
const DiscordAccountSchema = z
const DiscordAccountSchemaBase = z
.object({
...buildCommonChannelAccountShape({
omit: ["groupAllowFrom"],
@@ -372,7 +374,32 @@ const DiscordAccountSchema = z
// can inherit top-level allowFrom via runtime shallow merge.
});
export const DiscordConfigSchema = DiscordAccountSchema.extend({
function normalizeShippedDiscordDmAliases(value: unknown): unknown {
const entry = asObjectRecord(value);
if (!entry) {
return value;
}
const updated = normalizeLegacyDmAliases({
entry,
pathPrefix: "channels.discord",
changes: [],
}).entry;
const dm = asObjectRecord(updated.dm);
if (!dm || (dm.policy === undefined && dm.allowFrom === undefined)) {
return updated;
}
const { policy: _policy, allowFrom: _allowFrom, ...retainedDm } = dm;
const { dm: _dm, ...rest } = updated;
return Object.keys(retainedDm).length > 0 ? { ...rest, dm: retainedDm } : rest;
}
const DiscordAccountSchema = z.preprocess(
normalizeShippedDiscordDmAliases,
DiscordAccountSchemaBase,
);
const DiscordConfigSchemaBase = DiscordAccountSchemaBase.extend({
accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional(),
defaultAccount: z.string().optional(),
}).superRefine((value, ctx) => {
@@ -422,6 +449,11 @@ export const DiscordConfigSchema = DiscordAccountSchema.extend({
}
});
export const DiscordConfigSchema = z.preprocess(
normalizeShippedDiscordDmAliases,
DiscordConfigSchemaBase,
);
export const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema, {
uiHints: discordChannelConfigUiHints,
});