UNPKG

discord-ws-gateway

Version:

Simple and tiny implementation of Discord's Gateway using web standard APIs

160 lines (158 loc) 4.08 kB
// package.json var version = "0.1.1"; var homepage = "https://github.com/Chooks22/discord-ws-gateway"; // src/index.ts var USER_AGENT = `Discord-Gateway/${version} (+${homepage})`; function createClient(gatewayUrl, identity) { return new Client(gatewayUrl, identity); } var Client = class { static create = createClient; #listeners = /* @__PURE__ */ new Map(); #interval; #timeout; socket; #s = null; #zombied = false; #url; #resume_url; #session_id; #identity; closed = false; constructor(gatewayUrl, identity) { this.#url = gatewayUrl; this.#identity = identity; void this.#connect(gatewayUrl); } #connect(url) { this.socket = new WebSocket(`${url}?v=10&encoding=json`); this.socket.addEventListener("message", (e) => { const data = JSON.parse(e.data); switch (data.op) { // Dispatch case 0: { this.#s = data.s; const listeners = this.#listeners.get(data.t); const listeners_all = this.#listeners.get("ALL"); if (listeners !== void 0) { listeners.forEach((cb) => cb(data)); } if (listeners_all !== void 0) { listeners_all.forEach((cb) => cb(data)); } if (data.t === "READY") { this.#session_id = data.d.session_id; this.#resume_url = data.d.resume_gateway_url; } break; } // Hello case 10: { const interval = data.d.heartbeat_interval; const heartbeat = () => this.#heartbeat(); this.#timeout = setTimeout(() => { heartbeat(); this.#interval = setInterval(heartbeat, interval); }, interval * Math.random()); this.#send({ op: 2, d: { ...this.#identity, properties: { os: this.#identity.properties.os, browser: `${this.#identity.properties.browser} ${USER_AGENT}`.trim(), device: `${this.#identity.properties.device} ${USER_AGENT}`.trim() } } }); break; } // Heartbeat Ack case 11: { this.#zombied = false; break; } // Heartbeat case 1: { this.#zombied = false; this.#heartbeat(); break; } // Reconnect case 7: { void this.#resume(); break; } } }); return new Promise((res) => { this.socket.addEventListener("open", res, { once: true }); }); } #send(data) { const payload = JSON.stringify(data); this.socket.send(payload); } #heartbeat() { if (this.#zombied) { void this.#resume(); return; } this.#zombied = true; this.#send({ op: 1, d: this.#s }); } async #resume() { this.close(); await this.#connect(this.#resume_url); this.#send({ op: 6, d: { token: this.#identity.token, seq: this.#s, session_id: this.#session_id } }); } /** * @todo(Chooks22) handle reconnect cases */ #reconnect() { this.close(); void this.#connect(this.#url); } addEventListener(event, callback) { const listeners = this.#listeners.get(event); if (listeners !== void 0) { listeners.push(callback); } else { this.#listeners.set(event, [callback]); } return this; } removeEventListener(event, callback) { const listeners = this.#listeners.get(event); if (listeners !== void 0) { if (listeners.length === 1 && listeners[0] === callback) { this.#listeners.delete(event); } else { for (let i = listeners.length; i--; ) { if (listeners[i] === callback) { listeners.splice(i, 1); break; } } } } return this; } close(code = 1e3) { this.closed = true; clearTimeout(this.#timeout); clearInterval(this.#interval); this.socket.close(code); } }; export { Client, createClient };