mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 04:40:43 +00:00
fix(discord): route guild text commands (#78080)
This commit is contained in:
@@ -102,6 +102,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Discord/gateway: measure heartbeat ACK timeouts from the actual heartbeat send, preventing late initial heartbeats from triggering false reconnect loops while the channel is still awaiting readiness. Fixes #77668. (#78087) Thanks @bryce-d-greybeard and @NikolaFC.
|
||||
- Discord/guilds: route plain text control commands such as `/steer` through the normal authorization and mention gate instead of silently dropping them before an agent session can see them. Fixes #78080. Thanks @ramitrkar-hash.
|
||||
- Control UI/Sessions: make the compaction count a compact `N Checkpoint(s)` disclosure and show expanded session-level details with modern checkpoint history cards across responsive table layouts. Thanks @BunsDev.
|
||||
- Control UI/performance: keep chat and channel tabs responsive while history payloads and channel probes are slow, label partial channel status, and record slow chat/config render timings in the event log. Thanks @BunsDev.
|
||||
- Control UI/sessions: fire the documented `/new` command and lifecycle hooks only for explicit Control UI session creation, restoring session-memory and custom hook capture without changing SDK parent-session creates. Fixes #76957. Thanks @BunsDev.
|
||||
|
||||
@@ -65,9 +65,11 @@ export function createDiscordMessage(params: {
|
||||
mentionedEveryone?: boolean;
|
||||
attachments?: Array<Record<string, unknown>>;
|
||||
webhookId?: string;
|
||||
type?: import("../internal/discord.js").MessageType;
|
||||
}): import("../internal/discord.js").Message {
|
||||
return {
|
||||
id: params.id,
|
||||
type: params.type,
|
||||
content: params.content,
|
||||
timestamp: new Date().toISOString(),
|
||||
channelId: params.channelId,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ChannelType } from "../internal/discord.js";
|
||||
import { ChannelType, MessageType } from "../internal/discord.js";
|
||||
import { createPartialDiscordChannelWithThrowingGetters } from "../test-support/partial-channel.js";
|
||||
|
||||
const transcribeFirstAudioMock = vi.hoisted(() => vi.fn());
|
||||
@@ -966,6 +966,95 @@ describe("preflightDiscordMessage", () => {
|
||||
expect(result).not.toBeNull();
|
||||
});
|
||||
|
||||
it("routes ordinary guild text control commands through authorization instead of dropping them", async () => {
|
||||
const channelId = "channel-text-control-command";
|
||||
const guildId = "guild-text-control-command";
|
||||
const message = createDiscordMessage({
|
||||
id: "m-text-control-command",
|
||||
channelId,
|
||||
content: "/steer keep digging",
|
||||
author: {
|
||||
id: "user-1",
|
||||
bot: false,
|
||||
username: "Alice",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: {} as DiscordConfig,
|
||||
data: createGuildEvent({
|
||||
channelId,
|
||||
guildId,
|
||||
author: message.author,
|
||||
message,
|
||||
}),
|
||||
client: createGuildTextClient(channelId),
|
||||
}),
|
||||
allowFrom: ["discord:user-1"],
|
||||
guildEntries: {
|
||||
[guildId]: {
|
||||
channels: {
|
||||
[channelId]: {
|
||||
enabled: true,
|
||||
requireMention: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.baseText).toBe("/steer keep digging");
|
||||
expect(result?.commandAuthorized).toBe(true);
|
||||
expect(result?.shouldRequireMention).toBe(true);
|
||||
expect(result?.shouldBypassMention).toBe(true);
|
||||
});
|
||||
|
||||
it("still drops Discord native command echo messages", async () => {
|
||||
const channelId = "channel-native-command-echo";
|
||||
const guildId = "guild-native-command-echo";
|
||||
const message = createDiscordMessage({
|
||||
id: "m-native-command-echo",
|
||||
channelId,
|
||||
content: "/steer keep digging",
|
||||
type: MessageType.ChatInputCommand,
|
||||
author: {
|
||||
id: "user-1",
|
||||
bot: false,
|
||||
username: "Alice",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: {} as DiscordConfig,
|
||||
data: createGuildEvent({
|
||||
channelId,
|
||||
guildId,
|
||||
author: message.author,
|
||||
message,
|
||||
}),
|
||||
client: createGuildTextClient(channelId),
|
||||
}),
|
||||
allowFrom: ["discord:user-1"],
|
||||
guildEntries: {
|
||||
[guildId]: {
|
||||
channels: {
|
||||
[channelId]: {
|
||||
enabled: true,
|
||||
requireMention: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("does not mask mention gating when bot id is missing but mention patterns can detect", async () => {
|
||||
const channelId = "channel-missing-bot-id-mention-gate";
|
||||
const guildId = "guild-missing-bot-id-mention-gate";
|
||||
|
||||
@@ -230,13 +230,6 @@ export async function preflightDiscordMessage(
|
||||
includeForwarded: false,
|
||||
});
|
||||
|
||||
// Intercept text-only slash commands (e.g. user typing "/reset" instead of using Discord's slash command picker)
|
||||
// These should not be forwarded to the agent; proper slash command interactions are handled elsewhere
|
||||
if (!isDirectMessage && baseText && hasControlCommand(baseText, params.cfg)) {
|
||||
logVerbose(`discord: drop text-based slash command ${message.id} (intercepted at gateway)`);
|
||||
return null;
|
||||
}
|
||||
|
||||
recordChannelActivity({
|
||||
channel: "discord",
|
||||
accountId: params.accountId,
|
||||
|
||||
Reference in New Issue
Block a user