mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-17 16:12:21 -06:00
e4ff7c1620
* fix: Discord read/search timeout, session-key fallback, and gateway execution mode - Add 15s timeout to readMessagesDiscord and searchMessagesDiscord so they fail fast instead of hanging indefinitely (#73431) - Fall back to CommandTargetSessionKey in dispatchReplyFromConfig when SessionKey is empty, so Discord inbound message:received hooks fire reliably (#73431, refs #33038) - Add resolveExecutionMode to Discord channel actions routing read/search through gateway timeout path, matching Telegram's pattern (#73431) * fix: move timeout to fetch layer, drop send.messages wrapper Inject AbortSignal.timeout into the Discord proxy-request-client fetch wrapper so every Discord REST call gets a 15s timeout at the HTTP level. This replaces the Promise.race wrapper in send.messages.ts — cleaner, covers all calls, and actually aborts the TCP connection. * fix: remove unused callerController variable in proxy-request-client test * fix: remove unnecessary mergeAbortSignal helper
55 lines
1.7 KiB
TypeScript
55 lines
1.7 KiB
TypeScript
import { RequestClient, type RequestClientOptions } from "@buape/carbon";
|
|
import { FormData as UndiciFormData } from "undici";
|
|
|
|
export type ProxyRequestClientOptions = RequestClientOptions;
|
|
|
|
export const DISCORD_REST_TIMEOUT_MS = 15_000;
|
|
|
|
function toUndiciFormData(body: FormData): UndiciFormData {
|
|
const converted = new UndiciFormData();
|
|
for (const [key, value] of body.entries()) {
|
|
if (typeof value === "string") {
|
|
converted.append(key, value);
|
|
continue;
|
|
}
|
|
const filename = (value as Blob & { name?: unknown }).name;
|
|
if (typeof filename === "string" && filename.length > 0) {
|
|
converted.append(key, value, filename);
|
|
continue;
|
|
}
|
|
converted.append(key, value);
|
|
}
|
|
return converted;
|
|
}
|
|
|
|
function wrapDiscordFetch(fetchImpl: NonNullable<RequestClientOptions["fetch"]>) {
|
|
return (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
const signal = AbortSignal.timeout(DISCORD_REST_TIMEOUT_MS);
|
|
if (init?.body instanceof FormData) {
|
|
// Carbon builds global FormData; undici-backed proxy fetch needs undici's
|
|
// FormData class to preserve multipart boundaries.
|
|
return fetchImpl(input, {
|
|
...init,
|
|
signal,
|
|
body: toUndiciFormData(init.body) as unknown as BodyInit,
|
|
});
|
|
}
|
|
return fetchImpl(input, { ...init, signal });
|
|
};
|
|
}
|
|
|
|
export function createDiscordRequestClient(
|
|
token: string,
|
|
options?: ProxyRequestClientOptions,
|
|
): RequestClient {
|
|
if (!options?.fetch) {
|
|
return new RequestClient(token, options);
|
|
}
|
|
return new RequestClient(token, {
|
|
runtimeProfile: "persistent",
|
|
maxQueueSize: 1000,
|
|
...options,
|
|
fetch: wrapDiscordFetch(options.fetch),
|
|
});
|
|
}
|