even more fixes

This commit is contained in:
Skillz
2020-02-25 22:56:15 -05:00
parent 4d1e7818b0
commit bcf8c30021
9 changed files with 130 additions and 163 deletions
+38 -35
View File
@@ -1,16 +1,16 @@
import { endpoints } from '../constants/discord'
import DiscordRequestManager from '../managers/DiscordRequestManager.ts'
import { DiscordBotGatewayData, DiscordPayload, DiscordHeartbeatPayload, GatewayOpcode } from '../types/discord.ts'
import ShardingManager from '../managers/ShardingManager.ts'
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 {
connectWebSocket,
isWebSocketCloseEvent,
isWebSocketPingEvent,
isWebSocketPongEvent
} 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'
} 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"
class Client {
bot_id: string
@@ -31,9 +31,9 @@ class Client {
this.options = Object.assign(
{
properties: {
$os: '...',
$browser: '...',
$device: '...'
$os: "...",
$browser: "...",
$device: "..."
},
compress: false
},
@@ -57,15 +57,15 @@ class Client {
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);
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,
gateway,
messages,
connection: this.connect(gateway, data)
}
}
@@ -73,7 +73,7 @@ class Client {
async *collectMessages(gateway: Gateway) {
const { socket } = gateway
for await (const message of socket.receive()) {
if (typeof message === 'string') {
if (typeof message === "string") {
yield {
type: CollectedMessageType.Message,
data: JSON.parse(message)
@@ -90,25 +90,28 @@ class Client {
}
/** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */
async *connect(gateway: Gateway, data: DiscordBotGatewayData): AsyncGenerator<{ type: CollectedMessageType, data?: DiscordPayload, action?: Promise<void> }> {
async *connect(
gateway: Gateway,
data: DiscordBotGatewayData
): AsyncGenerator<{ type: CollectedMessageType; data?: DiscordPayload; action?: Promise<void> }> {
for await (const message of this.collectMessages(gateway)) {
switch (message.type) {
case CollectedMessageType.Ping:
console.log('Ping!')
yield message;
console.log("Ping!")
yield message
break
case CollectedMessageType.Pong:
console.log('Pong!')
yield message;
console.log("Pong!")
yield message
break
case CollectedMessageType.Close:
console.log('Close :(', message)
yield message;
console.log("Close :(", message)
yield message
break
case CollectedMessageType.Message:
await this.handleDiscordPayload(message.data, gateway);
yield message;
console.log({ yay: true, ...message });
await this.handleDiscordPayload(message.data, gateway)
yield message
console.log({ yay: true, ...message })
break
}
}
@@ -117,20 +120,20 @@ class Client {
this.spawnShards(data.shards)
}
handleDiscordPayload(data: DiscordPayload, gateway: Gateway) {
handleDiscordPayload(data: DiscordPayload, gateway: Gateway) {
switch (data.op) {
case GatewayOpcode.Hello:
console.log('heartbeating...');
return gateway.sendConstantHeartbeats((data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s);
}
console.log("heartbeating...")
return gateway.sendConstantHeartbeats((data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s)
}
// Make all code paths return a promise for consistency.
return Promise.resolve(undefined);
// 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);
if (id < total) this.spawnShards(total, id + 1)
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
import Client from "../module/Client.ts";
import { RequestMethod } from "../types/fetch";
import { RequestMethod } from "../types/fetch.ts";
type RequestBody = string | Blob | ArrayBufferView | ArrayBuffer | FormData | URLSearchParams | null | undefined;
@@ -12,9 +12,9 @@ export default class DiscordDiscordRequestManager {
this.token = token
}
async get(url: string) {
async get(url: string, body?: RequestBody) {
const headers = this.getDiscordHeaders();
return fetch(url, { headers }).then(res => res.json())
return fetch(url, { headers, body }).then(res => res.json())
}
async post (url: string, body: RequestBody) {
@@ -26,7 +26,7 @@ export default class DiscordDiscordRequestManager {
});
}
async delete (url: string, body: RequestBody) {
async delete (url: string, body?: RequestBody) {
const headers = this.getDiscordHeaders();
return fetch(url, {
method: RequestMethod.Delete,
+48 -50
View File
@@ -1,56 +1,54 @@
import {
connectWebSocket,
isWebSocketCloseEvent,
isWebSocketPingEvent,
isWebSocketPongEvent,
WebSocket
} from "https://deno.land/std/ws/mod.ts";
import { GatewayOpcode, Status } from "../types/discord.ts";
import { FulfilledClientOptions } from "../types/options.ts";
import { delay } from 'https://deno.land/std/util/async.ts';
import { WebSocket } from "https://deno.land/std/ws/mod.ts"
import { GatewayOpcode, Status } from "../types/discord.ts"
import { FulfilledClientOptions } from "../types/options.ts"
import { delay } from "https://deno.land/std/util/async.ts"
export default class Gateway {
constructor (public socket: WebSocket) {}
constructor(public socket: WebSocket) {}
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
}
});
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
}
})
}
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
}
sendHeartbeat (previousSequenceNumber: number | null = null) {
return this.sendObject({
op: GatewayOpcode.Heartbeat,
d: previousSequenceNumber
});
}
// 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)
}
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));
}
}
sendObject(object: object) {
return this.socket.send(JSON.stringify(object))
}
}
+10 -45
View File
@@ -7,15 +7,13 @@ import {
Get_Messages_Before,
MessageContent,
Create_Invite_Options
} from '../types/channel.ts'
import Client from '../module/client.ts'
import { endpoints } from '../constants/discord.ts'
import { create_message, Message } from './message.ts'
import { Message_Create_Options } from '../types/message.ts'
import { Permission, Permissions } from '../types/permission.ts'
import { Guild } from '../types/return-type.ts'
} from "../types/channel.ts"
import Client from "../module/client.ts"
import { endpoints } from "../constants/discord.ts"
import { create_message, Message } from "./message.ts"
import { Message_Create_Options } from "../types/message.ts"
export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => {
export const create_channel = (data: Channel_Create_Options, client: Client) => {
const base_channel = {
/** The unique id of the channel */
id: data.id,
@@ -60,7 +58,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien
// TODO: check if the bot has SEND_MESSAGES permission
}
if (typeof content === 'string') content = { content }
if (typeof content === "string") content = { content }
if (content.tts) {
// TODO: check if the bot has SEND_TTS_MESSAGE
}
@@ -91,33 +89,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien
parent_id: () => data.parent_id,
// TODO: fix this from being number on allow and deny to being array of strings
/** Fetch the permission overwrites */
permission_overwrites: () => data.permission_overwrites,
/** Check whether a member has certain permissions in this channel. */
has_permissions: (id: string, permissions: Permission[]) => {
if (id === guild.owner_id()) return true
const member = guild.members.get(id)
if (!member) {
throw 'Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild.'
}
let permissionBits = member.roles().reduce((bits, role_id) => {
const role = guild.roles.get(role_id)
if (!role) return bits
bits |= role.permissions()
return bits
}, 0)
data.permission_overwrites?.forEach(overwrite => {
permissionBits = (permissionBits & ~overwrite.deny) | overwrite.allow
})
if (permissionBits & Permissions.ADMINISTRATOR) return true
return permissions.every(permission => permissionBits & Permissions[permission])
}
permission_overwrites: () => data.permission_overwrites
}
// Guild Text Channel
@@ -133,7 +105,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien
delete_messages: (ids: string[], reason?: string) => {
// TODO: Requires the MANAGE_MESSAGES permission.
if (ids.length < 2) {
throw 'This endpoint will only accept 2-100 message ids.'
throw "This endpoint will only accept 2-100 message ids."
}
if (ids.length > 100) {
console.warn(
@@ -163,14 +135,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien
}
}
if (data.type === Channel_Types.GUILD_CATEGORY) {
return {
...base_guild_channel,
/** Gets an array of all the channels ids that are the children of this category. */
children_ids: () =>
Object.keys(guild.channels).filter(channel => guild.channels.get(channel).parent_id === data.id)
}
}
if (data.type === Channel_Types.GUILD_CATEGORY) return base_guild_channel
if (data.type === Channel_Types.GUILD_VOICE) {
return {
+17 -16
View File
@@ -1,6 +1,6 @@
import Client from '../module/client.ts'
import { endpoints } from '../constants/discord.ts'
import { format_image_url } from '../utils/cdn.ts'
import Client from "../module/client.ts"
import { endpoints } from "../constants/discord.ts"
import { format_image_url } from "../utils/cdn.ts"
import {
Create_Guild_Payload,
ChannelTypes,
@@ -13,13 +13,13 @@ import {
Create_Emojis_Options,
Edit_Emojis_Options,
Create_Role_Options
} from '../types/guild.ts'
import { create_role } from './role.ts'
import { create_member } from './member.ts'
import { create_channel } from './channel.ts'
import { Channel_Create_Options } from '../types/channel.ts'
import { Image_Size, Image_Formats } from '../types/cdn.ts'
import { Permissions, Permission } from '../types/permission.ts'
} from "../types/guild.ts"
import { create_role } from "./role.ts"
import { create_member } from "./member.ts"
import { create_channel } from "./channel.ts"
import { Channel_Create_Options } from "../types/channel.ts"
import { Image_Size, Image_Formats } from "../types/cdn.ts"
import { Permissions, Permission } from "../types/permission.ts"
export const create_guild = (data: Create_Guild_Payload, client: Client) => {
const guild = {
@@ -66,7 +66,7 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
/** The users in this guild. */
members: new Map(data.members.map(m => [m.user.id, create_member(m, data.id, data.roles, data.owner_id, client)])),
/** The channels in the guild */
channels: new Map(data.channels.map(c => [c.id, create_channel(c, client)])),
channels: new Map(data.channels.map(c => [c.id, create_channel(c, guild, client)])),
/** The presences of all the users in the guild. */
presences: new Map(data.presences.map(p => [p.user.id, p])),
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
@@ -85,6 +85,8 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
premium_subscription_count: () => data.premium_subscription_count,
/** The preferred locale of this guild only set if the guild has the DISCOVERABLE feature, defaults to en-US */
preferred_locale: () => data.preferred_locale,
/** Gets an array of all the channels ids that are the children of this category. */
category_children_ids: (id: string) => data.channels.filter(c => c.parent_id === id).map(c => c.id),
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
icon_url: (size: Image_Size = 128, format?: Image_Formats) =>
data.icon ? format_image_url(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined,
@@ -120,7 +122,7 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */
swap_channels: (channel_positions: Position_Swap[]) => {
if (channel_positions.length < 2) {
throw 'You must provide atleast two channels to be swapped.'
throw "You must provide atleast two channels to be swapped."
}
return client.discordRequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions)
},
@@ -268,12 +270,12 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
const member = guild.members.get(member_id)
if (!member) {
throw 'Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild.'
throw "Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild."
}
const channel = guild.channels.get(channel_id)
if (!channel) {
throw 'Invalid channel id provided. This channel was not found in the cache.'
throw "Invalid channel id provided. This channel was not found in the cache."
}
let permissionBits = member.roles().reduce((bits, role_id) => {
@@ -285,7 +287,7 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
return bits
}, 0)
data.permission_overwrites?.forEach(overwrite => {
channel.permission_overwrites?.forEach(overwrite => {
permissionBits = (permissionBits & ~overwrite.deny) | overwrite.allow
})
@@ -318,6 +320,5 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => {
}
}
return guild
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { Role_Data } from '../types/role'
import { Role_Data } from '../types/role.ts'
export const create_role = (data: Role_Data) => ({
/** The entire raw Role data */
+8 -7
View File
@@ -1,13 +1,14 @@
import { Properties } from "./discord.ts";
import { Properties } from "./discord.ts"
export interface FulfilledClientOptions {
token: string;
properties: Properties;
compress: boolean;
token: string
properties: Properties
compress: boolean
}
export interface ClientOptions {
token: string;
properties?: Properties;
compress?: boolean;
token: string
properties?: Properties
compress?: boolean
bot_id: string
}
+2 -3
View File
@@ -1,6 +1,5 @@
import { User } from "../structures/user";
import { Guild } from "../types/guild";
import { Channel } from "../types/channel";
import { User } from "../structures/user.ts";
import { Guild, Channel } from "../types/return-type.ts";
export const cache = {
guilds: new Map<string, Guild>(),
+2 -2
View File
@@ -1,5 +1,5 @@
import { ImageSize, ImageFormats } from '../structures/guild'
import { Image_Size, Image_Formats } from "../types/cdn.ts"
export const format_image_url = (url: string, size: ImageSize = 128, format?: ImageFormats) => {
export const format_image_url = (url: string, size: Image_Size = 128, format?: Image_Formats) => {
return `${url}.${format || url.includes('/a_') ? 'gif' : 'jpg'}/?size=${size}`
}