Files
openclaw/src/discord/monitor/gateway-plugin.ts
Peter Steinberger 97e56cb73c fix(discord): land proxy/media/reaction/model-picker regressions
Reimplements core Discord fixes from #25277 #25523 #25575 #25588 #25731 with expanded tests.

- thread proxy-aware fetch into inbound attachment/sticker downloads
- fetch /gateway/bot via proxy dispatcher before ws connect
- wire statusReactions emojis/timing overrides into controller
- compact model-picker custom_id keys with backward-compatible parsing

Co-authored-by: openperf <openperf@users.noreply.github.com>
Co-authored-by: chilu18 <chilu18@users.noreply.github.com>
Co-authored-by: Yipsh <Yipsh@users.noreply.github.com>
Co-authored-by: lbo728 <lbo728@users.noreply.github.com>
Co-authored-by: s1korrrr <s1korrrr@users.noreply.github.com>
2026-02-25 00:03:30 +00:00

88 lines
2.8 KiB
TypeScript

import { GatewayIntents, GatewayPlugin } from "@buape/carbon/gateway";
import type { APIGatewayBotInfo } from "discord-api-types/v10";
import { HttpsProxyAgent } from "https-proxy-agent";
import { ProxyAgent, fetch as undiciFetch } from "undici";
import WebSocket from "ws";
import type { DiscordAccountConfig } from "../../config/types.js";
import { danger } from "../../globals.js";
import type { RuntimeEnv } from "../../runtime.js";
export function resolveDiscordGatewayIntents(
intentsConfig?: import("../../config/types.discord.js").DiscordIntentsConfig,
): number {
let intents =
GatewayIntents.Guilds |
GatewayIntents.GuildMessages |
GatewayIntents.MessageContent |
GatewayIntents.DirectMessages |
GatewayIntents.GuildMessageReactions |
GatewayIntents.DirectMessageReactions |
GatewayIntents.GuildVoiceStates;
if (intentsConfig?.presence) {
intents |= GatewayIntents.GuildPresences;
}
if (intentsConfig?.guildMembers) {
intents |= GatewayIntents.GuildMembers;
}
return intents;
}
export function createDiscordGatewayPlugin(params: {
discordConfig: DiscordAccountConfig;
runtime: RuntimeEnv;
}): GatewayPlugin {
const intents = resolveDiscordGatewayIntents(params.discordConfig?.intents);
const proxy = params.discordConfig?.proxy?.trim();
const options = {
reconnect: { maxAttempts: 50 },
intents,
autoInteractions: true,
};
if (!proxy) {
return new GatewayPlugin(options);
}
try {
const wsAgent = new HttpsProxyAgent<string>(proxy);
const fetchAgent = new ProxyAgent(proxy);
params.runtime.log?.("discord: gateway proxy enabled");
class ProxyGatewayPlugin extends GatewayPlugin {
constructor() {
super(options);
}
override async registerClient(client: Parameters<GatewayPlugin["registerClient"]>[0]) {
if (!this.gatewayInfo) {
try {
const response = await undiciFetch("https://discord.com/api/v10/gateway/bot", {
headers: {
Authorization: `Bot ${client.options.token}`,
},
dispatcher: fetchAgent,
} as Record<string, unknown>);
this.gatewayInfo = (await response.json()) as APIGatewayBotInfo;
} catch (error) {
throw new Error(
`Failed to get gateway information from Discord: ${error instanceof Error ? error.message : String(error)}`,
{ cause: error },
);
}
}
return super.registerClient(client);
}
override createWebSocket(url: string) {
return new WebSocket(url, { agent: wsAgent });
}
}
return new ProxyGatewayPlugin();
} catch (err) {
params.runtime.error?.(danger(`discord: invalid gateway proxy: ${String(err)}`));
return new GatewayPlugin(options);
}
}