mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
even more fixes
This commit is contained in:
+38
-35
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user