mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 16:51:13 +00:00
fix(discord): proxy Carbon REST, webhook and monitor fetch paths; stagger multi-bot startup
This commit is contained in:
committed by
Peter Steinberger
parent
dfb423532b
commit
c8223606ca
@@ -18,6 +18,7 @@ import {
|
||||
resolveOutboundSendDep,
|
||||
} from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import { normalizeMessageChannel } from "openclaw/plugin-sdk/routing";
|
||||
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
||||
import {
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
@@ -86,6 +87,19 @@ async function loadDiscordProbeRuntime() {
|
||||
|
||||
const meta = getChatChannelMeta("discord");
|
||||
const REQUIRED_DISCORD_PERMISSIONS = ["ViewChannel", "SendMessages"] as const;
|
||||
const DISCORD_ACCOUNT_STARTUP_STAGGER_MS = 10_000;
|
||||
|
||||
function resolveDiscordStartupDelayMs(cfg: OpenClawConfig, accountId: string): number {
|
||||
const startupAccountIds = listDiscordAccountIds(cfg).filter((candidateId) => {
|
||||
const candidate = resolveDiscordAccount({ cfg, accountId: candidateId });
|
||||
return (
|
||||
candidate.enabled &&
|
||||
(resolveConfiguredFromCredentialStatuses(candidate) ?? Boolean(candidate.token.trim()))
|
||||
);
|
||||
});
|
||||
const startupIndex = startupAccountIds.findIndex((candidateId) => candidateId === accountId);
|
||||
return startupIndex <= 0 ? 0 : startupIndex * DISCORD_ACCOUNT_STARTUP_STAGGER_MS;
|
||||
}
|
||||
|
||||
const resolveDiscordDmPolicy = createScopedDmSecurityResolver<ResolvedDiscordAccount>({
|
||||
channelKey: "discord",
|
||||
@@ -561,6 +575,17 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
const startupDelayMs = resolveDiscordStartupDelayMs(ctx.cfg, account.accountId);
|
||||
if (startupDelayMs > 0) {
|
||||
ctx.log?.info(
|
||||
`[${account.accountId}] delaying provider startup ${Math.round(startupDelayMs / 1000)}s to reduce Discord startup rate limits`,
|
||||
);
|
||||
try {
|
||||
await sleepWithAbort(startupDelayMs, ctx.abortSignal);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const token = account.token.trim();
|
||||
let discordBotLabel = "";
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { RequestClient } from "@buape/carbon";
|
||||
import { loadConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { makeProxyFetch } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import type { RetryConfig, RetryRunner } from "openclaw/plugin-sdk/retry-runtime";
|
||||
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
@@ -29,8 +30,53 @@ function resolveToken(params: { accountId: string; fallbackToken?: string }) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function resolveRest(token: string, rest?: RequestClient) {
|
||||
return rest ?? new RequestClient(token);
|
||||
function resolveDiscordProxyUrl(
|
||||
account: Pick<ResolvedDiscordAccount, "config">,
|
||||
cfg?: ReturnType<typeof loadConfig>,
|
||||
): string | undefined {
|
||||
const accountProxy = account.config.proxy?.trim();
|
||||
if (accountProxy) {
|
||||
return accountProxy;
|
||||
}
|
||||
const channelProxy = cfg?.channels?.discord?.proxy;
|
||||
if (typeof channelProxy !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = channelProxy.trim();
|
||||
return trimmed || undefined;
|
||||
}
|
||||
|
||||
export function resolveDiscordProxyFetchForAccount(
|
||||
account: Pick<ResolvedDiscordAccount, "config">,
|
||||
cfg?: ReturnType<typeof loadConfig>,
|
||||
): typeof fetch | undefined {
|
||||
const proxy = resolveDiscordProxyUrl(account, cfg);
|
||||
return proxy ? makeProxyFetch(proxy) : undefined;
|
||||
}
|
||||
|
||||
export function resolveDiscordProxyFetch(
|
||||
opts: Pick<DiscordClientOpts, "cfg" | "accountId">,
|
||||
cfg?: ReturnType<typeof loadConfig>,
|
||||
): typeof fetch | undefined {
|
||||
const resolvedCfg = opts.cfg ?? cfg ?? loadConfig();
|
||||
const account = resolveAccountWithoutToken({
|
||||
cfg: resolvedCfg,
|
||||
accountId: opts.accountId,
|
||||
});
|
||||
return resolveDiscordProxyFetchForAccount(account, resolvedCfg);
|
||||
}
|
||||
|
||||
function resolveRest(
|
||||
token: string,
|
||||
account: ResolvedDiscordAccount,
|
||||
cfg: ReturnType<typeof loadConfig>,
|
||||
rest?: RequestClient,
|
||||
) {
|
||||
if (rest) {
|
||||
return rest;
|
||||
}
|
||||
const proxyFetch = resolveDiscordProxyFetchForAccount(account, cfg);
|
||||
return new RequestClient(token, proxyFetch ? { fetch: proxyFetch } : undefined);
|
||||
}
|
||||
|
||||
function resolveAccountWithoutToken(params: {
|
||||
@@ -66,7 +112,7 @@ export function createDiscordRestClient(
|
||||
accountId: account.accountId,
|
||||
fallbackToken: account.token,
|
||||
});
|
||||
const rest = resolveRest(token, opts.rest);
|
||||
const rest = resolveRest(token, account, resolvedCfg, opts.rest);
|
||||
return { token, rest, account };
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ export function createDiscordMonitorClient(params: {
|
||||
accountId: string;
|
||||
applicationId: string;
|
||||
token: string;
|
||||
proxyFetch?: typeof fetch;
|
||||
commands: BaseCommand[];
|
||||
components: BaseMessageInteractiveComponent[];
|
||||
modals: Modal[];
|
||||
@@ -115,6 +116,7 @@ export function createDiscordMonitorClient(params: {
|
||||
token: params.token,
|
||||
autoDeploy: false,
|
||||
eventQueue: eventQueueOpts,
|
||||
...(params.proxyFetch ? { requestOptions: { fetch: params.proxyFetch } } : {}),
|
||||
},
|
||||
{
|
||||
commands: params.commands,
|
||||
|
||||
@@ -42,6 +42,7 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { summarizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { resolveDiscordAccount } from "../accounts.js";
|
||||
import { isDiscordExecApprovalClientEnabled } from "../exec-approvals.js";
|
||||
import { resolveDiscordProxyFetchForAccount } from "../client.js";
|
||||
import { fetchDiscordApplicationId } from "../probe.js";
|
||||
import { normalizeDiscordToken } from "../token.js";
|
||||
import { createDiscordVoiceCommand } from "../voice/command.js";
|
||||
@@ -592,6 +593,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
const discordAccountThreadBindings =
|
||||
cfg.channels?.discord?.accounts?.[account.accountId]?.threadBindings;
|
||||
const discordRestFetch = resolveDiscordRestFetch(rawDiscordCfg.proxy, runtime);
|
||||
const discordProxyFetch = resolveDiscordProxyFetchForAccount(account, cfg);
|
||||
const dmConfig = rawDiscordCfg.dm;
|
||||
let guildEntries = rawDiscordCfg.guilds;
|
||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||
@@ -903,6 +905,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
accountId: account.accountId,
|
||||
applicationId,
|
||||
token,
|
||||
proxyFetch: discordProxyFetch,
|
||||
commands,
|
||||
components,
|
||||
modals,
|
||||
|
||||
@@ -16,6 +16,7 @@ import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
||||
import { convertMarkdownTables } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
|
||||
import { resolveDiscordAccount } from "./accounts.js";
|
||||
import { resolveDiscordProxyFetch } from "./client.js";
|
||||
import { rewriteDiscordKnownMentions } from "./mentions.js";
|
||||
import {
|
||||
buildDiscordMessagePayload,
|
||||
@@ -369,8 +370,9 @@ export async function sendWebhookMessageDiscord(
|
||||
});
|
||||
const replyTo = typeof opts.replyTo === "string" ? opts.replyTo.trim() : "";
|
||||
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
||||
const fetchImpl = resolveDiscordProxyFetch({ cfg: opts.cfg, accountId: opts.accountId });
|
||||
|
||||
const response = await fetch(
|
||||
const response = await (fetchImpl ?? fetch)(
|
||||
resolveWebhookExecutionUrl({
|
||||
webhookId,
|
||||
webhookToken,
|
||||
|
||||
Reference in New Issue
Block a user