mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:40:44 +00:00
fix: harden Discord voice commands in threads
This commit is contained in:
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Codex harness: apply the GPT-5 behavior and heartbeat prompt overlay to native Codex app-server runs, so `codex/gpt-5.x` sessions get the same follow-through, tool-use, and proactive heartbeat guidance as OpenAI GPT-5 runs.
|
||||
- OpenAI/Responses: keep embedded OpenAI Responses runs on HTTP when `models.providers.openai.baseUrl` points at a local mock or other non-public endpoint, so mocked/custom endpoints no longer drift onto the hardcoded public websocket transport. (#69815) Thanks @vincentkoc.
|
||||
- Channels/config: require resolved runtime config on channel send/action/client helpers and block runtime helper `loadConfig()` calls, so SecretRefs are resolved at startup/boundaries instead of being re-read during sends.
|
||||
- Discord: pass resolved runtime config through guild and moderation action helpers, so thread-originated Discord commands can run channel, member, role, and guild actions without falling back to runtime config reads. (#70215) Thanks @szponeczek.
|
||||
- CLI/channels: preserve bundled setup promotion metadata when a loaded partial channel plugin omits it, so adding a non-default account still moves legacy single-account fields such as Telegram `streaming` into `accounts.default`.
|
||||
- Telegram: keep the sent-message ownership cache isolated per configured session store, so own-message reaction filtering remains correct with custom `session.store` paths.
|
||||
- Security/update: fail closed when exact pinned npm plugin or hook-pack updates detect integrity drift, and expose aborted plugin drift details in `openclaw update --json`.
|
||||
@@ -35,6 +36,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Memory-core/dreaming: suppress the startup-only managed dreaming cron unavailable warning when the cron service is still attaching, while preserving the runtime warning if cron genuinely remains unavailable. Fixes #69939. (#69941) Thanks @Sanjays2402.
|
||||
- Mattermost: suppress reasoning-only payloads even when they arrive as blockquoted `> Reasoning:` text, preventing `/reasoning on` from leaking thinking into channel posts. (#69927) Thanks @lawrence3699.
|
||||
- Discord: read `channel.parentId` through a safe accessor in the slash-command, reaction, and model-picker paths so partial `GuildThreadChannel` prototype getters no longer throw `Cannot access rawData on partial Channel` when commands like `/new` run from inside a thread. Fixes #69861. (#69908) Thanks @neeravmakwana.
|
||||
- Discord: use safe channel name and parent accessors across voice command authorization, so `/vc` commands from partial Discord thread channels no longer crash on Carbon rawData getters. (#70199) Thanks @hanamizuki.
|
||||
- Browser/Chrome MCP: reset cached existing-session control sessions when a `navigate_page` call times out, so one stuck navigation no longer poisons the browser profile until a gateway restart. (#69733) Thanks @ayeshakhalid192007-dev.
|
||||
- Browser/Chrome MCP: propagate click timeouts and abort signals to existing-session actions so a stuck click fails fast and reconnects instead of poisoning the browser tool until gateway restart. (#63524) Thanks @dongseok0.
|
||||
- Gateway/channel health: base stale-socket recovery on provider-proven transport activity instead of inbound app-event freshness, preventing quiet Slack, Discord, Telegram, Matrix, and local-style channels from being restarted solely because no user traffic arrived. (#69833) Thanks @bek91.
|
||||
|
||||
@@ -96,4 +96,38 @@ describe("createDiscordVoiceCommand", () => {
|
||||
ephemeral: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("vc status tolerates partial thread channels with throwing getters", async () => {
|
||||
const statusSpy = vi.fn(() => []);
|
||||
const manager = {
|
||||
status: statusSpy,
|
||||
} as unknown as DiscordVoiceManager;
|
||||
const { status } = createVoiceCommandHarness(manager);
|
||||
const partialChannel = { id: "123456789012345678" };
|
||||
Object.defineProperties(partialChannel, {
|
||||
name: {
|
||||
get() {
|
||||
throw new Error("Cannot access rawData on partial Channel");
|
||||
},
|
||||
},
|
||||
parentId: {
|
||||
get() {
|
||||
throw new Error("Cannot access rawData on partial Channel");
|
||||
},
|
||||
},
|
||||
});
|
||||
const { interaction, reply } = createInteraction({
|
||||
channel: partialChannel as CommandInteraction["channel"],
|
||||
client: { fetchChannel: vi.fn(async () => null) } as unknown as CommandInteraction["client"],
|
||||
guild: { id: "g1", name: "Guild" } as CommandInteraction["guild"],
|
||||
});
|
||||
|
||||
await expect(status.run(interaction)).resolves.toBeUndefined();
|
||||
|
||||
expect(statusSpy).toHaveBeenCalledTimes(1);
|
||||
expect(reply).toHaveBeenCalledWith({
|
||||
content: "No active voice sessions.",
|
||||
ephemeral: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,13 +66,8 @@ async function authorizeVoiceCommand(
|
||||
}
|
||||
|
||||
const channelId = channelOverride?.id ?? channel?.id ?? "";
|
||||
const rawChannelName =
|
||||
channelOverride?.name ?? (channel && "name" in channel ? (channel.name as string) : undefined);
|
||||
const rawParentId =
|
||||
channelOverride?.parentId ??
|
||||
("parentId" in (channel ?? {})
|
||||
? ((channel as { parentId?: string }).parentId ?? undefined)
|
||||
: undefined);
|
||||
const rawChannelName = channelOverride?.name ?? resolveDiscordChannelNameSafe(channel);
|
||||
const rawParentId = channelOverride?.parentId ?? resolveDiscordChannelParentIdSafe(channel);
|
||||
const channelInfo = channelId
|
||||
? await resolveDiscordChannelInfo(interaction.client, channelId)
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user