mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
all my fixes
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const handleInternalReady = () => {
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 ()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user