From 6a6b6cb0bf1fed9b5bdb0b02c85a6e5341c9ba43 Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 15:56:33 -0500 Subject: [PATCH 1/7] 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}`)); +}; From 05b63730481bc4b0853bf27c782c786f87b74193 Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 16:29:51 -0500 Subject: [PATCH 2/7] remove defaults and comments --- tsconfig.json | 72 +++++++-------------------------------------------- 1 file changed, 10 insertions(+), 62 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 19db3c704..f2edea5bf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,66 +1,14 @@ { "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // Hack to stop VSCode from suggesting imports without ./ or ../ as a prefix. - "baseUrl": "../../", /* Base directory to resolve non-absolute module names. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "target": "esnext", + "strict": true /* Enable all strict type-checking options. */, + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } } From 40601cdc6da4b2c0a84fdfb10423cdd960a8fd9e Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 16:30:02 -0500 Subject: [PATCH 3/7] prettier --- utils/cdn.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/utils/cdn.ts b/utils/cdn.ts index 1df62ab0e..ae221007f 100644 --- a/utils/cdn.ts +++ b/utils/cdn.ts @@ -1,10 +1,5 @@ -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}` +} From 2d9a194848f32cdf24ff3041fa6942c872ee6e70 Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 16:30:07 -0500 Subject: [PATCH 4/7] prettier --- structures/guild.ts | 521 ++++++++++++++++++++++++-------------------- 1 file changed, 280 insertions(+), 241 deletions(-) diff --git a/structures/guild.ts b/structures/guild.ts index eea824588..f40659d9f 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,268 +1,307 @@ -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" +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 */ - id: string - /** The guild name 2-100 characters */ - name: string - /** The guild icon image hash */ - icon: string | null - /** The guild splash image hash */ - splash: string | null - /** The id of the owner */ - owner_id: string - /** The voice region id for the guild */ - region: string - /** The afk channel id */ - afk_channel_id: string | null - /** AFK Timeout in seconds. */ - afk_timeout: number - /** The verification level required for the guild */ - verification_level: number - /** The roles in the guild */ - roles: Role[] - /** The custom guild emojis */ - emojis: Emoji[] - /** Enabled guild features */ - features: GuildFeatures[] - /** Required MFA level for the guild */ - mfa_level: number - /** The id of the channel to which system mesages are sent */ - system_channel_id: string | null - /** When this guild was joined at */ - joined_at: string - /** Whether this is considered a large guild */ - large: boolean - /** Whether this guild is unavailable */ - unavailable: boolean - /** Total number of members in this guild */ - member_count: number - voice_states: VoiceState[] - /** Users in the guild */ - members: Member[] - /** Channels in the guild */ - channels: Channel[] - presences: Presence[] - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - max_presences?: number | null - /** The maximum amount of members for the guild */ - max_members?: number - /** The vanity url code for the guild */ - vanity_url_code: string | null - /** The description for the guild */ - description: string | null - /** The banner hash */ - banner: string | null - /** The premium tier */ - premium_tier: number - /** The total number of users currently boosting this server. */ - premium_subscription_count: number - /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ - preferred_locale: string + /** The guild id */ + id: string + /** The guild name 2-100 characters */ + name: string + /** The guild icon image hash */ + icon: string | null + /** The guild splash image hash */ + splash: string | null + /** The id of the owner */ + owner_id: string + /** The voice region id for the guild */ + region: string + /** The afk channel id */ + afk_channel_id: string | null + /** AFK Timeout in seconds. */ + afk_timeout: number + /** The verification level required for the guild */ + verification_level: number + /** The roles in the guild */ + roles: Role[] + /** The custom guild emojis */ + emojis: Emoji[] + /** Enabled guild features */ + features: GuildFeatures[] + /** Required MFA level for the guild */ + mfa_level: number + /** The id of the channel to which system mesages are sent */ + system_channel_id: string | null + /** When this guild was joined at */ + joined_at: string + /** Whether this is considered a large guild */ + large: boolean + /** Whether this guild is unavailable */ + unavailable: boolean + /** Total number of members in this guild */ + member_count: number + voice_states: VoiceState[] + /** Users in the guild */ + members: Member[] + /** Channels in the guild */ + channels: Channel[] + presences: Presence[] + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + max_presences?: number | null + /** The maximum amount of members for the guild */ + max_members?: number + /** The vanity url code for the guild */ + vanity_url_code: string | null + /** The description for the guild */ + description: string | null + /** The banner hash */ + banner: string | null + /** The premium tier */ + premium_tier: number + /** The total number of users currently boosting this server. */ + premium_subscription_count: number + /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ + preferred_locale: string } interface Guild { - /** The guild id */ - id: string - /** The guild name 2-100 characters */ - name: string - /** The guild icon image hash */ - icon: string | null - /** The guild splash image hash */ - splash: string | null - /** The id of the owner */ - ownerID: string - /** The voice region id for the guild */ - region: string - /** The afk channel id */ - afkChannelID: string | null - /** AFK Timeout in seconds. */ - afkTimeout: number - /** The verification level required for the guild */ - verificationLevel: number - /** The roles in the guild */ - roles: Role[] - /** The custom guild emojis */ - emojis: Emoji[] - /** Enabled guild features */ - features: GuildFeatures[] - /** Required MFA level for the guild */ - mfaLevel: number - /** The id of the channel to which system mesages are sent */ - systemChannelID: string | null - /** When this guild was joined at */ - joinedAt: number - /** Whether this is considered a large guild */ - large: boolean - /** Whether this guild is unavailable */ - unavailable: boolean - /** Total number of members in this guild */ - memberCount: number - voiceStates: VoiceState[] - /** Users in the guild */ - members: Member[] - /** Channels in the guild */ - channels: Channel[] - presences: Presence[] - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - maxPresences?: number | null - /** The maximum amount of members for the guild */ - maxMembers?: number - /** The vanity url code for the guild */ - vanityURLCode: string | null - /** The description for the guild */ - description: string | null - /** The banner hash */ - banner: string | null - /** The premium tier */ - premiumTier: number - /** The total number of users currently boosting this server. */ - premiumSubscriptionCount: number - /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ - preferredLocale: string - /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - iconURL(): string | undefined - /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ - 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 + /** The guild id */ + id: string + /** The guild name 2-100 characters */ + name: string + /** The guild icon image hash */ + icon: string | null + /** The guild splash image hash */ + splash: string | null + /** The id of the owner */ + ownerID: string + /** The voice region id for the guild */ + region: string + /** The afk channel id */ + afkChannelID: string | null + /** AFK Timeout in seconds. */ + afkTimeout: number + /** The verification level required for the guild */ + verificationLevel: number + /** The roles in the guild */ + roles: Role[] + /** The custom guild emojis */ + emojis: Emoji[] + /** Enabled guild features */ + features: GuildFeatures[] + /** Required MFA level for the guild */ + mfaLevel: number + /** The id of the channel to which system mesages are sent */ + systemChannelID: string | null + /** When this guild was joined at */ + joinedAt: number + /** Whether this is considered a large guild */ + large: boolean + /** Whether this guild is unavailable */ + unavailable: boolean + /** Total number of members in this guild */ + memberCount: number + voiceStates: VoiceState[] + /** Users in the guild */ + members: Member[] + /** Channels in the guild */ + channels: Channel[] + presences: Presence[] + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + maxPresences?: number | null + /** The maximum amount of members for the guild */ + maxMembers?: number + /** The vanity url code for the guild */ + vanityURLCode: string | null + /** The description for the guild */ + description: string | null + /** The banner hash */ + banner: string | null + /** The premium tier */ + premiumTier: number + /** The total number of users currently boosting this server. */ + premiumSubscriptionCount: number + /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ + preferredLocale: string + /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ + iconURL(): string | undefined + /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ + 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 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[] + /** 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 + 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 + 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 + /** 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, - 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)), - features: data.features, - mfaLevel: data.mfa_level, - systemChannelID: data.system_channel_id, - joinedAt: Date.parse(data.joined_at), - large: data.large, - unavailable: data.unavailable, - memberCount: data.member_count, - voiceStates: data.voice_states.map(voiceState => createVoiceState(voiceState)), - members: data.members.map(member => createMember(member)), - channels: data.channels.map(channel => createChannel(channel)), - presences: data.presences.map(presence => createPresence(presence)), - maxPresences: data.max_presences, - maxMembers: data.max_members, - vanityURLCode: data.vanity_url_code, - description: data.description, - banner: data.banner, - premiumTier: data.premium_tier, - premiumSubscriptionCount: data.premium_subscription_count, - preferredLocale: data.preferred_locale, - 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: 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 - }) - } - } + const guild: Guild = { + 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)), + features: data.features, + mfaLevel: data.mfa_level, + systemChannelID: data.system_channel_id, + joinedAt: Date.parse(data.joined_at), + large: data.large, + unavailable: data.unavailable, + memberCount: data.member_count, + voiceStates: data.voice_states.map(voiceState => createVoiceState(voiceState)), + members: data.members.map(member => createMember(member)), + channels: data.channels.map(channel => createChannel(channel)), + presences: data.presences.map(presence => createPresence(presence)), + maxPresences: data.max_presences, + maxMembers: data.max_members, + vanityURLCode: data.vanity_url_code, + description: data.description, + banner: data.banner, + premiumTier: data.premium_tier, + premiumSubscriptionCount: data.premium_subscription_count, + preferredLocale: data.preferred_locale, + 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: 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 + return guild } From d36658287359ad41a3e3b527677c834209095bc5 Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 16:30:58 -0500 Subject: [PATCH 5/7] prettier on everything --- managers/RequestManager.ts | 23 ++-- managers/ShardingManager.ts | 5 +- mod.ts | 6 +- module/Client.ts | 72 +++++----- module/websocket.ts | 24 ++-- structures/channel.ts | 4 +- structures/emoji.ts | 4 +- structures/member.ts | 4 +- structures/presence.ts | 4 +- structures/role.ts | 4 +- structures/voiceState.ts | 4 +- types/discord.ts | 260 ++++++++++++++++++------------------ utils/logger.ts | 38 +++--- 13 files changed, 223 insertions(+), 229 deletions(-) diff --git a/managers/RequestManager.ts b/managers/RequestManager.ts index 2f00736f5..0b6fc7d0a 100644 --- a/managers/RequestManager.ts +++ b/managers/RequestManager.ts @@ -1,12 +1,12 @@ -import Client from "../module/Client.ts"; +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; + this.client = client + this.token = token } async get(url: string, payload?: unknown) { @@ -18,15 +18,14 @@ class RequestManager { // let attempts = 0 const headers = { Authorization: this.token, - "User-Agent": - `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)` - }; + 'User-Agent': `DiscordBot (https://github.com/skillz4killz/discordeno, 0.0.1)` + } - console.log("payload", payload); + console.log('payload', payload) - const data = await fetch(url, { headers }).then(res => res.json()); - return data; + const data = await fetch(url, { headers }).then(res => res.json()) + return data } } -export default RequestManager; +export default RequestManager diff --git a/managers/ShardingManager.ts b/managers/ShardingManager.ts index d22eb213a..80c13145f 100644 --- a/managers/ShardingManager.ts +++ b/managers/ShardingManager.ts @@ -1,4 +1,3 @@ -class ShardingManager extends Map { -} +class ShardingManager extends Map {} -export default ShardingManager; +export default ShardingManager diff --git a/mod.ts b/mod.ts index 5dfb40389..7c83caa3d 100644 --- a/mod.ts +++ b/mod.ts @@ -1,7 +1,7 @@ -import Client from "./module/Client.ts" -import { configs } from "./configs.ts" +import Client from './module/Client.ts' +import { configs } from './configs.ts' const Discordeno = new Client(configs.token) Discordeno.connect() -export default Discordeno \ No newline at end of file +export default Discordeno diff --git a/module/Client.ts b/module/Client.ts index a1d3a2fbd..b27b96bcf 100644 --- a/module/Client.ts +++ b/module/Client.ts @@ -1,87 +1,79 @@ -import { endpoints } from "../constants/discord.ts"; -import RequestManager from "../managers/RequestManager.ts"; -import { DiscordBotGateway, DiscordPayload, - DiscordHeartbeatPayload } from "../types/discord.ts"; -import ShardingManager from "../managers/ShardingManager.ts"; +import { endpoints } from '../constants/discord.ts' +import RequestManager from '../managers/RequestManager.ts' +import { DiscordBotGateway, DiscordPayload, DiscordHeartbeatPayload } from '../types/discord.ts' +import ShardingManager from '../managers/ShardingManager.ts' import { connectWebSocket, isWebSocketCloseEvent, isWebSocketPingEvent, isWebSocketPongEvent, WebSocket -} from "https://deno.land/std/ws/mod.ts"; +} from 'https://deno.land/std/ws/mod.ts' // 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 { logGreen, logRed, logYellow, logBlue } from "../utils/logger.ts"; +import { keepDiscordWebsocketAlive, updatePreviousSequenceNumber } from './websocket.ts' +import { logGreen, logRed, logYellow, logBlue } from '../utils/logger.ts' class Client { /** The bot's token. This should never be used by end users. It is meant to be used internally to make requests to the Discord API. */ - token: string; + token: string /** The Rate limit manager to handle all outgoing requests to discord. Not meant to be used by users. */ - RequestManager: RequestManager; + RequestManager: RequestManager /** Creates and handles all the shards necessary for the bot. */ - ShardingManager: ShardingManager; + ShardingManager: ShardingManager constructor(token: string) { - this.token = `Bot ${token}`; - this.RequestManager = new RequestManager(this, this.token); - this.ShardingManager = new ShardingManager(); + this.token = `Bot ${token}` + this.RequestManager = new RequestManager(this, this.token) + this.ShardingManager = new ShardingManager() } /** Begins initial handshake, creates the websocket with Discord and spawns all necessary shards. */ async connect() { - const data = (await this.RequestManager.get( - endpoints.GATEWAY_BOT - )) as DiscordBotGateway; + const data = (await this.RequestManager.get(endpoints.GATEWAY_BOT)) as DiscordBotGateway // Open a WS with the url from discord. - const sock = await connectWebSocket(data.url); - console.log(sock); - logGreen("ws connected! (type 'close' to quit)"); + const sock = await connectWebSocket(data.url) + console.log(sock) + logGreen("ws connected! (type 'close' to quit)") for await (const msg of sock.receive()) { - if (typeof msg === "string") { + 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); + logYellow('< ' + msg) } else if (isWebSocketPingEvent(msg)) { - logBlue("< ping"); + logBlue('< ping') } else if (isWebSocketPongEvent(msg)) { - logBlue("< pong"); + logBlue('< pong') } else if (isWebSocketCloseEvent(msg)) { - logRed(`closed: code=${msg.code}, reason=${msg.reason}`); + logRed(`closed: code=${msg.code}, reason=${msg.reason}`) } } // Begin spawning all necessary shards - this.spawnShards(data.shards); + this.spawnShards(data.shards) } 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 } } 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) } } -export default Client; +export default Client diff --git a/module/websocket.ts b/module/websocket.ts index 28957e286..004bbe873 100644 --- a/module/websocket.ts +++ b/module/websocket.ts @@ -1,16 +1,22 @@ -import { WebSocket } from "https://deno.land/std/ws/mod.ts"; +import { WebSocket } from 'https://deno.land/std/ws/mod.ts' let previousSequenceNumber: number | null = null -export const keepDiscordWebsocketAlive = (socket: WebSocket, millesecondsInterval: number, payload: number | null = null) => { - previousSequenceNumber = payload +export const keepDiscordWebsocketAlive = ( + socket: WebSocket, + millesecondsInterval: number, + payload: number | null = null +) => { + previousSequenceNumber = payload - setInterval(() => { - socket.send(JSON.stringify({ - op: 1, - d: previousSequenceNumber - })) - }, millesecondsInterval) + setInterval(() => { + socket.send( + JSON.stringify({ + op: 1, + d: previousSequenceNumber + }) + ) + }, millesecondsInterval) } export const updatePreviousSequenceNumber = (sequence: number | null = null) => { diff --git a/structures/channel.ts b/structures/channel.ts index 56a87d4bd..696770aba 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,3 +1,3 @@ export const createChannel = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/structures/emoji.ts b/structures/emoji.ts index dd4b785e0..ec8ade3be 100644 --- a/structures/emoji.ts +++ b/structures/emoji.ts @@ -1,3 +1,3 @@ export const createEmoji = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/structures/member.ts b/structures/member.ts index 808054783..49aee74dd 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -1,3 +1,3 @@ export const createMember = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/structures/presence.ts b/structures/presence.ts index 674908e9e..6f63f9f1f 100644 --- a/structures/presence.ts +++ b/structures/presence.ts @@ -1,3 +1,3 @@ export const createPresence = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/structures/role.ts b/structures/role.ts index e6d53f325..e10ad2b2e 100644 --- a/structures/role.ts +++ b/structures/role.ts @@ -1,3 +1,3 @@ export const createRole = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/structures/voiceState.ts b/structures/voiceState.ts index c7790c7bb..ef78632d1 100644 --- a/structures/voiceState.ts +++ b/structures/voiceState.ts @@ -1,3 +1,3 @@ export const createVoiceState = (data: unknown) => { - console.log(data); -}; + console.log(data) +} diff --git a/types/discord.ts b/types/discord.ts index a2a85a4ab..cc155da61 100644 --- a/types/discord.ts +++ b/types/discord.ts @@ -1,156 +1,156 @@ export interface DiscordPayload { - /** OP code for the payload */ - op: number - /** The real event data. Any JSON value basically. */ - d: unknown - /** The sequence number, used for resuming sessions and heartbeats. ONLY for OPCode 0 */ - s?: number - /** The event name for this payload. ONLY for OPCode 0 */ - t?: string + /** OP code for the payload */ + op: number + /** The real event data. Any JSON value basically. */ + d: unknown + /** The sequence number, used for resuming sessions and heartbeats. ONLY for OPCode 0 */ + s?: number + /** The event name for this payload. ONLY for OPCode 0 */ + t?: string } export interface DiscordBotGateway { - /** The WSS URL that can be used for connecting to the gateway. */ - url: string - /** The recommended number of shards to use when connecting. */ - shards: number - /** Info on the current start limit. */ - session_start_limit: { - /** The total number of session starts the current user is allowed. */ - total: number - /** The remaining number of session starts the current user is allowed. */ - remaining: number - /** Milliseconds left until limit is reset. */ - reset_after: number - } + /** The WSS URL that can be used for connecting to the gateway. */ + url: string + /** The recommended number of shards to use when connecting. */ + shards: number + /** Info on the current start limit. */ + session_start_limit: { + /** The total number of session starts the current user is allowed. */ + total: number + /** The remaining number of session starts the current user is allowed. */ + remaining: number + /** Milliseconds left until limit is reset. */ + reset_after: number + } } export interface DiscordHeartbeatPayload { - heartbeat_interval: number + heartbeat_interval: number } export enum GatewayOpcode { - Dispatch = 0, - Heartbeat, - Identify, - StatusUpdate, - VoiceStateUpdate, - Resume, - Reconnect, - RequestGuildMembers, - InvalidSession, - Hello, - HeartbeatACK + Dispatch = 0, + Heartbeat, + Identify, + StatusUpdate, + VoiceStateUpdate, + Resume, + Reconnect, + RequestGuildMembers, + InvalidSession, + Hello, + HeartbeatACK } export enum GatewayCloseEventCode { - UnknownError = 4000, - UnknownOpcode, - DecodeError, - NotAuthenticated, - AuthenticationFailed, - AlreadyAuthenticated, - InvalidSeq = 4007, - RateLimited, - SessionTimeout, - InvalidShard, - ShardingRequired + UnknownError = 4000, + UnknownOpcode, + DecodeError, + NotAuthenticated, + AuthenticationFailed, + AlreadyAuthenticated, + InvalidSeq = 4007, + RateLimited, + SessionTimeout, + InvalidShard, + ShardingRequired } export enum VoiceOpcode { - Identify, - SelectProtocol, - Ready, - Heartbeat, - SessionDescription, - Speaking, - HeartbeatACK, - Resume, - Hello, - Resumed, - ClientDisconnect = 13 + Identify, + SelectProtocol, + Ready, + Heartbeat, + SessionDescription, + Speaking, + HeartbeatACK, + Resume, + Hello, + Resumed, + ClientDisconnect = 13 } export enum VoiceCloseEventCode { - UnknownOpcode = 4001, - NotAuthenticated = 4003, - AuthenticationFailed, - AlreadyAuthenticated, - SessionNoLongerValid, - SessionTimeout = 4009, - ServerNotFound = 4011, - UnknownProtocol, - Disconnected = 4014, - VoiceServerCrashed, - UnknownEncryptionMode + UnknownOpcode = 4001, + NotAuthenticated = 4003, + AuthenticationFailed, + AlreadyAuthenticated, + SessionNoLongerValid, + SessionTimeout = 4009, + ServerNotFound = 4011, + UnknownProtocol, + Disconnected = 4014, + VoiceServerCrashed, + UnknownEncryptionMode } export enum HttpResponseCode { - Ok = 200, - Created, - NoContent = 204, - NotModified = 304, - BadRequest = 400, - Unauthorized = 401, - Forbidden = 403, - NotFound, - MethodNotAllowed, - TooManyRequests = 429, - GatewayUnavailable = 502, - // ServerError left untyped because it's 5xx. + Ok = 200, + Created, + NoContent = 204, + NotModified = 304, + BadRequest = 400, + Unauthorized = 401, + Forbidden = 403, + NotFound, + MethodNotAllowed, + TooManyRequests = 429, + GatewayUnavailable = 502 + // ServerError left untyped because it's 5xx. } export enum JSONErrorCode { - UnknownAccount = 10001, - UnknownApplication, - UnknownChannel, - UnknownGuild, - UnknownIntegration, - UnknownInvite, - UnknownMember, - UnknownMessge, - UnknownOverwrite, - UnknownProvider, - UnknownRole, - UnknownToken = 10012, - UnknownUser, - UnknownEmoji, - UnknownWebhook, - BotsCannotUse = 20001, - OnlyBotsCanUse, - MaxGuildsReached = 30001, - MaxFriendsReached, - MaxPinsReached, - MaxGuildRolesReached = 30005, - MaxReactionsReached = 30010, - MaxGuildChannelsReached = 30013, - MaxInvitesReached = 30016, - Unathorized = 40001, - UserIsBannedFromGuild = 40007, - MissingAccess = 50001, - InvalidAccountType = 50002, - CannotExecuteOnDMChannel, - WidgetDisabled, - CannotEditMessageByAnotherUser, - CannotSendEmptyMessage, - CannotSendMessageToUser, - CannotSendMessageInVoiceChannel, - ChannelVerificationTooHigh, - OAuth2ApplicationNoBot, - OAuth2ApplicationLimitReached, - InvalidOAuthState, - MissingPermissions, - InvalidAuthenticationToken, - NoteIsTooLong, - TooFewOrTooManyMessagesToDelete, - MessageCanOnlyBePinnedInParentChannel = 50019, - InviteCodeTakenOrInvalid, - CannotExecuteOnSystemMessage, - InvalidOAuth2AccessToken, - MessageProvidedTooOldToBulkDelet = 50034, - InvalidFormBody, - InviteAcceptedToGuildApplicationBotNotIn, - InvalidAPIVersion = 50041, - ReactionBlocked = 90001, - ResourceOverloaded = 130000 + UnknownAccount = 10001, + UnknownApplication, + UnknownChannel, + UnknownGuild, + UnknownIntegration, + UnknownInvite, + UnknownMember, + UnknownMessge, + UnknownOverwrite, + UnknownProvider, + UnknownRole, + UnknownToken = 10012, + UnknownUser, + UnknownEmoji, + UnknownWebhook, + BotsCannotUse = 20001, + OnlyBotsCanUse, + MaxGuildsReached = 30001, + MaxFriendsReached, + MaxPinsReached, + MaxGuildRolesReached = 30005, + MaxReactionsReached = 30010, + MaxGuildChannelsReached = 30013, + MaxInvitesReached = 30016, + Unathorized = 40001, + UserIsBannedFromGuild = 40007, + MissingAccess = 50001, + InvalidAccountType = 50002, + CannotExecuteOnDMChannel, + WidgetDisabled, + CannotEditMessageByAnotherUser, + CannotSendEmptyMessage, + CannotSendMessageToUser, + CannotSendMessageInVoiceChannel, + ChannelVerificationTooHigh, + OAuth2ApplicationNoBot, + OAuth2ApplicationLimitReached, + InvalidOAuthState, + MissingPermissions, + InvalidAuthenticationToken, + NoteIsTooLong, + TooFewOrTooManyMessagesToDelete, + MessageCanOnlyBePinnedInParentChannel = 50019, + InviteCodeTakenOrInvalid, + CannotExecuteOnSystemMessage, + InvalidOAuth2AccessToken, + MessageProvidedTooOldToBulkDelet = 50034, + InvalidFormBody, + InviteAcceptedToGuildApplicationBotNotIn, + InvalidAPIVersion = 50041, + ReactionBlocked = 90001, + ResourceOverloaded = 130000 } diff --git a/utils/logger.ts b/utils/logger.ts index 3a4e8a03b..238f99e7f 100644 --- a/utils/logger.ts +++ b/utils/logger.ts @@ -1,34 +1,32 @@ -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`; + let hour = hours + let amOrPm = `AM` if (hour > 12) { - amOrPm = `PM`; - hour = 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}`)) +} From 82f474ff4b526f9447060ffd795907a2ead4d11d Mon Sep 17 00:00:00 2001 From: Skillz Date: Mon, 10 Feb 2020 23:39:31 -0500 Subject: [PATCH 6/7] more work on guilds --- constants/discord.ts | 8 +- structures/guild.ts | 207 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 204 insertions(+), 11 deletions(-) diff --git a/constants/discord.ts b/constants/discord.ts index 1674cd72e..e8ce88c68 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -6,8 +6,14 @@ export const baseEndpoints = { export const endpoints = { GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, + GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, - GUILD_CHANNELS: (id: string) => `/guilds/${id}/channels`, + GUILD_CHANNELS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/channels`, + GUILD_EMOJI: (id: string, emojiID: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emojiID}`, + GUILD_EMOJIS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis`, GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, + GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, + GUILD_ROLE: (id: string, roleID: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${roleID}`, + GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}` } diff --git a/structures/guild.ts b/structures/guild.ts index f40659d9f..5092b074f 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -129,13 +129,118 @@ interface Guild { /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ preferredLocale: string /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - iconURL(): string | undefined + iconURL(size?: ImageSize, format?: ImageFormats): string | undefined /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ - splashURL(): string | undefined + splashURL(size?: ImageSize, format?: ImageFormats): string | undefined /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ - bannerURL(): string | undefined + bannerURL(size?: ImageSize, format?: ImageFormats): string | undefined /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ createChannel(name: string, options: ChannelCreateOptions): Promise + /** Create an emoji in the server. Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a JSON status code. */ + createEmoji(name: string, image: string, options: CreateEmojisOptions): Promise + /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ + editEmoji(id: string, options: EditEmojisOptions): Promise + /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ + deleteEmoji(id: string, reason?: string): Promise + /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ + createRole(options: CreateRoleOptions): Promise + /** Edi a guild role. Requires the MANAGE_ROLES permission. */ + editRole(id: string, options: CreateRoleOptions): Promise + /** Delete a guild role. Requires the MANAGE_ROLES permission. */ + deleteRole(id: string): Promise + /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ + getPruneCount(days: number): Promise + /** Begin pruning all members in the given time period */ + pruneMembers(days: number): Promise + getAuditLogs(options: GetAuditLogsOptions): Promise + + leaveVoiceChannel(): Promise +} + +export interface GetAuditLogsOptions { + /** Filter the logs for actions made by this user. */ + user_id?: string + /** The type of audit log. */ + action_type?: AuditLogType + /** Filter the logs before a certain log entry. */ + before?: string + /** How many entries are returned. Between 1-100. Default 50. */ + limit?: number +} + +export type AuditLogType = + | `GUILD_UPDATE` + | `CHANNEL_CREATE` + | `CHANNEL_UPDATE` + | `CHANNEL_DELETE` + | `CHANNEL_OVERWRITE_CREATE` + | `CHANNEL_OVERWRITE_UPDATE` + | `CHANNEL_OVERWRITE_DELETE` + | `MEMBER_KICK` + | `MEMBER_PRUNE` + | `MEMBER_BAN_ADD` + | `MEMBER_BAN_REMOVE` + | `MEMBER_UPDATE` + | `MEMBER_ROLE_UPDATE` + | `MEMBER_MOVE` + | `MEMBER_DISCONNECT` + | `BOT_ADD` + | `ROLE_CREATE` + | `ROLE_UPDATE` + | `ROLE_DELETE` + | `INVITE_CREATE` + | `INVITE_UPDATE` + | `INVITE_DELETE` + | `WEBHOOK_CREATE` + | `WEBHOOK_UPDATE` + | `WEBHOOK_DELETE` + | `EMOJI_CREATE` + | `EMOJI_UPDATE` + | `EMOJI_DELETE` + | `MESSAGE_DELETE` + | `MESSAGE_BULK_DELETE` + | `MESSAGE_PIN` + | `MESSAGE_UNPIN` + | `INTEGRATION_CREATE` + | `INTEGRATION_UPDATE` + | `INTEGRATION_DELETE` + +export enum AuditLogs { + GUILD_UPDATE = 1, + CHANNEL_CREATE = 10, + CHANNEL_UPDATE, + CHANNEL_DELETE, + CHANNEL_OVERWRITE_CREATE, + CHANNEL_OVERWRITE_UPDATE, + CHANNEL_OVERWRITE_DELETE, + MEMBER_KICK = 20, + MEMBER_PRUNE, + MEMBER_BAN_ADD, + MEMBER_BAN_REMOVE, + MEMBER_UPDATE, + MEMBER_ROLE_UPDATE, + MEMBER_MOVE, + MEMBER_DISCONNECT, + BOT_ADD, + ROLE_CREATE = 30, + ROLE_UPDATE, + ROLE_DELETE, + INVITE_CREATE = 40, + INVITE_UPDATE, + INVITE_DELETE, + WEBHOOK_CREATE = 50, + WEBHOOK_UPDATE, + WEBHOOK_DELETE, + EMOJI_CREATE = 60, + EMOJI_UPDATE, + EMOJI_DELETE, + MESSAGE_DELETE = 72, + MESSAGE_BULK_DELETE, + MESSAGE_PIN, + MESSAGE_UNPIN, + INTEGRATION_CREATE = 80, + INTEGRATION_UPDATE, + INTEGRATION_DELETE } export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 @@ -246,6 +351,34 @@ export interface ChannelCreateOptions { parent_id?: string /** Whether the channel is nsfw */ nsfw?: boolean + /** The reason to add in the Audit Logs. */ + reason?: string +} + +export interface CreateEmojisOptions { + /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ + roles: string[] + /** The reason to have in the Audit Logs. */ + reason: string +} + +export interface EditEmojisOptions { + /** The name of the emoji */ + name: string + /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ + roles: string[] +} + +export interface CreateRoleOptions { + name?: string + permissions?: Permission[] + color?: number + hoist?: boolean + mentionable?: boolean +} + +export interface PrunePayload { + pruned: number } export const createGuild = (data: CreateGuildPayload, client: Client) => { @@ -280,27 +413,81 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { premiumTier: data.premium_tier, premiumSubscriptionCount: data.premium_subscription_count, preferredLocale: data.preferred_locale, - iconURL: (size: ImageSize = 128, format?: ImageFormats) => + iconURL: (size, format) => data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, - splashURL: (size: ImageSize = 128, format?: ImageFormats) => + splashURL: (size, format) => data.splash ? formatImageURL(endpoints.GUILD_SPLASH(data.id, data.splash), size, format) : undefined, - bannerURL: (size: ImageSize = 128, format?: ImageFormats) => + bannerURL: (size, format) => data.banner ? formatImageURL(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, - createChannel: (name: string, options?: ChannelCreateOptions) => { + createChannel: (name, options) => { // 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 => ({ + ...perm, allow: perm.allow.map(p => Permissions[p]), - deny: perm.deny.map(p => Permissions[p]), - ...perm + deny: perm.deny.map(p => Permissions[p]) })) : undefined, ...options }) - } + }, + createEmoji: (name, image, options) => { + // TODO: Check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { + ...options, + name, + image + }) + }, + editEmoji: (id, options) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.patch(endpoints.GUILD_EMOJI(data.id, id), { + name: options.name, + roles: options.roles + }) + }, + deleteEmoji: (id, reason) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) + }, + createRole: async options => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + const role = await client.RequestManager.post(endpoints.GUILD_ROLES(data.id), { + ...options, + permissions: options.permissions?.map(perm => Permissions[perm]) + }) + // TODO: cache this role + + return role + }, + editRole: (id, options) => { + return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) + }, + deleteRole: id => { + return client.RequestManager.delete(endpoints.GUILD_ROLE(data.id, id)) + }, + getPruneCount: async days => { + if (days < 1) throw `The number of days to count prune for must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission + const result = (await client.RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload + return result.pruned + }, + pruneMembers: days => { + if (days < 1) throw `The number of days must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission. + return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) + }, + getAuditLogs: options => { + // TODO: check if the bot has VIEW_AUDIT_LOGS permission + return client.RequestManager.get(endpoints.GUILD_AUDIT_LOGS(data.id), { + ...options, + limit: options.limit && options.limit >= 1 && options.limit <= 100 ? options.limit : 50 + }) + }, + leaveVoiceChannel: () => {} } return guild From 0172bc1b9454b2824fcd3c25ea1850ec6f2580c6 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Mon, 10 Feb 2020 23:49:56 -0500 Subject: [PATCH 7/7] Create CONTRIBUTING.md --- CONTRIBUTING.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..58ac18338 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Fundamental Design Goals + +This document serves to outline the overall design goals of the project. Please see below a list of these fundamentals. + +## Do not allow anything Discord does not permit +Prevent any and all attempts of making user bots. If someone connects and the client.user is not a bot user then throw an error immediately. + +Do not support non-bot features like Group DMs or dm calls etc... + +## Prettier Philosophy Regarding Options + +Avoid options/customizable whenever possible. Always enforce default values. Except in cases like intents where the user should be able to pick which intents to listen for. + +## Security + +Permission checks should be done by the library! We can throw a custom error that shows which permissions are missing in order to run this request and save an API call. This will also prevent bots from getting banned due to Missing Access errors. + +Typescript 3.8 provides **TRUE** private props and methods that no one can access. We will use this to our advantage to truly make a proper API. This isn't a silly `_` to mark it as a private but the user should NEVER be able to access it no matter what. + +## Functional API + +Events emitted by the client, for example the message creation event, should not emit a `Message` class instance, but instead a *POJO* (Plain Ol' JavaScript Object). This will overall make a cleaner and more performant API, while removing the headaches of extending built-in classes, and inheritance. + +Use functions when possible instead of an event emitter to prevent emitter related memory leak issues or a number of other headaches that arise. + +TLDR: Avoid `classes` whenever possible. Avoid `loops` whenever possible(opt for iterations like .forEach, map reduce, some find etc...) + +## Documentation + +Use `/** Description here */` comments above all properties and methods to describe it so that VSC and other good IDE's with intellisense can pick it up and provide the documentation right inside the IDE preventing a developer from needing Discord API docs or even Deno documentation. + +We should have a step by step guide nonetheless but this is a POST v1 launch. +We should have a template repo to creating a boilerplate bot. + +## Backwards Compatibility BS + +Backwards compatibility is the death of code. It causes clutter and uglyness to pile up and makes developers lazier. There will be no such thing as backwards compatibility reasons in Discordeno. We will always support the latest and greatest of JS. The end! Users can fork the lib at any commit to keep older versions until they are ready to update. + +That said, we don't expect many things to be changing drastically after v1. As you can imagine Typescript allows the latest and greatest of JS so we will be ahead of the curve for years to come. + +## Style Guide + +Prettier is our style guide. No discussions around styling ever. The options are set and that is all. When you code let prettier handle the styling. PERIOD!