fix(discord): normalize command allowFrom prefixes

This commit is contained in:
Sebastian
2026-02-17 08:45:12 -05:00
parent 96fb276481
commit dff8692613
3 changed files with 68 additions and 11 deletions

View File

@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
- Telegram: preserve private-chat topic `message_thread_id` on outbound sends (message/sticker/poll), keep thread-not-found retry fallback, and avoid masking `chat not found` routing errors. (#18993) Thanks @obviyus.
- Discord: prevent duplicate media delivery when the model uses the `message send` tool with media, by skipping media extraction from messaging tool results since the tool already sent the message directly. (#18270)
- Discord: route `audioAsVoice` auto-replies through the voice message API so opt-in audio renders as voice messages. (#18041) Thanks @zerone0x.
- Discord/Commands: normalize `commands.allowFrom` entries with `user:`/`discord:`/`pk:` prefixes and `<@id>` mentions so command authorization matches Discord allowlist behavior. (#18042)
- Telegram: keep draft-stream preview replies attached to the user message for `replyToMode: "all"` in groups and DMs, preserving threaded reply context from preview through finalization. (#17880) Thanks @yinghaosang.
- Telegram: prevent streaming final replies from being overwritten by later final/error payloads, and suppress fallback tool-error warnings when a recovered assistant answer already exists after tool calls. (#17883) Thanks @Marvae and @obviyus.
- Telegram: debounce the first draft-stream preview update (30-char threshold) and finalize short responses by editing the stop-time preview message, improving first push notifications and avoiding duplicate final sends. (#18148) Thanks @Marvae.

View File

@@ -253,6 +253,15 @@ describe("resolveCommandAuthorization", () => {
} as MsgContext;
}
function makeDiscordContext(senderId: string, fromOverride?: string): MsgContext {
return {
Provider: "discord",
Surface: "discord",
From: fromOverride ?? `discord:${senderId}`,
SenderId: senderId,
} as MsgContext;
}
function resolveWithCommandsAllowFrom(senderId: string, commandAuthorized: boolean) {
return resolveCommandAuthorization({
ctx: makeWhatsAppContext(senderId),
@@ -372,6 +381,48 @@ describe("resolveCommandAuthorization", () => {
expect(auth.isAuthorizedSender).toBe(true);
});
it("normalizes Discord commands.allowFrom prefixes and mentions", () => {
const cfg = {
commands: {
allowFrom: {
discord: ["user:123", "<@!456>", "pk:member-1"],
},
},
} as OpenClawConfig;
const userAuth = resolveCommandAuthorization({
ctx: makeDiscordContext("123"),
cfg,
commandAuthorized: false,
});
expect(userAuth.isAuthorizedSender).toBe(true);
const mentionAuth = resolveCommandAuthorization({
ctx: makeDiscordContext("456"),
cfg,
commandAuthorized: false,
});
expect(mentionAuth.isAuthorizedSender).toBe(true);
const pkAuth = resolveCommandAuthorization({
ctx: makeDiscordContext("member-1", "discord:999"),
cfg,
commandAuthorized: false,
});
expect(pkAuth.isAuthorizedSender).toBe(true);
const deniedAuth = resolveCommandAuthorization({
ctx: makeDiscordContext("other"),
cfg,
commandAuthorized: false,
});
expect(deniedAuth.isAuthorizedSender).toBe(false);
});
});
});

View File

@@ -82,6 +82,21 @@ const formatLower = (allowFrom: Array<string | number>) =>
.filter(Boolean)
.map((entry) => entry.toLowerCase());
const formatDiscordAllowFrom = (allowFrom: Array<string | number>) =>
allowFrom
.map((entry) =>
String(entry)
.trim()
.replace(/^<@!?/, "")
.replace(/>$/, "")
.replace(/^discord:/i, "")
.replace(/^user:/i, "")
.replace(/^pk:/i, "")
.trim()
.toLowerCase(),
)
.filter(Boolean);
function buildDirectOrGroupThreadToolContext(params: {
context: ChannelThreadingContext;
hasRepliedRef: ChannelThreadingToolContext["hasRepliedRef"];
@@ -218,17 +233,7 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
String(entry),
);
},
formatAllowFrom: ({ allowFrom }) =>
allowFrom
.map((entry) => String(entry).trim())
.filter(Boolean)
.map((entry) =>
entry
.replace(/^discord:/i, "")
.replace(/^user:/i, "")
.replace(/^pk:/i, "")
.toLowerCase(),
),
formatAllowFrom: ({ allowFrom }) => formatDiscordAllowFrom(allowFrom),
},
groups: {
resolveRequireMention: resolveDiscordGroupRequireMention,