mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
fix: harden discord agent cid parsing (#29013) (thanks @Jacky1n7)
This commit is contained in:
@@ -111,6 +111,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Discord/Agent component interactions: accept Components v2 `cid` payloads alongside legacy `componentId`, and safely decode percent-encoded IDs without throwing on malformed `%` sequences. Landed from contributor PR #29013 by @Jacky1n7. Thanks @Jacky1n7.
|
||||
- Discord/Inbound media fallback: preserve attachment and sticker metadata when Discord CDN fetch/save fails by keeping URL-based media entries in context, with regression coverage for save failures and mixed success/failure ordering. Landed from contributor PR #28906 by @Sid-Qin. Thanks @Sid-Qin.
|
||||
- Docs/Docker images: clarify the official GHCR image source and tag guidance (`main`, `latest`, `<version>`), and document that `OPENCLAW_IMAGE` skips local image builds but still uses the repo-local compose/setup flow. (#27214, #31180) Fixes #15655. Thanks @ipl31.
|
||||
- Agents/Model fallback: classify additional network transport errors (`ECONNREFUSED`, `ENETUNREACH`, `EHOSTUNREACH`, `ENETRESET`, `EAI_AGAIN`) as failover-worthy so fallback chains advance when primary providers are unreachable. Landed from contributor PR #19077 by @ayanesakura. Thanks @ayanesakura.
|
||||
|
||||
@@ -406,22 +406,21 @@ export function buildAgentSelectCustomId(componentId: string): string {
|
||||
|
||||
/**
|
||||
* Parse agent component data from Carbon's parsed ComponentData
|
||||
* Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
|
||||
* Supports both legacy { componentId } and Components v2 { cid } payloads.
|
||||
*/
|
||||
function readParsedComponentId(data: ComponentData): unknown {
|
||||
if (!data || typeof data !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
return "cid" in data
|
||||
? (data as Record<string, unknown>).cid
|
||||
: (data as Record<string, unknown>).componentId;
|
||||
}
|
||||
|
||||
function parseAgentComponentData(data: ComponentData): {
|
||||
componentId: string;
|
||||
} | null {
|
||||
if (!data || typeof data !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
|
||||
// Components v2 / other builders may use { cid: "xxx" } (e.g. occomp:cid=xxx).
|
||||
const raw =
|
||||
("cid" in data
|
||||
? (data as Record<string, unknown>).cid
|
||||
: (data as Record<string, unknown>).componentId) ??
|
||||
(data as Record<string, unknown>).componentId;
|
||||
const raw = readParsedComponentId(data);
|
||||
|
||||
const decodeSafe = (value: string): string => {
|
||||
// `cid` values may be raw (not URI-encoded). Guard against malformed % sequences.
|
||||
@@ -601,10 +600,7 @@ function parseDiscordComponentData(
|
||||
if (!data || typeof data !== "object") {
|
||||
return null;
|
||||
}
|
||||
const rawComponentId =
|
||||
"cid" in data
|
||||
? (data as { cid?: unknown }).cid
|
||||
: (data as { componentId?: unknown }).componentId;
|
||||
const rawComponentId = readParsedComponentId(data);
|
||||
const rawModalId =
|
||||
"mid" in data ? (data as { mid?: unknown }).mid : (data as { modalId?: unknown }).modalId;
|
||||
let componentId = normalizeComponentId(rawComponentId);
|
||||
|
||||
@@ -182,6 +182,44 @@ describe("agent components", () => {
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓" });
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("accepts cid payloads for agent button interactions", async () => {
|
||||
const button = createAgentComponentButton({
|
||||
cfg: createCfg(),
|
||||
accountId: "default",
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["123456789"],
|
||||
});
|
||||
const { interaction, defer, reply } = createDmButtonInteraction();
|
||||
|
||||
await button.run(interaction, { cid: "hello_cid" } as ComponentData);
|
||||
|
||||
expect(defer).toHaveBeenCalledWith({ ephemeral: true });
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓" });
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("hello_cid"),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps malformed percent cid values without throwing", async () => {
|
||||
const button = createAgentComponentButton({
|
||||
cfg: createCfg(),
|
||||
accountId: "default",
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["123456789"],
|
||||
});
|
||||
const { interaction, defer, reply } = createDmButtonInteraction();
|
||||
|
||||
await button.run(interaction, { cid: "hello%2G" } as ComponentData);
|
||||
|
||||
expect(defer).toHaveBeenCalledWith({ ephemeral: true });
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓" });
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("hello%2G"),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("discord component interactions", () => {
|
||||
|
||||
Reference in New Issue
Block a user