From 6a6b6cb0bf1fed9b5bdb0b02c85a6e5341c9ba43 Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 15:56:33 -0500 Subject: [PATCH] even more work and first deno fmt --- .vscode/setting.json | 13 +---- constants/discord.ts | 1 + managers/RequestManager.ts | 47 +++++++-------- managers/ShardingManager.ts | 2 +- module/Client.ts | 27 +++++---- structures/channel.ts | 3 + structures/emoji.ts | 3 + structures/guild.ts | 113 ++++++++++++++++++++++++++++++++---- structures/member.ts | 3 + structures/presence.ts | 3 + structures/role.ts | 3 + structures/voiceState.ts | 3 + utils/cdn.ts | 13 +++-- utils/logger.ts | 42 +++++++------- 14 files changed, 195 insertions(+), 81 deletions(-) create mode 100644 structures/channel.ts create mode 100644 structures/emoji.ts create mode 100644 structures/member.ts create mode 100644 structures/presence.ts create mode 100644 structures/role.ts create mode 100644 structures/voiceState.ts diff --git a/.vscode/setting.json b/.vscode/setting.json index 885f0f1ae..50a13e280 100644 --- a/.vscode/setting.json +++ b/.vscode/setting.json @@ -1,20 +1,9 @@ { "deno.enable": true, + "editor.formatOnSave": true, "[typescript]": { "editor.defaultFormatter": "axetroy.vscode-deno" }, - "[typescriptreact]": { - "editor.defaultFormatter": "axetroy.vscode-deno" - }, - "[javascript]": { - "editor.defaultFormatter": "axetroy.vscode-deno" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "axetroy.vscode-deno" - }, - "[markdown]": { - "editor.defaultFormatter": "axetroy.vscode-deno" - }, "[json]": { "editor.defaultFormatter": "axetroy.vscode-deno" } diff --git a/constants/discord.ts b/constants/discord.ts index 95f9921a4..1674cd72e 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -7,6 +7,7 @@ export const baseEndpoints = { export const endpoints = { GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, + GUILD_CHANNELS: (id: string) => `/guilds/${id}/channels`, GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}` } diff --git a/managers/RequestManager.ts b/managers/RequestManager.ts index b58f6b38c..2f00736f5 100644 --- a/managers/RequestManager.ts +++ b/managers/RequestManager.ts @@ -1,31 +1,32 @@ import Client from "../module/Client.ts"; class RequestManager { - client: Client - token: string + client: Client; + token: string; - constructor(client: Client, token: string) { - this.client = client - this.token = token - } + constructor(client: Client, token: string) { + this.client = client; + this.token = token; + } - async get(url: string, payload?: unknown) { - // THIS IS IMPORTANT. It keeps clean stack errors in the users own files to better help debug errors. - // const stackHolder = {}; - // TODO: Figure out why this doesnt work - // Error.captureStackTrace(stackHolder) + async get(url: string, payload?: unknown) { + // THIS IS IMPORTANT. It keeps clean stack errors in the users own files to better help debug errors. + // const stackHolder = {}; + // TODO: Figure out why this doesnt work + // Error.captureStackTrace(stackHolder) - // let attempts = 0 - const headers = { - Authorization: this.token, - "User-Agent": `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)`, - } - - console.log('payload', payload) - - const data = await fetch(url, { headers }).then(res => res.json()) - return data - } + // let attempts = 0 + const headers = { + Authorization: this.token, + "User-Agent": + `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)` + }; + + console.log("payload", payload); + + const data = await fetch(url, { headers }).then(res => res.json()); + return data; + } } -export default RequestManager \ No newline at end of file +export default RequestManager; diff --git a/managers/ShardingManager.ts b/managers/ShardingManager.ts index e82c74479..d22eb213a 100644 --- a/managers/ShardingManager.ts +++ b/managers/ShardingManager.ts @@ -1,4 +1,4 @@ class ShardingManager extends Map { } -export default ShardingManager +export default ShardingManager; diff --git a/module/Client.ts b/module/Client.ts index 8f1dbe93c..a1d3a2fbd 100644 --- a/module/Client.ts +++ b/module/Client.ts @@ -1,6 +1,7 @@ import { endpoints } from "../constants/discord.ts"; import RequestManager from "../managers/RequestManager.ts"; -import { DiscordBotGateway, DiscordPayload, DiscordHeartbeatPayload } from "../types/discord.ts"; +import { DiscordBotGateway, DiscordPayload, + DiscordHeartbeatPayload } from "../types/discord.ts"; import ShardingManager from "../managers/ShardingManager.ts"; import { connectWebSocket, @@ -12,7 +13,8 @@ import { // 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 { keepDiscordWebsocketAlive, + updatePreviousSequenceNumber } from "./websocket.ts"; import { logGreen, logRed, logYellow, logBlue } from "../utils/logger.ts"; class Client { @@ -36,16 +38,16 @@ class Client { )) as DiscordBotGateway; // Open a WS with the url from discord. const sock = await connectWebSocket(data.url); - console.log(sock) + 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) + const json = JSON.parse(msg); + this.handleDiscordPayload(json, sock); } catch { - logRed(`Invalid JSON String send by discord: ${msg}`) + logRed(`Invalid JSON String send by discord: ${msg}`); } logYellow("< " + msg); } else if (isWebSocketPingEvent(msg)) { @@ -64,11 +66,15 @@ class Client { handleDiscordPayload(data: DiscordPayload, socket: WebSocket) { switch (data.op) { case 10: // Initial Heartbeat - keepDiscordWebsocketAlive(socket, (data.d as DiscordHeartbeatPayload).heartbeat_interval, data.s) - break + keepDiscordWebsocketAlive( + socket, + (data.d as DiscordHeartbeatPayload).heartbeat_interval, + data.s + ); + break; case 11: - updatePreviousSequenceNumber(data.s) - break + updatePreviousSequenceNumber(data.s); + break; } } @@ -79,4 +85,3 @@ class Client { } export default Client; - diff --git a/structures/channel.ts b/structures/channel.ts new file mode 100644 index 000000000..56a87d4bd --- /dev/null +++ b/structures/channel.ts @@ -0,0 +1,3 @@ +export const createChannel = (data: unknown) => { + console.log(data); +}; diff --git a/structures/emoji.ts b/structures/emoji.ts new file mode 100644 index 000000000..dd4b785e0 --- /dev/null +++ b/structures/emoji.ts @@ -0,0 +1,3 @@ +export const createEmoji = (data: unknown) => { + console.log(data); +}; diff --git a/structures/guild.ts b/structures/guild.ts index 2fbaae1a7..eea824588 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,6 +1,12 @@ import Client from "../module/Client" import { endpoints } from "../constants/discord" import { formatImageURL } from "../utils/cdn" +import { createRole } from "./role" +import { createEmoji } from "./emoji" +import { createVoiceState } from "./voiceState" +import { createMember } from "./member" +import { createChannel } from "./channel" +import { createPresence } from "./presence" interface CreateGuildPayload { /** The guild id */ @@ -128,24 +134,103 @@ interface Guild { splashURL(): string | undefined /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ bannerURL(): string | undefined + /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ + createChannel(name: string, options: ChannelCreateOptions): Promise } export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 export type ImageFormats = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' +export type ChannelType = 'text' | 'dm' | 'news' | 'voice' | 'category' | 'store' + +export type Permission = `CREATE_INSTANT_INVITE` | `KICK_MEMBERS` | `BAN_MEMBERS` | `ADMINISTRATOR` | `MANAGE_CHANNELS` | `MANAGE_GUILD` | `ADD_REACTIONS` | `VIEW_AUDIT_LOG` | `VIEW_CHANNEL` | `SEND_MESSAGES` | `SEND_TTS_MESSAGES` | `MANAGE_MESSAGES` | `EMBED_LINKS` | `ATTACH_FILES` | `READ_MESSAGE_HISTORY` | `MENTION_EVERYONE` | `USE_EXTERNAL_EMOJIS` | `CONNECT` | `SPEAK` | `MUTE_MEMBERS` | `DEAFEN_MEMBERS` | `MOVE_MEMBERS` | `USE_VAD` | `PRIORITY_SPEAKER` | `STREAM` | `CHANGE_NICKNAME` | `MANAGE_NICKNAMES` | `MANAGE_ROLES` | `MANAGE_WEBHOOKS` | `MANAGE_EMOJIS` + +export interface Overwrite { + /** The role or user id */ + id: string + /** Whether this is a role or a member */ + type: 'role' | 'member' + /** The permissions that this id is allowed to do. (This will mark it as a green check.) */ + allow: Permission[] + /** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */ + deny: Permission[] +} + +export enum ChannelTypes { + text, + dm, + voice, + category = 4, + news, + store +} + +export enum Permissions { + CREATE_INSTANT_INVITE = 0x00000001, + KICK_MEMBERS = 0x00000002, + BAN_MEMBERS = 0x00000004, + ADMINISTRATOR = 0x00000008, + MANAGE_CHANNELS = 0x00000010, + MANAGE_GUILD = 0x00000020, + ADD_REACTIONS = 0x00000040, + VIEW_AUDIT_LOG = 0x00000080, + VIEW_CHANNEL = 0x00000400, + SEND_MESSAGES = 0x00000800, + SEND_TTS_MESSAGES = 0x00001000, + MANAGE_MESSAGES = 0x00002000, + EMBED_LINKS = 0x00004000, + ATTACH_FILES = 0x00008000, + READ_MESSAGE_HISTORY = 0x00010000, + MENTION_EVERYONE = 0x00020000, + USE_EXTERNAL_EMOJIS = 0x00040000, + CONNECT = 0x00100000, + SPEAK = 0x00200000, + MUTE_MEMBERS = 0x00400000, + DEAFEN_MEMBERS = 0x00800000, + MOVE_MEMBERS = 0x01000000, + USE_VAD = 0x02000000, + PRIORITY_SPEAKER = 0x00000100, + STREAM = 0x00000200, + CHANGE_NICKNAME = 0x04000000, + MANAGE_NICKNAMES = 0x08000000, + MANAGE_ROLES = 0x10000000, + MANAGE_WEBHOOKS = 0x20000000, + MANAGE_EMOJIS = 0x40000000 +} + +export interface ChannelCreateOptions { + /** The type of the channel */ + type?: ChannelType, + /** The channel topic. (0-1024 characters) */ + topic?: string + /** The bitrate(in bits) of the voice channel. */ + bitrate?: number + /** The user limit of the voice channel. */ + user_limit?: number + /** The amount of seconds a user has to wait before sending another message. (0-21600 seconds). Bots, as well as users with the permission `manage_messages or manage_channel` are unaffected. */ + rate_limit_per_user?: number + /** The sorting position of the channel */ + position?: number + /** The channel's permission overwrites */ + permission_overwrites?: Overwrite[] + /** The id of the parent category for the channel */ + parent_id?: string + /** Whether the channel is nsfw */ + nsfw?: boolean +} export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { - id: data.id, - name: data.name, + id: data.id, + name: data.name, icon: data.icon, splash: data.splash, - ownerID: data.owner_id, - region: data.region, - afkChannelID: data.afk_channel_id, - afkTimeout: data.afk_timeout, - verificationLevel: data.verification_level, - roles: data.roles.map(role => createRole(role)), - emojis: data.emojis.map(emoji => createEmoji(emoji)), + ownerID: data.owner_id, + region: data.region, + afkChannelID: data.afk_channel_id, +afkTimeout: data.afk_timeout, + verificationLevel: data.verification_level, + roles: data.roles.map(role => createRole(role)), + emojis: data.emojis.map(emoji => createEmoji(emoji)), features: data.features, mfaLevel: data.mfa_level, systemChannelID: data.system_channel_id, @@ -168,7 +253,15 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { iconURL: (size: ImageSize = 128, format?: ImageFormats) => data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, splashURL: (size: ImageSize = 128, format?: ImageFormats) => data.splash ? formatImageURL(endpoints.GUILD_SPLASH(data.id, data.splash), size, format) : undefined, bannerURL: (size: ImageSize = 128, format?: ImageFormats) => data.banner ? formatImageURL(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, - createChannel: (name, ) + createChannel: (name: string, options?: ChannelCreateOptions) => { + // TODO: Check if the bot has `MANAGE_CHANNELS` permission before making a channel + return client.RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { + name, + type: options?.type ? ChannelTypes[options.type] : undefined, + permission_overwrites: options?.permission_overwrites ? options.permission_overwrites.map(perm => ({ allow: perm.allow.map(p => Permissions[p]), deny: perm.deny.map(p => Permissions[p]), ...perm })) : undefined, + ...options + }) + } } return guild diff --git a/structures/member.ts b/structures/member.ts new file mode 100644 index 000000000..808054783 --- /dev/null +++ b/structures/member.ts @@ -0,0 +1,3 @@ +export const createMember = (data: unknown) => { + console.log(data); +}; diff --git a/structures/presence.ts b/structures/presence.ts new file mode 100644 index 000000000..674908e9e --- /dev/null +++ b/structures/presence.ts @@ -0,0 +1,3 @@ +export const createPresence = (data: unknown) => { + console.log(data); +}; diff --git a/structures/role.ts b/structures/role.ts new file mode 100644 index 000000000..e6d53f325 --- /dev/null +++ b/structures/role.ts @@ -0,0 +1,3 @@ +export const createRole = (data: unknown) => { + console.log(data); +}; diff --git a/structures/voiceState.ts b/structures/voiceState.ts new file mode 100644 index 000000000..c7790c7bb --- /dev/null +++ b/structures/voiceState.ts @@ -0,0 +1,3 @@ +export const createVoiceState = (data: unknown) => { + console.log(data); +}; diff --git a/utils/cdn.ts b/utils/cdn.ts index e96f8c819..1df62ab0e 100644 --- a/utils/cdn.ts +++ b/utils/cdn.ts @@ -1,5 +1,10 @@ -import { ImageSize, ImageFormats } from "../structures/guild" +import { ImageSize, ImageFormats } from "../structures/guild"; -export const formatImageURL = (url: string, size: ImageSize = 128, format?: ImageFormats) => { - return `${url}.${format || url.includes("/a_") ? "gif" : 'jpg'}/?size=${size}` -} +export const formatImageURL = ( + url: string, + size: ImageSize = 128, + format?: ImageFormats +) => { + return `${url}.${format || url.includes("/a_") ? "gif" : "jpg"}/?size=${size + }`; +}; diff --git a/utils/logger.ts b/utils/logger.ts index 2fe507566..3a4e8a03b 100644 --- a/utils/logger.ts +++ b/utils/logger.ts @@ -1,32 +1,34 @@ -import { blue, green, red, yellow } from "https://deno.land/std/fmt/colors.ts" +import { blue, green, red, yellow } from "https://deno.land/std/fmt/colors.ts"; export const getTime = () => { - const now = new Date() - const hours = now.getHours() - const minute = now.getMinutes() + const now = new Date(); + const hours = now.getHours(); + const minute = now.getMinutes(); - let hour = hours - let amOrPm = `AM` - if (hour > 12) { - amOrPm = `PM` - hour = hour - 12 - } + let hour = hours; + let amOrPm = `AM`; + if (hour > 12) { + amOrPm = `PM`; + hour = hour - 12; + } - return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}` -} + return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 + ? minute + : `0${minute}`} ${amOrPm}`; +}; export const logGreen = (text: string) => { - console.log(green(`[${getTime()}] => ${text}`)) -} + console.log(green(`[${getTime()}] => ${text}`)); +}; export const logBlue = (text: string) => { - console.log(blue(`[${getTime()}] => ${text}`)) -} + console.log(blue(`[${getTime()}] => ${text}`)); +}; export const logRed = (text: string) => { - console.log(red(`[${getTime()}] => ${text}`)) -} + console.log(red(`[${getTime()}] => ${text}`)); +}; export const logYellow = (text: string) => { - console.log(yellow(`[${getTime()}] => ${text}`)) -} + console.log(yellow(`[${getTime()}] => ${text}`)); +};