UNPKG

rx-discord-client

Version:

Reactive client for receiving discord server messages

104 lines (92 loc) 3.65 kB
import {RawData, WebSocket} from 'ws'; import {Observable, Subscriber} from 'rxjs'; export class DiscordClient extends Observable<{ type: string, data: any }> { private ws: WebSocket | undefined; private pingInterval: NodeJS.Timer | undefined; private sequence: number | null = null; private constructor( private token: string, private reinitializeOnClose = true ) { super((observer: Subscriber<{ type: string, operation: number, data: any }>) => { this.init(observer); const pingInterval = this.pingInterval; const ws = this.ws; return { unsubscribe(): void { ws?.close(); if (pingInterval) { clearInterval(pingInterval); } } }; }); } public static create(token: string): Observable<{ type: string, data: any }> { return new this(token); } private parseMessage(message: RawData): { type: string, operation: number, data: any, sequence: number } { const parsed = JSON.parse(message.toString()); const type = parsed.t; const operation = parsed.op; const data = parsed.d; const sequence = parsed.s; return {type, operation, data, sequence}; } private handleWebsocketMessage(observer: Subscriber<{ type: string, data: any }>, msg: RawData): void { const {type, operation, data, sequence} = this.parseMessage(msg); this.sequence = sequence; switch (operation) { case 10: // Initial message received this.pingInterval = setInterval(() => { this.ws?.send(JSON.stringify({op: 1, d: this.sequence})) }, data.heartbeat_interval); break; case 1: // Ping requested this.ws?.send(JSON.stringify({op: 1, d: this.sequence})); break; case 0: observer.next({type, data}); break; } } private initializeConnection(): void { this.ws?.send(JSON.stringify({ op: 2, d: { token: this.token, properties: { $os: 'linux', $browser: 'chrome', $device: 'chrome', } } })); } private handleWebsocketClose(observer: Subscriber<{ type: string, operation: number, data: any }>, e: any): void { console.log(`[${new Date().toISOString()}] `, `Connection closed, recreating connection [${e.toString()}]`); if (this.reinitializeOnClose) { this.init(observer); } else { observer.complete(); } } private handleWebsocketError(e: Error): void { console.log(`[${new Date().toISOString()}] `, `Connection error, closing connection [${e.toString()}]`); this.ws?.close(); } private init(observer: Subscriber<{ type: string, operation: number, data: any }>): void { if (!this.token) { const error = 'Token is not set'; observer.error(error); return; } this.ws = new WebSocket('wss://gateway.discord.gg/?v=6&encoding=json'); this.ws.on('open', () => this.initializeConnection()); this.ws.on('message', (m) => this.handleWebsocketMessage(observer, m)); this.ws.on('error', (e) => this.handleWebsocketError(e)); this.ws.on('close', (e) => this.handleWebsocketClose(observer, e)); } }