all my fixes

This commit is contained in:
Skillz
2020-02-26 13:56:52 -05:00
parent a7da41f835
commit bf271cfa43
8 changed files with 168 additions and 141 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ Discord API library wrapper in Deno
## TODO
- [Review compression of payloads with GZIP](https://discordapp.com/developers/docs/topics/gateway#sending-payloads-example-gateway-dispatch)
-
- Handle checking if guild is unavailable before taking guild related actions.
## Motivations/Features
+3
View File
@@ -0,0 +1,3 @@
export const handleInternalReady = () => {
}
+4 -32
View File
@@ -1,41 +1,13 @@
import Client from "./module/client.ts"
import { configs } from "./configs.ts"
import { StatusType, GatewayOpcode } from "./types/discord.ts"
import { Intents } from "./types/options.ts"
const startup = async () => {
const client = new Client({
new Client({
token: configs.token,
bot_id: '675412054529540107'
bot_id: "675412054529540107",
intents: [Intents.GUILDS, Intents.GUILD_MESSAGES]
})
const { gateway, connection } = await client.bootstrap()
for await (const message of connection) {
if (message.data?.op === GatewayOpcode.Hello) {
await message.action
gateway.updateStatus({
afk: false,
status: StatusType.DoNotDisturb
})
}
}
}
startup()
// ;(async function() {
// const client = new Client({
// token: configs.token
// })
// const { gateway, connection } = await client.bootstrap()
// for await (const message of connection) {
// if (message.data?.op === GatewayOpcode.Hello) {
// await message.action
// await gateway.updateStatus({
// afk: false,
// status: StatusType.DoNotDisturb
// })
// }
// }
// })()
+57 -54
View File
@@ -1,16 +1,26 @@
import { endpoints } from "../constants/discord.ts"
import DiscordRequestManager from "./discord-request-manager.ts"
import { DiscordBotGatewayData, DiscordPayload, DiscordHeartbeatPayload, GatewayOpcode } from "../types/discord.ts"
import ShardingManager from "./sharding-manager.ts"
import { DiscordBotGatewayData, DiscordPayload, DiscordHeartbeatPayload, GatewayOpcode, StatusType } from "../types/discord.ts"
import { spawnShards } from "./sharding-manager.ts"
import {
connectWebSocket,
isWebSocketCloseEvent,
isWebSocketPingEvent,
isWebSocketPongEvent
isWebSocketPongEvent,
WebSocket
} from "https://deno.land/std/ws/mod.ts"
import Gateway from "./gateway.ts"
import { ClientOptions, FulfilledClientOptions } from "../types/options.ts"
import { CollectedMessageType } from "../types/message-type.ts"
import { sendConstantHeartbeats } from "./gateway.ts"
const defaultOptions = {
properties: {
$os: "linux",
$browser: "Discordeno",
$device: "Discordeno"
},
compress: false
}
class Client {
bot_id: string
@@ -18,8 +28,6 @@ class Client {
token: string
/** The Rate limit manager to handle all outgoing requests to discord. Not meant to be used by users. */
discordRequestManager: DiscordRequestManager
/** Creates and handles all the shards necessary for the bot. */
shardingManager: ShardingManager
/** The options (with defaults) passed to the `Client` constructor. */
options: FulfilledClientOptions
@@ -28,51 +36,53 @@ class Client {
constructor(options: ClientOptions) {
// Assign some defaults to the options to make them fulfilled / not annoying to use.
this.options = Object.assign(
{
properties: {
$os: "...",
$browser: "...",
$device: "..."
},
compress: false
},
options
)
this.options = {
...defaultOptions,
...options,
intents: options.intents.reduce((bits, next) => (bits |= next), 0)
}
this.bot_id = options.bot_id
this.token = options.token
this.authorization = `Bot ${this.options.token}`
this.discordRequestManager = new DiscordRequestManager(this, this.authorization)
this.shardingManager = new ShardingManager()
}
getGatewayData() {
return this.discordRequestManager.get(endpoints.GATEWAY_BOT) as Promise<DiscordBotGatewayData>
}
createWebsocketConnection(data: DiscordBotGatewayData) {
console.log({ data })
return connectWebSocket(data.url)
this.bootstrap()
}
async bootstrap() {
const data = await this.getGatewayData()
const socket = await this.createWebsocketConnection(data)
const gateway = new Gateway(socket)
const messages = this.collectMessages(gateway)
await gateway.identify(this.options)
return {
data,
socket,
gateway,
messages,
connection: this.connect(gateway, data)
const data = await this.discordRequestManager.get(endpoints.GATEWAY_BOT) as DiscordBotGatewayData
const socket = await connectWebSocket(data.url)
this.collectMessages(socket)
// Intial identify with the gateway
await socket.send(
JSON.stringify({
op: GatewayOpcode.Identify,
d: {
token: this.options.token,
// TODO: Let's get compression working, eh?
compress: false,
properties: this.options.properties,
intents: this.options.intents
}
})
)
for await (const message of this.connect(socket, data)) {
console.log("mymessage", message)
if (message.data?.op === GatewayOpcode.Hello) {
await message.action
}
if (message.data?.t === 'READY') {
console.log('ready event was received')
// this.options.eventHandlers.ready()
}
}
}
async *collectMessages(gateway: Gateway) {
const { socket } = gateway
async *collectMessages(socket: WebSocket) {
for await (const message of socket.receive()) {
console.log("collecting", message)
if (typeof message === "string") {
yield {
type: CollectedMessageType.Message,
@@ -91,10 +101,10 @@ class Client {
/** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */
async *connect(
gateway: Gateway,
socket: WebSocket,
data: DiscordBotGatewayData
): AsyncGenerator<{ type: CollectedMessageType; data?: DiscordPayload; action?: Promise<void> }> {
for await (const message of this.collectMessages(gateway)) {
for await (const message of this.collectMessages(socket)) {
switch (message.type) {
case CollectedMessageType.Ping:
console.log("Ping!")
@@ -109,32 +119,25 @@ class Client {
yield message
break
case CollectedMessageType.Message:
await this.handleDiscordPayload(message.data, gateway)
this.handleDiscordPayload(message.data, socket)
yield message
console.log({ yay: true, ...message })
break
}
}
// Begin spawning all necessary shards
this.spawnShards(data.shards)
spawnShards(data.shards)
}
handleDiscordPayload(data: DiscordPayload, gateway: Gateway) {
handleDiscordPayload(data: DiscordPayload, socket: WebSocket) {
switch (data.op) {
case GatewayOpcode.Hello:
console.log("heartbeating...")
return gateway.sendConstantHeartbeats((data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s)
sendConstantHeartbeats(socket, (data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s)
return
}
// Make all code paths return a promise for consistency.
return Promise.resolve(undefined)
}
spawnShards(total: number, id = 1) {
// this.ShardingManager.spawnShard(id);
if (id < total) this.spawnShards(total, id + 1)
}
}
export default Client
+7 -48
View File
@@ -1,54 +1,13 @@
import { WebSocket } from "https://deno.land/std/ws/mod.ts"
import { GatewayOpcode, Status } from "../types/discord.ts"
import { FulfilledClientOptions } from "../types/options.ts"
import { GatewayOpcode } from "../types/discord.ts"
import { delay } from "https://deno.land/std/util/async.ts"
export default class Gateway {
constructor(public socket: WebSocket) {}
export const sendConstantHeartbeats = async (socket: WebSocket, interval: number, previousSequenceNumber: number | null = null) => {
await delay(interval)
identify(options: FulfilledClientOptions) {
return this.sendObject({
op: GatewayOpcode.Identify,
d: {
token: options.token,
// TOOD: Let's get compression working, eh?
compress: false,
properties: options.properties
}
})
}
if (previousSequenceNumber) previousSequenceNumber += 1
sendHeartbeat(previousSequenceNumber: number | null = null) {
return this.sendObject({
op: GatewayOpcode.Heartbeat,
d: previousSequenceNumber
})
}
updateStatus(status: Status) {
this.sendObject({
op: GatewayOpcode.StatusUpdate,
d: status
})
}
async sendConstantHeartbeats(
interval: number,
previousSequenceNumber: number | null = null,
shouldContinue: () => boolean = () => true
): Promise<void> {
await delay(interval)
if (!shouldContinue()) {
return
}
// TODO: If the initial seq num is null, this will make it forever null until a restart. Is this good?
this.sendHeartbeat(previousSequenceNumber === null ? previousSequenceNumber : previousSequenceNumber++)
return this.sendConstantHeartbeats(interval, previousSequenceNumber)
}
sendObject(object: object) {
return this.socket.send(JSON.stringify(object))
}
// TODO: If the initial seq num is null, this will make it forever null until a restart. Is this good?
socket.send(JSON.stringify({ op: GatewayOpcode.Heartbeat, d: previousSequenceNumber }))
sendConstantHeartbeats(socket, interval, previousSequenceNumber)
}
+4 -3
View File
@@ -1,3 +1,4 @@
class ShardingManager extends Map {}
export default ShardingManager
export const spawnShards = (total: number, id = 1) => {
// this.ShardingManager.spawnShard(id);
if (id < total) spawnShards(total, id + 1)
}
+90 -1
View File
@@ -3,7 +3,8 @@ import { Properties } from "./discord.ts"
export interface FulfilledClientOptions {
token: string
properties: Properties
compress: boolean
compress: boolean,
intents: number
}
export interface ClientOptions {
@@ -11,4 +12,92 @@ export interface ClientOptions {
properties?: Properties
compress?: boolean
bot_id: string
intents: Intents[]
}
export enum Intents {
/** Enables the following events:
* - GUILD_CREATE
* - GUILD_DELETE
* - GUILD_ROLE_CREATE
* - GUILD_ROLE_UPDATE
* - GUILD_ROLE_DELETE
* - CHANNEL_CREATE
* - CHANNEL_UPDATE
* - CHANNEL_DELETE
* - CHANNEL_PINS_UPDATE
*/
GUILDS = 1 << 0,
/** Enables the following events:
* - GUILD_MEMBER_ADD
* - GUILD_MEMBER_UPDATE
* - GUILD_MEMBER_REMOVE
*/
GUILD_MEMBERS = 1 << 1,
/** Enables the following events:
* - GUILD_BAN_ADD
* - GUILD_BAN_REMOVE
*/
GUILD_BANS = 1 << 2,
/** Enables the following events:
* - GUILD_EMOJIS_UPDATE
*/
GUILD_EMOJIS = 1 << 3,
/** Enables the following events:
* - GUILD_INTEGRATIONS_UPDATE
*/
GUILD_INTEGRATIONS = 1 << 4,
/** Enables the following events:
* - WEBHOOKS_UPDATE
*/
GUILD_WEBHOOKS = 1 << 5,
/** Enables the following events:
* - INVITE_CREATE
* - INVITE_DELETE
*/
GUILD_INVITES = 1 << 6,
/** Enables the following events:
* - VOICE_STATE_UPDATE
*/
GUILD_VOICE_STATES = 1 << 7,
/** Enables the following events:
* - PRESENCE_UPDATE
*/
GUILD_PRESENCES = 1 << 8,
/** Enables the following events:
* - MESSAGE_CREATE
* - MESSAGE_UPDATE
* - MESSAGE_DELETE
*/
GUILD_MESSAGES = 1 << 9,
/** Enables the following events:
* - MESSAGE_REACTION_ADD
* - MESSAGE_REACTION_REMOVE
* - MESSAGE_REACTION_REMOVE_ALL
* - MESSAGE_REACTION_REMOVE_EMOJI
*/
GUILD_MESSAGE_REACTIONS = 1 << 10,
/** Enables the following events:
* - TYPING_START
*/
GUILD_MESSAGE_TYPING = 1 << 11,
/** Enables the following events:
* - CHANNEL_CREATE
* - MESSAGE_CREATE
* - MESSAGE_UPDATE
* - MESSAGE_DELETE
* - CHANNEL_PINS_UPDATE
*/
DIRECT_MESSAGES = 1 << 12,
/** Enables the following events:
* - MESSAGE_REACTION_ADD
* - MESSAGE_REACTION_REMOVE
* - MESSAGE_REACTION_REMOVE_ALL
* - MESSAGE_REACTION_REMOVE_EMOJI
*/
DIRECT_MESSAGE_REACTIONS = 1 << 13,
/** Enables the following events:
* - TYPING_START
*/
DIRECT_MESSAGE_TYPING = 1 << 14
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { DiscordPayload } from "./discord";
import { DiscordPayload } from "./discord.ts";
import Gateway from "../module/gateway.ts";
export abstract class ActionQueue<Action> {
@@ -35,4 +35,4 @@ export class GatewayActionQueue extends ActionQueue<DiscordPayload> {
}
shouldDispatchImmediately ()
}
}