Cloudflare Workers中Discord Bot无法连接网关更新状态问题
Cloudflare Workers中Discord网关WebSocket连接失败问题解决
问题描述
我有一个部署在Cloudflare Workers上的Discord Bot,想要通过Discord网关更新在线状态。以下代码在浏览器控制台中可以正常运行,但在Workers环境下完全失效:
export function setPresence(token: string) { return new Promise((resolve, reject) => { const activities = [{ name: `you`, type: 3 }]; const socket = new WebSocket("wss://gateway.discord.gg/?v=10&encoding=json"); function send(op, d) { return socket.send(JSON.stringify({ op, d })); } socket.addEventListener("error", e => console.error(e) || reject(e)); socket.addEventListener("open", _ => console.log("websocket open")); socket.addEventListener("close", _ => console.log("websocket closed") || resolve()); socket.addEventListener("message", event => { const data = JSON.parse(event.data); const { op, d } = data; console.log({ data }); switch (op) { case 1: // Heartbeat case 10: { // Hello send(1, null); break; }; case 11: { // Heartbeat ACK send(2, { token, properties: { os: "linux", browser: "cloudflare workers", device: "cloudflare workers" }, intents: 0 }); break; } case 0: { // Ready console.log("websocket ready"); send(3, { activities: [activities[Math.floor(activities.length * Math.random())]], status: "online", since: 91879201, afk: false }); setTimeout(_ => socket.close(), 10000); } } }); }); }
运行差异:
- 浏览器环境:WebSocket能正常建立连接,完成身份验证并更新状态(浏览器正常运行截图)
- Workers环境(
wrangler dev --remote):无"websocket open"日志,仅触发错误事件和"websocket closed"日志(Workers运行报错截图) - 尝试通过
fetch("https://gateway.discord.gg/?v=10&encoding=json")获取响应的webSocket属性,结果始终为null
问题原因
Cloudflare Workers的WebSocket实现与浏览器存在核心差异:
- 连接创建方式限制:Workers不支持直接通过
new WebSocket()创建客户端连接,必须通过fetchAPI的webSocket属性初始化连接,这是环境强制要求。 - Discord网关协议适配:原代码中Hello包(op=10)的心跳响应参数错误,不符合Discord网关规范,会导致连接被主动断开。
解决方案
修改代码,适配Workers的WebSocket规则并修正协议逻辑:
export async function setPresence(token: string) { const activities = [{ name: `you`, type: 3 }]; // 通过fetch初始化WebSocket连接 const response = await fetch("wss://gateway.discord.gg/?v=10&encoding=json", { method: "GET", headers: { "Upgrade": "websocket", "Connection": "Upgrade" } }); if (!response.webSocket) { throw new Error("无法从fetch响应获取WebSocket连接"); } const socket = response.webSocket; socket.accept(); // 必须调用accept()确认连接 function send(op: number, d: any) { socket.send(JSON.stringify({ op, d })); } return new Promise<void>((resolve, reject) => { socket.addEventListener("error", (e) => { console.error(e); reject(e); }); socket.addEventListener("open", () => { console.log("websocket open"); }); socket.addEventListener("close", () => { console.log("websocket closed"); resolve(); }); socket.addEventListener("message", (event) => { const data = JSON.parse(event.data); const { op, d } = data; console.log({ data }); switch (op) { case 1: // Heartbeat case 10: { // Hello // 发送心跳时携带Hello包返回的心跳间隔参数 send(1, d?.heartbeat_interval || null); break; } case 11: { // Heartbeat ACK send(2, { token, properties: { os: "linux", browser: "cloudflare workers", device: "cloudflare workers" }, intents: 0 }); break; } case 0: { // Ready console.log("websocket ready"); send(3, { activities: [activities[Math.floor(activities.length * Math.random())]], status: "online", since: 91879201, afk: false }); setTimeout(() => socket.close(), 10000); break; } } }); }); }
关键修改说明
- fetch创建WebSocket:Workers环境要求必须通过指定
Upgrade和Connection头的fetch请求建立WebSocket连接,不能直接实例化WebSocket对象。 - 调用socket.accept():获取到WebSocket对象后必须调用此方法,否则连接会被立即关闭。
- 修正心跳参数:Hello包返回的
heartbeat_interval需要作为心跳包(op=1)的参数发送,原代码传null不符合Discord网关协议,会导致连接异常断开。
内容的提问来源于stack exchange,提问作者code913
相关产品推荐
相关产品推荐

