import { endpoints } from '../constants/discord.ts' import RequestManager from '../managers/RequestManager.ts' import { DiscordBotGateway, DiscordPayload, DiscordHeartbeatPayload } from '../types/discord.ts' import ShardingManager from '../managers/ShardingManager.ts' import { connectWebSocket, isWebSocketCloseEvent, isWebSocketPingEvent, isWebSocketPongEvent, WebSocket } from 'https://deno.land/std/ws/mod.ts' // import { encode } from "https://deno.land/std/strings/mod.ts" // import { BufReader } from "https://deno.land/std/io/bufio.ts" // import { TextProtoReader } from "https://deno.land/std/textproto/mod.ts" import { keepDiscordWebsocketAlive, updatePreviousSequenceNumber } from './websocket.ts' import { logGreen, logRed, logYellow, logBlue } from '../utils/logger.ts' class Client { /** The bot's token. This should never be used by end users. It is meant to be used internally to make requests to the Discord API. */ token: string /** The Rate limit manager to handle all outgoing requests to discord. Not meant to be used by users. */ RequestManager: RequestManager /** Creates and handles all the shards necessary for the bot. */ ShardingManager: ShardingManager constructor(token: string) { this.token = `Bot ${token}` this.RequestManager = new RequestManager(this, this.token) this.ShardingManager = new ShardingManager() } /** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */ async connect() { const data = (await this.RequestManager.get(endpoints.GATEWAY_BOT)) as DiscordBotGateway // Open a WS with the url from discord. const sock = await connectWebSocket(data.url) console.log(sock) logGreen("ws connected! (type 'close' to quit)") for await (const msg of sock.receive()) { if (typeof msg === 'string') { try { const json = JSON.parse(msg) this.handleDiscordPayload(json, sock) } catch { logRed(`Invalid JSON String send by discord: ${msg}`) } logYellow('< ' + msg) } else if (isWebSocketPingEvent(msg)) { logBlue('< ping') } else if (isWebSocketPongEvent(msg)) { logBlue('< pong') } else if (isWebSocketCloseEvent(msg)) { logRed(`closed: code=${msg.code}, reason=${msg.reason}`) } } // Begin spawning all necessary shards this.spawnShards(data.shards) } handleDiscordPayload(data: DiscordPayload, socket: WebSocket) { switch (data.op) { case 10: // Initial Heartbeat keepDiscordWebsocketAlive(socket, (data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s) break case 11: updatePreviousSequenceNumber(data.s) break } } spawnShards(total: number, id = 1) { // this.ShardingManager.spawnShard(id); if (id < total) this.spawnShards(total, id + 1) } } export default Client