From 03ffb63f0fd9dc3a9ca1644445c632b81748f447 Mon Sep 17 00:00:00 2001 From: ayntee Date: Fri, 26 Feb 2021 14:54:07 +0400 Subject: [PATCH 001/200] build: update dependencies to latest (#550) --- deps.ts | 2 +- src/interactions/deps.ts | 2 +- test/deps.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps.ts b/deps.ts index bdb3e0b04..6988b6a9a 100644 --- a/deps.ts +++ b/deps.ts @@ -1 +1 @@ -export { encode } from "https://deno.land/std@0.87.0/encoding/base64.ts"; +export { encode } from "https://deno.land/std@0.88.0/encoding/base64.ts"; diff --git a/src/interactions/deps.ts b/src/interactions/deps.ts index 2bc944b72..0a4ff58ff 100644 --- a/src/interactions/deps.ts +++ b/src/interactions/deps.ts @@ -1,2 +1,2 @@ -export { serve } from "https://deno.land/std@0.87.0/http/server.ts"; +export { serve } from "https://deno.land/std@0.88.0/http/server.ts"; export { verify } from "https://esm.sh/@evan/wasm@0.0.49/target/ed25519/deno.js"; diff --git a/test/deps.ts b/test/deps.ts index e028981d2..d164db214 100644 --- a/test/deps.ts +++ b/test/deps.ts @@ -2,5 +2,5 @@ export { assertArrayIncludes, assertEquals, assertExists, -} from "https://deno.land/std@0.86.0/testing/asserts.ts"; +} from "https://deno.land/std@0.88.0/testing/asserts.ts"; export * from "../mod.ts"; From 7b482aeed15ea664b7b3cf85b3010cc2ca0ffd52 Mon Sep 17 00:00:00 2001 From: ayntee Date: Fri, 26 Feb 2021 21:18:10 +0400 Subject: [PATCH 002/200] fix(rest/queue): logical error in response status handling (#549) --- src/rest/queue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/queue.ts b/src/rest/queue.ts index 1ef2fd766..beea327ec 100644 --- a/src/rest/queue.ts +++ b/src/rest/queue.ts @@ -62,7 +62,7 @@ export async function processQueue(id: string) { response.headers, ); - if (response.status < 200 && response.status >= 400) { + if (response.status < 200 || response.status >= 400) { restCache.eventHandlers.error( "httpError", queuedRequest.payload, From 8f8f988e2b4883d1745e6a978da7939572d0bb7d Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Fri, 26 Feb 2021 19:11:02 +0100 Subject: [PATCH 003/200] fix(structures/channel): empty guildID in Channel struct (#551) --- src/api/structures/guild.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/structures/guild.ts b/src/api/structures/guild.ts index bffd97546..f1cc711cb 100644 --- a/src/api/structures/guild.ts +++ b/src/api/structures/guild.ts @@ -146,7 +146,7 @@ export async function createGuild(data: CreateGuildPayload, shardID: number) { ); await Promise.all(channels.map(async (channel) => { - const channelStruct = await structures.createChannel(channel); + const channelStruct = await structures.createChannel(channel, rest.id); return cacheHandlers.set("channels", channelStruct.id, channelStruct); })); From 7a52f301b3b3d4348f4889847615c6d2a1f96032 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 27 Feb 2021 23:54:37 +0400 Subject: [PATCH 004/200] fix(util/permissions): move owner check before member cache check (#555) --- src/util/permissions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/permissions.ts b/src/util/permissions.ts index 97a97ac27..8579cb99b 100644 --- a/src/util/permissions.ts +++ b/src/util/permissions.ts @@ -52,15 +52,15 @@ export async function botHasPermission( guildID: string, permissions: Permission[], ) { - const member = await cacheHandlers.get("members", botID); - if (!member) return false; - const guild = await cacheHandlers.get("guilds", guildID); if (!guild) return false; // Check if the bot is the owner of the guild, if it is, returns true if (guild.ownerID === botID) return true; + const member = await cacheHandlers.get("members", botID); + if (!member) return false; + // The everyone role is not in member.roles const permissionBits = [...member.guilds.get(guildID)?.roles || [], guild.id] .map((id) => guild.roles.get(id)!) From a72bf9bb2e2e0c0d97f9ba620509d567aa021798 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 28 Feb 2021 00:05:01 +0400 Subject: [PATCH 005/200] test: remove weird workaround (#556) --- .github/workflows/test.yml | 6 ++---- test/mod.test.ts | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bf456c31..4c31dcf43 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,10 +13,8 @@ jobs: deno-version: ${{ matrix.deno }} - name: Cache dependencies run: deno cache mod.ts - - name: Run local tests - run: TEST_TYPE=local deno test --allow-env - - name: Run API tests + - name: Run test script if: github.ref == 'refs/heads/master' - run: TEST_TYPE=api deno test --allow-net --allow-env + run: deno test --allow-net --allow-env env: DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} diff --git a/test/mod.test.ts b/test/mod.test.ts index 15fca7706..4be57e15c 100644 --- a/test/mod.test.ts +++ b/test/mod.test.ts @@ -36,7 +36,6 @@ import { export const defaultTestOptions: Partial = { sanitizeOps: false, sanitizeResources: false, - ignore: Deno.env.get("TEST_TYPE") !== "api", }; // Temporary data From 56ae257f02c1f83bf4bdf479d314f52b374a3990 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 28 Feb 2021 00:06:45 +0400 Subject: [PATCH 006/200] docs(README): add link to invalid request limit (#552) * docs(README): add link to invalid request limit * style: format README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1759d3482..97d6e7517 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ Discordeno follows [Semantic Versioning](https://semver.org/) - **Secure & stable**: Discordeno is actively maintained to ensure great performance and convenience. Moreover, it internally checks all missing permissions before forwarding a request to the Discord API so that the client - does not get globally-banned by Discord. + does not get + [globally-banned by Discord](https://discord.com/developers/docs/topics/rate-limits#invalid-request-limit). - **Simple, Efficient, & Lightweight**: Discordeno is simplistic, easy-to-use, versatile while being efficient and lightweight. Follows [Convention Over Configuration](https://en.wikipedia.org/wiki/Convention_over_configuration) From 684f827bd728f2b4e123b87fdf2559ac18835a63 Mon Sep 17 00:00:00 2001 From: Dhruvin-Purohit <73269605+Dhruvin-Purohit@users.noreply.github.com> Date: Sun, 28 Feb 2021 14:42:14 +0530 Subject: [PATCH 007/200] docs(README): fix grammatical error (#557) * feat(grammar): fixed a mistake. resources is a plural word but is being used as a singular word here * Update docs/src/README.md Co-authored-by: ayntee --- docs/src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/README.md b/docs/src/README.md index 20afdd20f..fbdc3ec3f 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -54,5 +54,5 @@ resources: - [TypeScript Crash Course by Traversy Media](https://www.youtube.com/watch?v=rAy_3SIqT-E) -There is always more resources... Take your time and don't fret! Come back when +There are always more resources... Take your time and don't fret! Come back when you are ready, we can't wait to see what your Discordeno created bot does! From 04dbdaf2e0b955b72af077d7ec18fd68cbc22c66 Mon Sep 17 00:00:00 2001 From: Dhruvin-Purohit <73269605+Dhruvin-Purohit@users.noreply.github.com> Date: Mon, 1 Mar 2021 00:22:30 +0530 Subject: [PATCH 008/200] docs(faq): fix spelling and grammatical errors (#558) * feat(grammar): Common Mistakes. * Update docs/src/faq.md Co-authored-by: ayntee --- docs/src/faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/faq.md b/docs/src/faq.md index 04ecfcc44..14af40820 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -21,11 +21,11 @@ of it's code! A breaking change in typings is a breaking change for the library! ## How Stable Is Discordeno? -One of the biggest issues with almost every library(I have used) is stability. +One of the biggest issues with almost every library (that I have used) is stability. None of the libraries gave much love and attention to TypeScript developers the way it deserves. Sometimes TypeScript projects would break because breaking changes to typings did not make a MAJOR bump so TypeScript bots in production -would break. Sometimes I was personally maintaing the typings because no one +would break. Sometimes I was personally maintaining the typings because no one else was for that lib. Some libs were pre 1.0 and didn't even have a stable branch/version where I would not have to worry about breaking changes. From 848e3d5ced5a6add042c8948905af165272258e9 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 15:19:09 +0400 Subject: [PATCH 009/200] style(docs/faq): run deno fmt --- docs/src/faq.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/src/faq.md b/docs/src/faq.md index 14af40820..e8bfdf840 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -21,13 +21,14 @@ of it's code! A breaking change in typings is a breaking change for the library! ## How Stable Is Discordeno? -One of the biggest issues with almost every library (that I have used) is stability. -None of the libraries gave much love and attention to TypeScript developers the -way it deserves. Sometimes TypeScript projects would break because breaking -changes to typings did not make a MAJOR bump so TypeScript bots in production -would break. Sometimes I was personally maintaining the typings because no one -else was for that lib. Some libs were pre 1.0 and didn't even have a stable -branch/version where I would not have to worry about breaking changes. +One of the biggest issues with almost every library (that I have used) is +stability. None of the libraries gave much love and attention to TypeScript +developers the way it deserves. Sometimes TypeScript projects would break +because breaking changes to typings did not make a MAJOR bump so TypeScript bots +in production would break. Sometimes I was personally maintaining the typings +because no one else was for that lib. Some libs were pre 1.0 and didn't even +have a stable branch/version where I would not have to worry about breaking +changes. This is why I made it one of my foundational goals of this library to have the best stability for TypeScript developers. No matter how small, a breaking change From 0ec769fad0777848c52016d0db9cf171374aecc2 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 15:33:45 +0400 Subject: [PATCH 010/200] chore: remove 'deno.unstable' from settings.json --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e15e7fd59..088bb67d7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { "deno.enable": true, "deno.lint": true, - "deno.unstable": true, "editor.defaultFormatter": "denoland.vscode-deno", "editor.formatOnSave": true, "editor.codeActionsOnSave": { From 832cb634605449ef56b96a8bd83ece1c781754b5 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 20:56:17 +0400 Subject: [PATCH 011/200] test(main): close websocket instead of Deno.exit() (#560) --- test/mod.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/mod.test.ts b/test/mod.test.ts index 4be57e15c..d28280831 100644 --- a/test/mod.test.ts +++ b/test/mod.test.ts @@ -13,6 +13,7 @@ import { cache, Channel, channelOverwriteHasPermission, + closeWS, createGuildChannel, createGuildRole, createServer, @@ -347,8 +348,9 @@ Deno.test({ // Forcefully exit the Deno process once all tests are done. Deno.test({ - name: "exit the process forcefully after all the tests are done\n", + name: "[main] close the websocket connection", fn() { - Deno.exit(); + closeWS(); }, + ...defaultTestOptions, }); From 25d7983ebab789e860aa7496581736a2d0b38794 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 21:26:53 +0400 Subject: [PATCH 012/200] chore: add vscode-related files to .gitignore (#561) --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f82cdf33a..33cbff9b6 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,10 @@ public/ .idea/ # Windows -desktop.ini \ No newline at end of file +desktop.ini + +# Visual Studio Code +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +*.code-workspace From b021d3a21ce85071ad58e20ea52185bd36517ce1 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 21:28:29 +0400 Subject: [PATCH 013/200] chore: enable deno and deno lint in container --- .devcontainer/devcontainer.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c382addc8..77746a261 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,15 @@ // Set *default* container specific settings.json values on container create. "settings": { - "terminal.integrated.shell.linux": "/bin/bash" + "terminal.integrated.shell.linux": "/bin/bash", + "deno.enable": true, + "deno.lint": true, + "editor.defaultFormatter": "denoland.vscode-deno", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + "source.fixAll": true + } }, // Add the IDs of extensions you want installed when the container is created. @@ -20,4 +28,4 @@ // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" -} \ No newline at end of file +} From 585ed283fefcdbce3274444e8ce315a8e39313b3 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 1 Mar 2021 21:57:59 +0400 Subject: [PATCH 014/200] test(main): remove auto-deletion of guilds --- test/mod.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/mod.test.ts b/test/mod.test.ts index d28280831..9ba38479a 100644 --- a/test/mod.test.ts +++ b/test/mod.test.ts @@ -59,14 +59,6 @@ Deno.test({ intents: ["GUILD_MESSAGES", "GUILDS"], }); - eventHandlers.ready = () => { - if (cache.guilds.size >= 10) { - cache.guilds.map((guild) => - guild.ownerID === botID && deleteServer(guild.id) - ); - } - }; - // Delay the execution by 5 seconds await delay(5000); From f1473224cca4bb15a30205fe64b759e61835ff62 Mon Sep 17 00:00:00 2001 From: ayntee Date: Tue, 2 Mar 2021 00:09:00 +0400 Subject: [PATCH 015/200] docs(README): add table of contents section --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 97d6e7517..ef5bd5094 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Discordeno follows [Semantic Versioning](https://semver.org/) ![Lint](https://github.com/discordeno/discordeno/workflows/Lint/badge.svg) ![Test](https://github.com/discordeno/discordeno/workflows/Test/badge.svg) +## Table of contents + +- [Features](#features) +- [Getting Started](#getting-started) + - [Minimal Example](#minimal-example) + - [Boilerplates](#boilerplates) +- [Links](#links) +- [Contributing](#contributing) + ## Features - **Secure & stable**: Discordeno is actively maintained to ensure great @@ -65,7 +74,7 @@ unofficial boilerplates: (official)](https://github.com/discordeno/slash-commands-boilerplate) - [Add Your Own!](https://github.com/discordeno/discordeno/pulls) -## Useful Links +## Links - [Website](https://discordeno.mod.land) - [Documentation](https://doc.deno.land/https/deno.land/x/discordeno/mod.ts) @@ -75,7 +84,3 @@ unofficial boilerplates: We appreciate your help! Before contributing, please read the [Contributing Guide](https://github.com/discordeno/discordeno/blob/master/.github/CONTRIBUTING.md). - -### License - -[License can be found here](https://github.com/discordeno/discordeno/blob/master/LICENSE) From 16ba155f33addc97c1138e551e8643671967853d Mon Sep 17 00:00:00 2001 From: ayntee Date: Wed, 3 Mar 2021 14:07:57 +0400 Subject: [PATCH 016/200] style: format json files (#563) --- .devcontainer/devcontainer.json | 49 +++++++++++++-------------- .vscode/settings.json | 16 ++++----- docs/src/stepbystep/createlanguage.md | 14 ++++---- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 77746a261..77b63f18d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,31 +1,30 @@ { - "name": "Deno", - "dockerFile": "Dockerfile", + "name": "Deno", + "dockerFile": "Dockerfile", - // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.shell.linux": "/bin/bash", - "deno.enable": true, - "deno.lint": true, - "editor.defaultFormatter": "denoland.vscode-deno", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true, - "source.fixAll": true - } - }, + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "deno.enable": true, + "deno.lint": true, + "editor.defaultFormatter": "denoland.vscode-deno", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + "source.fixAll": true + } + }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "denoland.vscode-deno" - ], + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "denoland.vscode-deno" + ] + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], + // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. + // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], - // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. - // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], - - // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "vscode" + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + // "remoteUser": "vscode" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 088bb67d7..35b46c0ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,10 @@ { - "deno.enable": true, - "deno.lint": true, - "editor.defaultFormatter": "denoland.vscode-deno", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true, - "source.fixAll": true - } + "deno.enable": true, + "deno.lint": true, + "editor.defaultFormatter": "denoland.vscode-deno", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + "source.fixAll": true + } } diff --git a/docs/src/stepbystep/createlanguage.md b/docs/src/stepbystep/createlanguage.md index 539f84752..ceb1e05e5 100644 --- a/docs/src/stepbystep/createlanguage.md +++ b/docs/src/stepbystep/createlanguage.md @@ -45,7 +45,7 @@ translations work properly now. ```json { - "DESCRIPTION": "Hug yourself or another user." + "DESCRIPTION": "Hug yourself or another user." } ``` @@ -58,9 +58,9 @@ add those in. ```json { - "DESCRIPTION": "Hug yourself or another user.", - "SELF": "If you had no one to hug you could have hugged me. Years from now, when you're thinking about me, you're gonna say: 'How did I ever get along without that wonderful, constant companion?' *Woof.*", - "OTHER": "{{user}} was hugged by {{mention}}" + "DESCRIPTION": "Hug yourself or another user.", + "SELF": "If you had no one to hug you could have hugged me. Years from now, when you're thinking about me, you're gonna say: 'How did I ever get along without that wonderful, constant companion?' *Woof.*", + "OTHER": "{{user}} was hugged by {{mention}}" } ``` @@ -127,9 +127,9 @@ example of different languages. ```json { - "DESCRIPTION": "Abrázate a ti mismo oa otro usuario", - "SELF": "Si no tuvieras a nadie a quien abrazar, podrías haberme abrazado. Años a partir de ahora, cuando estés pensando en mí, dirás: '¿Cómo me las arreglé sin esa maravillosa y constante compañera?' *Guau.*", - "OTHER": "{{user}} fue abrazado por {{mention}}" + "DESCRIPTION": "Abrázate a ti mismo oa otro usuario", + "SELF": "Si no tuvieras a nadie a quien abrazar, podrías haberme abrazado. Años a partir de ahora, cuando estés pensando en mí, dirás: '¿Cómo me las arreglé sin esa maravillosa y constante compañera?' *Guau.*", + "OTHER": "{{user}} fue abrazado por {{mention}}" } ``` From c6ce5f7269a13b10880ce220e72f7c587700e285 Mon Sep 17 00:00:00 2001 From: ayntee Date: Thu, 4 Mar 2021 09:30:27 -0500 Subject: [PATCH 017/200] test: set sanitizeExit to false --- test/mod.test.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/mod.test.ts b/test/mod.test.ts index 9ba38479a..4cc7aab0b 100644 --- a/test/mod.test.ts +++ b/test/mod.test.ts @@ -1,10 +1,3 @@ -import { - deleteChannel, - deleteRole, - deleteServer, - getChannel, -} from "../src/api/handlers/guild.ts"; -import { eventHandlers } from "../src/bot.ts"; import { addReaction, assertEquals, @@ -18,9 +11,13 @@ import { createGuildRole, createServer, delay, + deleteChannel, deleteMessageByID, + deleteRole, + deleteServer, editChannel, editRole, + getChannel, getMessage, getPins, Guild, @@ -37,6 +34,7 @@ import { export const defaultTestOptions: Partial = { sanitizeOps: false, sanitizeResources: false, + sanitizeExit: false, }; // Temporary data @@ -340,9 +338,9 @@ Deno.test({ // Forcefully exit the Deno process once all tests are done. Deno.test({ - name: "[main] close the websocket connection", + name: "[main] exit the process forcefully", fn() { - closeWS(); + Deno.exit(); }, ...defaultTestOptions, }); From e10b83536793b6b75320dfa071be3ad74fcdaab6 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 6 Mar 2021 08:48:29 +0100 Subject: [PATCH 018/200] feat(rest/server): support TLS protocol (#562) * add keys option * add serveTLS * Update src/rest/types/server.ts * Update src/rest/types/server.ts * rename the keys * forgot that one * Update src/rest/types/server.ts * some renaming * Update src/rest/types/server.ts * Update src/rest/server.ts Co-authored-by: ayntee --- src/rest/server.ts | 10 ++++++++-- src/rest/types/server.ts | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/rest/server.ts b/src/rest/server.ts index aa53a9f47..fbff1b460 100644 --- a/src/rest/server.ts +++ b/src/rest/server.ts @@ -1,12 +1,18 @@ // SERVERLESS REST CLIENT THAT CAN WORK ACROSS SHARDS/WORKERS TO COMMUNICATE GLOBAL RATE LIMITS EASILY import { restCache } from "./cache.ts"; -import { serve, ServerRequest } from "./deps.ts"; +import { serve, ServerRequest, serveTLS } from "./deps.ts"; import { processRequest } from "./request.ts"; import { RestServerOptions } from "./types/mod.ts"; /** Begins an http server that will handle incoming requests. */ export async function startRESTServer(options: RestServerOptions) { - const server = serve({ port: options.port }); + const server = options.keys + ? serveTLS({ + port: options.port, + certFile: options.keys.cert, + keyFile: options.keys.key, + }) + : serve({ port: options.port }); for await (const request of server) { handlePayload(request, options).catch((error) => { diff --git a/src/rest/types/server.ts b/src/rest/types/server.ts index bf3881160..470d22038 100644 --- a/src/rest/types/server.ts +++ b/src/rest/types/server.ts @@ -11,6 +11,13 @@ export interface RestServerOptions { maxRetryCount: number; /** The api version you would like to use */ apiVersion?: number; + /** The TLS keys you would like to use */ + keys?: { + /** Path of the server certificate file (public key) */ + cert: string; + /** Path of the server's private key file. */ + key: string; + }; } export interface RestEventHandlers { From 565b9e14721a99735ec1d7b14190158d07ad441a Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 6 Mar 2021 11:48:48 +0400 Subject: [PATCH 019/200] docs(structures/member): update description for Member#guildMember() (#565) --- src/api/structures/member.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/structures/member.ts b/src/api/structures/member.ts index b46c41f47..3b385172f 100644 --- a/src/api/structures/member.ts +++ b/src/api/structures/member.ts @@ -171,7 +171,7 @@ export interface Member { guild(guildID: string): Guild | undefined; /** Get the nickname or the username if no nickname */ name(guildID: string): string; - /** Get the nickname */ + /** Get the guild member object for the specified guild */ guildMember(guildID: string): GuildMember | undefined; /** Send a direct message to the user is possible */ sendDM( From 65ee27556aa4200cbc5e3166d6d3eb78f9bc1165 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 6 Mar 2021 19:29:46 +0400 Subject: [PATCH 020/200] docs(README): remove hard-coded prefix from usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef5bd5094..27dae2dc8 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ startBot({ console.log("Successfully connected to gateway"); }, messageCreate(message) { - if (message.content === "!ping") { + if (message.content === "ping") { message.reply("Pong using Discordeno!"); } }, From 67e6a41e99f37cd5af3bb8a5d7dd30aa4151987c Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 6 Mar 2021 13:06:11 -0500 Subject: [PATCH 021/200] feat(types/auditlog): add missing props to DiscordAuditLogChange interface (#600) --- src/types/api/auditlog.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/types/api/auditlog.ts b/src/types/api/auditlog.ts index a2047f362..bb9ed0230 100644 --- a/src/types/api/auditlog.ts +++ b/src/types/api/auditlog.ts @@ -97,7 +97,7 @@ export interface DiscordOptionalAuditEntryInfoParam { /** https://discord.com/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-structure */ export interface DiscordAuditLogChange { - /** new value of the key */ + /** new value of the key. If not present, while old_value is, that means the property that was changed has been reset */ new_value?: DiscordAuditLogChangeValue; /** old value of the key */ old_value?: DiscordAuditLogChangeValue; @@ -112,6 +112,12 @@ export type DiscordAuditLogChangeValue = old_value: string; key: | "name" + | "description" + | "discovery_splash_hash" + | "banner_hash" + | "preferred_locale" + | "rules_channel_id" + | "public_updates_channel_id" | "icon_hash" | "splash_hash" | "owner_id" @@ -150,7 +156,8 @@ export type DiscordAuditLogChangeValue = | "uses" | "max_age" | "expire_behavior" - | "expire_grace_period"; + | "expire_grace_period" + | "user_limit"; } | { new_value: Partial; From 5802a9d1ca0a2660424301e0f04a6209e15eafe8 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 6 Mar 2021 22:16:02 +0400 Subject: [PATCH 022/200] fix(types/interactions): make "content" optional in SlashCommandCallbackData (#599) * fix(types/interactions): make "content" optional in SlashCommandCallbackData' Closes #590 * ignore lint --- .github/workflows/lint.yml | 2 +- src/interactions/types/interactions.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 81b10b6db..4f69d018d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,4 +9,4 @@ jobs: - name: Run fmt check script run: deno fmt --check - name: Run lint script - run: deno lint src/** test/** --unstable --ignore=./src/types/ + run: deno lint src/** test/** --unstable --ignore=./src/types,./src/interactions/types diff --git a/src/interactions/types/interactions.ts b/src/interactions/types/interactions.ts index 87e36f8a9..295015e34 100644 --- a/src/interactions/types/interactions.ts +++ b/src/interactions/types/interactions.ts @@ -49,11 +49,11 @@ export interface SlashCommandCallbackData { /** is the response TTS */ tts?: boolean; /** message content */ - content: string; + content?: string; /** supports up to 10 embeds */ embeds?: Embed[]; /** allowed mentions for the message */ - "allowed_mentions"?: AllowedMentions; + allowed_mentions?: AllowedMentions; /** acceptable values are message flags */ flags?: number; } From d8f3674779efeeab3a51d7bde18d1ec4daa47979 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 6 Mar 2021 22:17:04 +0400 Subject: [PATCH 023/200] fix(handlers/webhook): do not add Date.now() to 900000 ms in executeSlashCommand() (#601) Related #589 --- src/api/handlers/webhook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 72e8a1855..fdad0ec4b 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -461,7 +461,7 @@ export async function executeSlashCommand( cache.executedSlashCommands.set(token, id); setTimeout( () => cache.executedSlashCommands.delete(token), - Date.now() + 900000, + 900000, ); // If no mentions are provided, force disable mentions From 33ff8cd752a6a448e8bce129a1e2a847bd21c265 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sat, 6 Mar 2021 22:19:11 +0400 Subject: [PATCH 024/200] feat(types/permission): add USE_SLASH_COMMANDS permission (#595) --- src/types/permission.ts | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/src/types/permission.ts b/src/types/permission.ts index 7c2a896ba..cd8006cb2 100644 --- a/src/types/permission.ts +++ b/src/types/permission.ts @@ -1,34 +1,4 @@ -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 = keyof typeof Permissions; export enum Permissions { CREATE_INSTANT_INVITE = 0x00000001, @@ -61,4 +31,5 @@ export enum Permissions { MANAGE_ROLES = 0x10000000, MANAGE_WEBHOOKS = 0x20000000, MANAGE_EMOJIS = 0x40000000, + USE_SLASH_COMMANDS = 0x80000000, } From e5bf5cd47cdfc61fd29c95899fae866d22f48144 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 6 Mar 2021 13:59:45 -0500 Subject: [PATCH 025/200] feat(handlers/webhook): support ephemeral messages (#602) Co-authored-by: ayntee --- src/api/handlers/webhook.ts | 9 +++++++-- src/types/webhook.ts | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index fdad0ec4b..74fda402b 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -6,10 +6,10 @@ import { EditSlashResponseOptions, EditWebhookMessageOptions, Errors, - ExecuteSlashCommandOptions, ExecuteWebhookOptions, MessageCreateOptions, SlashCommand, + SlashCommandResponseOptions, UpsertSlashCommandOptions, UpsertSlashCommandsOptions, WebhookCreateOptions, @@ -448,7 +448,7 @@ export function deleteSlashCommand(id: string, guildID?: string) { export async function executeSlashCommand( id: string, token: string, - options: ExecuteSlashCommandOptions, + options: SlashCommandResponseOptions, ) { // If its already been executed, we need to send a followup response if (cache.executedSlashCommands.has(token)) { @@ -464,6 +464,11 @@ export async function executeSlashCommand( 900000, ); + // If the user wants this as a private message mark it ephemeral + if (options.private) { + options.data.flags = 64; + } + // If no mentions are provided, force disable mentions if (!(options.data.allowed_mentions)) { options.data.allowed_mentions = { parse: [] }; diff --git a/src/types/webhook.ts b/src/types/webhook.ts index d5a680785..225034a86 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -191,7 +191,7 @@ export interface SlashCommandCallbackData { embeds?: Embed[]; /** allowed mentions for the message */ "allowed_mentions"?: AllowedMentions; - /** acceptable values are message flags */ + /** acceptable values are message flags, set to 64 to make your response ephemeral */ flags?: number; } @@ -224,6 +224,12 @@ export interface ExecuteSlashCommandOptions { data: SlashCommandCallbackData; } +export interface SlashCommandResponseOptions + extends ExecuteSlashCommandOptions { + /** Whether to make this response visible ONLY to the user who used this command. It will also be deleted after some time. */ + private?: boolean; +} + export interface EditSlashResponseOptions extends SlashCommandCallbackData { /** If this is not provided, it will default to editing the original response. */ messageID?: string; From d084c3f3c4d1399640e023f225e825399854074e Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 6 Mar 2021 14:44:31 -0500 Subject: [PATCH 026/200] fix(handlers/channel): handle max_age and max_uses limit in createInvite() (#597) * fix: handle invalid invite create limits * refactor: cleanup conditonals * chore: clarification Co-authored-by: ayntee --- src/api/handlers/channel.ts | 14 ++++++++++++++ src/types/channel.ts | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/api/handlers/channel.ts b/src/api/handlers/channel.ts index e22b410ca..5e1b772d1 100644 --- a/src/api/handlers/channel.ts +++ b/src/api/handlers/channel.ts @@ -321,6 +321,20 @@ export async function createInvite( throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE); } + if (options.max_age && (options.max_age > 604800 || options.max_age < 0)) { + console.log( + `The max age for invite created in ${channelID} was not between 0-604800. Using default values instead.`, + ); + options.max_age = undefined; + } + + if (options.max_uses && (options.max_uses > 100 || options.max_uses < 0)) { + console.log( + `The max uses for invite created in ${channelID} was not between 0-100. Using default values instead.`, + ); + options.max_uses = undefined; + } + const result = await RequestManager.post( endpoints.CHANNEL_INVITES(channelID), options, diff --git a/src/types/channel.ts b/src/types/channel.ts index ad3202a95..3743c2ebc 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -154,11 +154,12 @@ export interface GetMessagesAround extends GetMessages { around: string; } +// TODO: v11 change to camelcase export interface CreateInviteOptions { - /** Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400 (24 hours) */ - "max_age": number; - /** Max number of uses or 0 for unlimited. Default 0 */ - "max_uses": number; + /** Duration of invite in seconds before expiry, or 0 for never. Between 0-604800 (7 days). Defaults to 86400 (24 hours). */ + "max_age"?: number; + /** Max number of uses or 0 for unlimited. Between 0-100. Default 0 */ + "max_uses"?: number; /** Whether this invite only grants temporary membership. */ temporary: boolean; /** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */ From c319ca66cdeaaa28c48bd3d4c8e4472d8acfef54 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 08:55:18 +0100 Subject: [PATCH 027/200] feat(handlers/message): add options to getReactions (#607) --- src/api/handlers/message.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/api/handlers/message.ts b/src/api/handlers/message.ts index 5cd28d190..24c6579bb 100644 --- a/src/api/handlers/message.ts +++ b/src/api/handlers/message.ts @@ -1,6 +1,7 @@ import { botID } from "../../bot.ts"; import { RequestManager } from "../../rest/request_manager.ts"; import { + DiscordGetReactionsParams, Errors, MessageContent, MessageCreateOptions, @@ -264,9 +265,14 @@ export async function removeReactionEmoji( } /** Get a list of users that reacted with this emoji. */ -export async function getReactions(message: Message, reaction: string) { +export async function getReactions( + message: Message, + reaction: string, + options?: DiscordGetReactionsParams, +) { const result = (await RequestManager.get( endpoints.CHANNEL_MESSAGE_REACTION(message.channelID, message.id, reaction), + options, )) as UserPayload[]; return Promise.all(result.map(async (res) => { From 1c5c90834a6012641244eef15354bd817ef396a9 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 7 Mar 2021 11:59:22 +0400 Subject: [PATCH 028/200] Create CODEOWNERS --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..070268509 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +* @ayntee @Skillz4Killz + +*.ts @ayntee @Skillz4Killz @itohatweb From 9c7340f6240841f69db7b5132b4759a977ec48ce Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sun, 7 Mar 2021 09:39:19 -0500 Subject: [PATCH 029/200] fix(handlers/webhook): return Message object instead of raw payload (#611) * fix: change any to message types * Update webhook.ts * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> Co-authored-by: ayntee Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/api/handlers/webhook.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 74fda402b..8533ab256 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -252,9 +252,10 @@ export async function editWebhookMessage( const result = await RequestManager.patch( endpoints.WEBHOOK_MESSAGE(webhookID, webhookToken, messageID), { ...options, allowed_mentions: options.allowed_mentions }, - ); + ) as MessageCreateOptions; - return result; + const message = await structures.createMessage(result); + return message; } export async function deleteWebhookMessage( @@ -552,5 +553,11 @@ export async function editSlashResponse( options, ); - return result; + // If the original message was edited, this will not return a message + if (!options.messageID) return result; + + const message = await structures.createMessage( + result as MessageCreateOptions, + ); + return message; } From 30470d8551125f6826cc7ecb19066de64999d630 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 15:45:53 +0100 Subject: [PATCH 030/200] update types --- src/types/webhook.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/webhook.ts b/src/types/webhook.ts index 225034a86..573b0117a 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -237,20 +237,20 @@ export interface EditSlashResponseOptions extends SlashCommandCallbackData { export interface UpsertSlashCommandOptions { /** 3-32 character command name */ - name: string; + name?: string; /** 1-100 character description */ - description: string; + description?: string; /** The parameters for the command */ - options?: SlashCommandOption[]; + options?: SlashCommandOption[] | null; } export interface UpsertSlashCommandsOptions { /** The id of the command */ id: string; /** 3-32 character command name */ - name: string; + name?: string; /** 1-100 character description */ - description: string; + description?: string; /** The parameters for the command */ - options?: SlashCommandOption[]; + options?: SlashCommandOption[] | null; } From b095b995ec6a4833ece60ed4e7cbda0d088c5cdd Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 15:46:08 +0100 Subject: [PATCH 031/200] fix upsertSlashCommand & upsertSlashCommands --- src/api/handlers/webhook.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 74fda402b..4f3eef451 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -336,12 +336,17 @@ export async function upsertSlashCommand( guildID?: string, ) { // Use ... for content length due to unicode characters and js .length handling - if ([...options.name].length < 2 || [...options.name].length > 32) { + if ( + options.name && + ([...options.name].length < 2 || [...options.name].length > 32) + ) { throw new Error(Errors.INVALID_SLASH_NAME); } if ( - [...options.description].length < 1 || [...options.description].length > 100 + options.description && + ([...options.description].length < 1 || + [...options.description].length > 100) ) { throw new Error(Errors.INVALID_SLASH_DESCRIPTION); } @@ -371,12 +376,17 @@ export async function upsertSlashCommands( ) { const data = options.map((option) => { // Use ... for content length due to unicode characters and js .length handling - if ([...option.name].length < 2 || [...option.name].length > 32) { + if ( + option.name && + ([...option.name].length < 2 || [...option.name].length > 32) + ) { throw new Error(Errors.INVALID_SLASH_NAME); } if ( - [...option.description].length < 1 || [...option.description].length > 100 + option.description && + ([...option.description].length < 1 || + [...option.description].length > 100) ) { throw new Error(Errors.INVALID_SLASH_DESCRIPTION); } From 4af6f363f88eb2c2d20aa51aa7d636eeea183457 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 7 Mar 2021 18:48:12 +0400 Subject: [PATCH 032/200] feat(handlers/webhook): test slash command name against ^[\w-]{1,32}$ (#613) * feat(handlers/webhook): test slash command name against ^[\w-]{1,32}$ * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> * move to constants file * idk * idk Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/api/handlers/webhook.ts | 14 +++++--------- src/util/constants.ts | 2 ++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 8533ab256..bc181def7 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -17,7 +17,7 @@ import { WebhookPayload, } from "../../types/mod.ts"; import { cache } from "../../util/cache.ts"; -import { endpoints } from "../../util/constants.ts"; +import { endpoints, SLASH_COMMANDS_NAME_REGEX } from "../../util/constants.ts"; import { botHasChannelPermissions } from "../../util/permissions.ts"; import { urlToBase64 } from "../../util/utils.ts"; import { structures } from "../structures/mod.ts"; @@ -282,8 +282,7 @@ export async function deleteWebhookMessage( * Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. */ export async function createSlashCommand(options: CreateSlashCommandOptions) { - // Use ... for content length due to unicode characters and js .length handling - if ([...options.name].length < 2 || [...options.name].length > 32) { + if (!SLASH_COMMANDS_NAME_REGEX.test(options.name)) { throw new Error(Errors.INVALID_SLASH_NAME); } @@ -336,8 +335,7 @@ export async function upsertSlashCommand( options: UpsertSlashCommandOptions, guildID?: string, ) { - // Use ... for content length due to unicode characters and js .length handling - if ([...options.name].length < 2 || [...options.name].length > 32) { + if (!SLASH_COMMANDS_NAME_REGEX.test(options.name)) { throw new Error(Errors.INVALID_SLASH_NAME); } @@ -371,8 +369,7 @@ export async function upsertSlashCommands( guildID?: string, ) { const data = options.map((option) => { - // Use ... for content length due to unicode characters and js .length handling - if ([...option.name].length < 2 || [...option.name].length > 32) { + if (!SLASH_COMMANDS_NAME_REGEX.test(option.name)) { throw new Error(Errors.INVALID_SLASH_NAME); } @@ -405,8 +402,7 @@ export async function editSlashCommand( options: EditSlashCommandOptions, guildID?: string, ) { - // Use ... for content length due to unicode characters and js .length handling - if ([...options.name].length < 2 || [...options.name].length > 32) { + if (!SLASH_COMMANDS_NAME_REGEX.test(options.name)) { throw new Error(Errors.INVALID_SLASH_NAME); } diff --git a/src/util/constants.ts b/src/util/constants.ts index afad4433c..af1d8ef57 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -175,3 +175,5 @@ export const endpoints = { // oAuth2 OAUTH2_APPLICATION: `${baseEndpoints.BASE_URL}/oauth2/applications/@me`, }; + +export const SLASH_COMMANDS_NAME_REGEX = /^[\w-]{1,32}$/; From 2865f42f724b3cfa070095ef5693d00dddd11a67 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 15:51:48 +0100 Subject: [PATCH 033/200] feat(types/embed): add proxy_url to EmbedVideo interface (#616) Co-authored-by: ayntee --- src/types/api/embed.ts | 2 ++ src/types/message.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/types/api/embed.ts b/src/types/api/embed.ts index 2188c3c11..0535d4516 100644 --- a/src/types/api/embed.ts +++ b/src/types/api/embed.ts @@ -56,6 +56,8 @@ export interface DiscordEmbedThumbnail { export interface DiscordEmbedVideo { /** source url of video */ url?: string; + /** a proxied url of the video */ + proxy_url?: string; /** height of video */ height?: number; /** width of video */ diff --git a/src/types/message.ts b/src/types/message.ts index 24f036a95..86f0e4d63 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -99,6 +99,8 @@ export interface EmbedThumbnail { export interface EmbedVideo { /** The source url of video */ url?: string; + /** a proxied url of the video */ + proxy_url?: string; /** The height of the video */ height?: number; /** The width of the video */ From 25858b314433d98241be50018c68ce880df0de32 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 15:51:59 +0100 Subject: [PATCH 034/200] feat(util): add camelToSnakeCase() & snakeToCamelCase() (#615) * feat(util): add to camel/snake case functions * add tests * hmm * add lint ignore comment Co-authored-by: ayntee --- src/util/utils.ts | 54 ++++++++++++++++++++++++++++++++++ test/utils.test.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 test/utils.test.ts diff --git a/src/util/utils.ts b/src/util/utils.ts index a2febf9b3..1b2bb9b1f 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -60,3 +60,57 @@ export const formatImageURL = ( return `${url}.${format || (url.includes("/a_") ? "gif" : "jpg")}?size=${size}`; }; + +function camelToSnakeCase(text: string) { + return text.replace(/ID|[A-Z]/g, ($1) => { + if ($1 === "ID") return "_id"; + return `_${$1.toLowerCase()}`; + }); +} + +function snakeToCamelCase(text: string) { + return text.replace(/_id|([-_][a-z])/ig, ($1) => { + if ($1 === "_id") return "ID"; + return $1.toUpperCase().replace("_", ""); + }); +} + +function isObject(obj: unknown) { + return obj === Object(obj) && !Array.isArray(obj) && + typeof obj !== "function"; +} +// deno-lint-ignore no-explicit-any +export function camelKeysToSnakeCase(obj: Record) { + if (isObject(obj)) { + // deno-lint-ignore no-explicit-any + const convertedObject: Record = {}; + Object.keys(obj) + .forEach((key) => { + convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase( + obj[key], + ); + }); + return convertedObject; + } else if (Array.isArray(obj)) { + obj = obj.map((element) => camelKeysToSnakeCase(element)); + } + return obj; +} + +// deno-lint-ignore no-explicit-any +export function snakeKeysToCamelCase(obj: Record) { + if (isObject(obj)) { + // deno-lint-ignore no-explicit-any + const convertedObject: Record = {}; + Object.keys(obj) + .forEach((key) => { + convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase( + obj[key], + ); + }); + return convertedObject; + } else if (Array.isArray(obj)) { + obj = obj.map((element) => snakeKeysToCamelCase(element)); + } + return obj; +} diff --git a/test/utils.test.ts b/test/utils.test.ts new file mode 100644 index 000000000..d2df243c3 --- /dev/null +++ b/test/utils.test.ts @@ -0,0 +1,73 @@ +import { camelKeysToSnakeCase, snakeKeysToCamelCase } from "../mod.ts"; +import { assertEquals } from "./deps.ts"; + +const testSnakeObject = { + // deno-lint-ignore camelcase + hello_world: "hello_world", + // deno-lint-ignore camelcase + the_universe: { + blue_planet: { + water: "is_blue", + dirt: "isDirty", + }, + moon: { + earth_moon: { + is_round: true, + }, + other_moon: { + is_round: 0, + }, + }, + arrays: ["one_two", { moo_cow: { boo: true } }], + test_the_id: "123123123123", + }, +}; + +const testCamelObject = { + helloWorld: "hello_world", + theUniverse: { + bluePlanet: { + water: "is_blue", + dirt: "isDirty", + }, + moon: { + earthMoon: { + isRound: true, + }, + otherMoon: { + isRound: 0, + }, + }, + arrays: ["one_two", { mooCow: { boo: true } }], + testTheID: "123123123123", + }, +}; + +const someOther = { + helloWorld: 1, +}; + +const someElseOther = { + // deno-lint-ignore camelcase + hello_world: 1, +}; + +Deno.test({ + name: "[utils] snakeKeysToCamelCase: assert convertion", + fn() { + const result = snakeKeysToCamelCase(testSnakeObject); + assertEquals(result, testCamelObject); + const resultTwo = snakeKeysToCamelCase(someOther); + assertEquals(resultTwo, someOther); + }, +}); + +Deno.test({ + name: "[utils] camelKeysToSnakeCase: assert convertion", + fn() { + const result = camelKeysToSnakeCase(testCamelObject); + assertEquals(result, testSnakeObject); + const resultTwo = camelKeysToSnakeCase(someElseOther); + assertEquals(resultTwo, someElseOther); + }, +}); From 4e00c31545362ea6a06ebfdc4c6463eadd588c7b Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 16:00:50 +0100 Subject: [PATCH 035/200] add extends --- src/types/webhook.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/types/webhook.ts b/src/types/webhook.ts index 573b0117a..754d682c3 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -244,13 +244,7 @@ export interface UpsertSlashCommandOptions { options?: SlashCommandOption[] | null; } -export interface UpsertSlashCommandsOptions { +export interface UpsertSlashCommandsOptions extends UpsertSlashCommandOptions { /** The id of the command */ id: string; - /** 3-32 character command name */ - name?: string; - /** 1-100 character description */ - description?: string; - /** The parameters for the command */ - options?: SlashCommandOption[] | null; } From dce5f6f90e008940c42109e502606426c92df86c Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 16:02:10 +0100 Subject: [PATCH 036/200] update jsdoc --- src/types/webhook.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/webhook.ts b/src/types/webhook.ts index 754d682c3..c0ff25748 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -236,11 +236,11 @@ export interface EditSlashResponseOptions extends SlashCommandCallbackData { } export interface UpsertSlashCommandOptions { - /** 3-32 character command name */ + /** 1-32 character name matching ^[\w-]{1,32}$ */ name?: string; /** 1-100 character description */ description?: string; - /** The parameters for the command */ + /** the parameters for the command */ options?: SlashCommandOption[] | null; } From b0ebd5627d9697a7970ab356d147b22236fd28e3 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 7 Mar 2021 19:02:30 +0400 Subject: [PATCH 037/200] Update src/types/webhook.ts --- src/types/webhook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/webhook.ts b/src/types/webhook.ts index c0ff25748..bc52309f9 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -240,7 +240,7 @@ export interface UpsertSlashCommandOptions { name?: string; /** 1-100 character description */ description?: string; - /** the parameters for the command */ + /** The parameters for the command */ options?: SlashCommandOption[] | null; } From b511a0d39ff1f0126c969c7e14b95beb598c2e38 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 16:21:58 +0100 Subject: [PATCH 038/200] fix(types/webhook): update interaction response types (#610) * add the changes from dc * Update src/types/webhook.ts * Update src/interactions/types/interactions.ts Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Co-authored-by: ayntee --- src/interactions/types/interactions.ts | 10 +++------- src/types/webhook.ts | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/interactions/types/interactions.ts b/src/interactions/types/interactions.ts index 295015e34..e9d291a3c 100644 --- a/src/interactions/types/interactions.ts +++ b/src/interactions/types/interactions.ts @@ -66,12 +66,8 @@ export enum InteractionType { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** ACK a command without sending a message, eating the user's input */ - ACKNOWLEDGE = 2, - /** respond with a message, eating the user's input */ - CHANNEL_MESSAGE = 3, - /** respond with a message, showing the user's input */ + /** Respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK a command without sending a message, showing the user's input */ - ACK_WITH_SOURCE = 5, + /** ACK an interaction and edit to a response later, the user sees a loading state */ + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, } diff --git a/src/types/webhook.ts b/src/types/webhook.ts index bc52309f9..925632135 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -198,14 +198,10 @@ export interface SlashCommandCallbackData { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** ACK a command without sending a message, eating the user's input */ - ACKNOWLEDGE = 2, - /** respond with a message, eating the user's input */ - CHANNEL_MESSAGE = 3, - /** respond with a message, showing the user's input */ + /** Respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK a command without sending a message, showing the user's input */ - ACK_WITH_SOURCE = 5, + /** ACK an interaction and edit to a response later, the user sees a loading state */ + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, } // TODO: remove this interface for v11 From bbe3c636f11b081bcbde355acbe8f100d828db7e Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 16:32:15 +0100 Subject: [PATCH 039/200] feat(types/interaction): add "resolved" field to DiscordInteractionCommand (#603) * fix(types): DiscordMember not extending DiscordBaseMember * add(types): DiscordInteractionDataResolved * add(types): resolved field to DiscordInteractionData * fix(types): DiscordInteractionData options is optional * Apply suggestions from code review Co-authored-by: ayntee Co-authored-by: ayntee --- src/types/api/interaction.ts | 25 +++++++++++++++++++++++-- src/types/api/member.ts | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/types/api/interaction.ts b/src/types/api/interaction.ts index d9cb62f7c..993409fc9 100644 --- a/src/types/api/interaction.ts +++ b/src/types/api/interaction.ts @@ -1,4 +1,9 @@ -import { DiscordMember } from "./mod.ts"; +import { + DiscordChannel, + DiscordMember, + DiscordRole, + DiscordUser, +} from "./mod.ts"; export interface DiscordInteractionCommand { /** id of the interaction */ @@ -31,8 +36,24 @@ export interface DiscordInteractionData { id: string; /** the name of the invoked command */ name: string; + /** converted users + roles + channels */ + resolved?: DiscordApplicationCommandInteractionDataResolved; /** the params + values from the user */ - options: DiscordInteractionDataOption[]; + options?: DiscordInteractionDataOption[]; +} + +export interface DiscordApplicationCommandInteractionDataResolved { + /** the IDs and User objects */ + users?: Record; + /** the IDs and partial Member objects */ + members?: Record>; + /** the IDs and Role objects */ + roles?: Record; + /** the IDs and partial Channel objects */ + channels?: Record< + string, + Pick + >; } export interface DiscordInteractionDataOption { diff --git a/src/types/api/member.ts b/src/types/api/member.ts index e17403cb0..3eaf32740 100644 --- a/src/types/api/member.ts +++ b/src/types/api/member.ts @@ -83,7 +83,7 @@ export interface DiscordBaseMember { } /** https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-structure */ -export interface DiscordMember { +export interface DiscordMember extends DiscordBaseMember { /** the user this guild member represents */ user?: DiscordUser; } From e21a86d497668d9952d9c5fb8f5fa42d8734f675 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sun, 7 Mar 2021 17:28:23 +0100 Subject: [PATCH 040/200] fix(handlers/webhook): handle slash commands limitations (#618) * add more errors * add the validation functions * spread --- src/api/handlers/webhook.ts | 119 +++++++++++++++++++++++++----------- src/types/errors.ts | 2 + 2 files changed, 85 insertions(+), 36 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 15f9039d1..1d2b56d8e 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -9,6 +9,9 @@ import { ExecuteWebhookOptions, MessageCreateOptions, SlashCommand, + SlashCommandOption, + SlashCommandOptionChoice, + SlashCommandOptionType, SlashCommandResponseOptions, UpsertSlashCommandOptions, UpsertSlashCommandsOptions, @@ -270,6 +273,82 @@ export async function deleteWebhookMessage( return result; } +function validateSlashOptionChoices( + choices: SlashCommandOptionChoice[], + optionType: SlashCommandOptionType, +) { + for (const choice of choices) { + if ([...choice.name].length < 1 || [...choice.name].length > 100) { + throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES); + } + + if ( + (optionType === SlashCommandOptionType.STRING && + (typeof choice.value !== "string" || choice.value.length < 1 || + choice.value.length > 100)) || + (optionType === SlashCommandOptionType.INTEGER && + typeof choice.value !== "number") + ) { + throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES); + } + } +} + +function validateSlashOptions(options: SlashCommandOption[]) { + for (const option of options) { + if ( + (option.choices?.length && option.choices.length > 25) || + option.type !== SlashCommandOptionType.STRING && + option.type !== SlashCommandOptionType.INTEGER + ) { + throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES); + } + + if ( + ([...option.name].length < 1 || [...option.name].length > 32) || + ([...option.description].length < 1 || + [...option.description].length > 100) + ) { + throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES); + } + + if (option.choices) { + validateSlashOptionChoices(option.choices, option.type); + } + } +} + +function validateSlashCommands( + commands: UpsertSlashCommandOptions[], + create = false, +) { + for (const command of commands) { + if ( + (command.name && !SLASH_COMMANDS_NAME_REGEX.test(command.name)) || + (create && !command.name) + ) { + throw new Error(Errors.INVALID_SLASH_NAME); + } + + if ( + (command.description && + ([...command.description].length < 1 || + [...command.description].length > 100)) || + (create && !command.description) + ) { + throw new Error(Errors.INVALID_SLASH_DESCRIPTION); + } + + if (command.options?.length) { + if (command.options.length > 25) { + throw new Error(Errors.INVALID_SLASH_OPTIONS); + } + + validateSlashOptions(command.options); + } + } +} + /** * There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means: * @@ -282,15 +361,7 @@ export async function deleteWebhookMessage( * Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. */ export async function createSlashCommand(options: CreateSlashCommandOptions) { - if (!SLASH_COMMANDS_NAME_REGEX.test(options.name)) { - throw new Error(Errors.INVALID_SLASH_NAME); - } - - if ( - [...options.description].length < 1 || [...options.description].length > 100 - ) { - throw new Error(Errors.INVALID_SLASH_DESCRIPTION); - } + validateSlashCommands([options], true); const result = await RequestManager.post( options.guildID @@ -335,17 +406,7 @@ export async function upsertSlashCommand( options: UpsertSlashCommandOptions, guildID?: string, ) { - if (options.name && !SLASH_COMMANDS_NAME_REGEX.test(options.name)) { - throw new Error(Errors.INVALID_SLASH_NAME); - } - - if ( - options.description && - ([...options.description].length < 1 || - [...options.description].length > 100) - ) { - throw new Error(Errors.INVALID_SLASH_DESCRIPTION); - } + validateSlashCommands([options]); const result = await RequestManager.patch( guildID @@ -370,27 +431,13 @@ export async function upsertSlashCommands( options: UpsertSlashCommandsOptions[], guildID?: string, ) { - const data = options.map((option) => { - if (option.name && !SLASH_COMMANDS_NAME_REGEX.test(option.name)) { - throw new Error(Errors.INVALID_SLASH_NAME); - } - - if ( - option.description && - ([...option.description].length < 1 || - [...option.description].length > 100) - ) { - throw new Error(Errors.INVALID_SLASH_DESCRIPTION); - } - - return option; - }); + validateSlashCommands(options); const result = await RequestManager.put( guildID ? endpoints.COMMANDS_GUILD(applicationID, guildID) : endpoints.COMMANDS(applicationID), - data, + options, ); return result; diff --git a/src/types/errors.ts b/src/types/errors.ts index cc64e5b3c..f3895022a 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -20,6 +20,8 @@ export enum Errors { // Interaction Errors INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION", INVALID_SLASH_NAME = "INVALID_SLASH_NAME", + INVALID_SLASH_OPTIONS = "INVALID_SLASH_OPTIONS", + INVALID_SLASH_OPTIONS_CHOICES = "INVALID_SLASH_OPTIONS_CHOICES", // Webhook Errors INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME", INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS", From 537d508cfa1da7f5f71863c5722a023869e08e20 Mon Sep 17 00:00:00 2001 From: ayntee Date: Sun, 7 Mar 2021 21:30:33 +0400 Subject: [PATCH 041/200] feat(types/guild): add computePruneCount to PruneOptions (#605) * fix(handlers/guild): rewrite and rename pruneMembers() * remove breaking change * Update src/api/handlers/guild.ts * Update src/types/guild.ts * Update src/types/guild.ts * Update src/types/guild.ts * Update src/api/handlers/guild.ts * Update src/api/handlers/guild.ts * Update src/api/handlers/guild.ts * Update src/types/guild.ts * Update src/types/guild.ts * Update src/types/guild.ts * Update src/api/handlers/guild.ts * Update src/api/handlers/guild.ts * Update src/api/handlers/guild.ts * fmt * fixes * Update src/types/guild.ts Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * fmt Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> --- src/api/handlers/guild.ts | 31 +++++++++++++++++++++--------- src/types/guild.ts | 40 +++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/api/handlers/guild.ts b/src/api/handlers/guild.ts index b59c2258b..9aff87160 100644 --- a/src/api/handlers/guild.ts +++ b/src/api/handlers/guild.ts @@ -529,9 +529,11 @@ export async function swapRoles(guildID: string, rolePositons: PositionSwap) { } /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ -export async function getPruneCount(guildID: string, options: PruneOptions) { - if (options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); - if (options.days > 30) throw new Error(Errors.PRUNE_MAX_DAYS); +export async function getPruneCount(guildID: string, options?: PruneOptions) { + if (options?.days && options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); + if (options?.days && options.days > 30) { + throw new Error(Errors.PRUNE_MAX_DAYS); + } const hasPerm = await botHasPermission(guildID, ["KICK_MEMBERS"]); if (!hasPerm) { @@ -540,16 +542,23 @@ export async function getPruneCount(guildID: string, options: PruneOptions) { const result = await RequestManager.get( endpoints.GUILD_PRUNE(guildID), - { ...options, include_roles: options.roles.join(",") }, + { ...options, include_roles: options?.roles?.join(",") }, ) as PrunePayload; return result.pruned; } -/** Begin pruning all members in the given time period */ -export async function pruneMembers(guildID: string, options: PruneOptions) { - if (options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); - if (options.days > 30) throw new Error(Errors.PRUNE_MAX_DAYS); +/** + * Begin a prune operation. Requires the KICK_MEMBERS permission. Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation. For large guilds it's recommended to set the computePruneCount option to false, forcing 'pruned' to null. Fires multiple Guild Member Remove Gateway events. + * + * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the roles (resolved to include_roles internally) parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + */ +export async function pruneMembers( + guildID: string, + { roles, computePruneCount, ...options }: PruneOptions, +) { + if (options.days && options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); + if (options.days && options.days > 30) throw new Error(Errors.PRUNE_MAX_DAYS); const hasPerm = await botHasPermission(guildID, ["KICK_MEMBERS"]); if (!hasPerm) { @@ -558,7 +567,11 @@ export async function pruneMembers(guildID: string, options: PruneOptions) { const result = await RequestManager.post( endpoints.GUILD_PRUNE(guildID), - { ...options, include_roles: options.roles.join(",") }, + { + ...options, + compute_prune_count: computePruneCount, + include_roles: roles, + }, ); return result; diff --git a/src/types/guild.ts b/src/types/guild.ts index 74f45390d..e31b0147f 100644 --- a/src/types/guild.ts +++ b/src/types/guild.ts @@ -547,10 +547,42 @@ export interface PrunePayload { } export interface PruneOptions { - /** number of days to count prune for (1 - 30). Defaults to 7 days. */ - days: number; - /** Include members with these role ids */ - roles: string[]; + /** Number of days to prune (1-30). Default: 7 */ + days?: + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23 + | 24 + | 25 + | 26 + | 27 + | 28 + | 29 + | 30; + /** Whether 'pruned' is returned, discouraged for large guilds. Default: true */ + computePruneCount?: boolean; + /** Role(s) to include */ + roles?: string[]; } export interface VoiceState { From 1b29c92aac3a8a7c55f414dae1b72884fea0489e Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 15:42:42 +0400 Subject: [PATCH 042/200] Revert "fix(types/webhook): update interaction response types (#610)" (#635) This reverts commit b511a0d39ff1f0126c969c7e14b95beb598c2e38. --- src/interactions/types/interactions.ts | 10 +++++++--- src/types/webhook.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/interactions/types/interactions.ts b/src/interactions/types/interactions.ts index e9d291a3c..295015e34 100644 --- a/src/interactions/types/interactions.ts +++ b/src/interactions/types/interactions.ts @@ -66,8 +66,12 @@ export enum InteractionType { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** Respond with a message, showing the user's input */ + /** ACK a command without sending a message, eating the user's input */ + ACKNOWLEDGE = 2, + /** respond with a message, eating the user's input */ + CHANNEL_MESSAGE = 3, + /** respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK an interaction and edit to a response later, the user sees a loading state */ - DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, + /** ACK a command without sending a message, showing the user's input */ + ACK_WITH_SOURCE = 5, } diff --git a/src/types/webhook.ts b/src/types/webhook.ts index 925632135..bc52309f9 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -198,10 +198,14 @@ export interface SlashCommandCallbackData { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** Respond with a message, showing the user's input */ + /** ACK a command without sending a message, eating the user's input */ + ACKNOWLEDGE = 2, + /** respond with a message, eating the user's input */ + CHANNEL_MESSAGE = 3, + /** respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK an interaction and edit to a response later, the user sees a loading state */ - DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, + /** ACK a command without sending a message, showing the user's input */ + ACK_WITH_SOURCE = 5, } // TODO: remove this interface for v11 From a515765abfae94cfcef309f29c9085e19a919291 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 15:42:51 +0400 Subject: [PATCH 043/200] Revert "fix(handlers/webhook): return Message object instead of raw payload (#611)" (#634) This reverts commit 9c7340f6240841f69db7b5132b4759a977ec48ce. --- src/api/handlers/webhook.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 1d2b56d8e..ba07414c6 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -255,10 +255,9 @@ export async function editWebhookMessage( const result = await RequestManager.patch( endpoints.WEBHOOK_MESSAGE(webhookID, webhookToken, messageID), { ...options, allowed_mentions: options.allowed_mentions }, - ) as MessageCreateOptions; + ); - const message = await structures.createMessage(result); - return message; + return result; } export async function deleteWebhookMessage( @@ -600,11 +599,5 @@ export async function editSlashResponse( options, ); - // If the original message was edited, this will not return a message - if (!options.messageID) return result; - - const message = await structures.createMessage( - result as MessageCreateOptions, - ); - return message; + return result; } From 53bd6c23862551ba1d14f18550fff37165eb4b56 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Mon, 8 Mar 2021 08:37:18 -0500 Subject: [PATCH 044/200] feat(types/member): add permissions in MemberCreatePayload (#641) * add permission * update interaction type --- src/interactions/types/member.ts | 2 ++ src/types/member.ts | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/interactions/types/member.ts b/src/interactions/types/member.ts index 94634a62e..a0bb175c5 100644 --- a/src/interactions/types/member.ts +++ b/src/interactions/types/member.ts @@ -40,4 +40,6 @@ export interface MemberCreatePayload { deaf: boolean; /** Whether the user is muted in voice channels */ mute: boolean; + /** total permissions of the member in the channel, including overrides */ + permissions: string; } diff --git a/src/types/member.ts b/src/types/member.ts index 5b9dc4854..41fe58b40 100644 --- a/src/types/member.ts +++ b/src/types/member.ts @@ -10,7 +10,8 @@ export interface EditMemberOptions { /** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission. */ deaf?: boolean; /** The id of the channel to move user to if they are connected to voice. To kick the user from their current channel, set to null. Requires MOVE_MEMBERS permission. When moving members to channels, must have permissions to both CONNECT to the channel and have the MOVE_MEMBER permission. */ - "channel_id"?: string | null; + // deno-lint-ignore camelcase + channel_id?: string | null; } export interface MemberCreatePayload { @@ -21,9 +22,11 @@ export interface MemberCreatePayload { /** Array of role ids that the member has */ roles: string[]; /** When the user joined the guild. */ - "joined_at": string; + // deno-lint-ignore camelcase + joined_at: string; /** When the user used their nitro boost on the server. */ - "premium_since"?: string; + // deno-lint-ignore camelcase + premium_since?: string; /** Whether the user is deafened in voice channels */ deaf: boolean; /** Whether the user is muted in voice channels */ @@ -48,3 +51,8 @@ export interface GuildMember { /** Whether the user has passed the guild's Membership Screening requirements */ pending?: boolean; } + +export interface InteractionMember extends GuildMember { + /** total permissions of the member in the channel, including overrides, returned when in the interaction object */ + permissions: string; +} From 5be674e3e83891a1377ef1d763ff58cc93024053 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Mon, 8 Mar 2021 08:41:31 -0500 Subject: [PATCH 045/200] feat(types/message): add fail_if_not_exists to Reference interface (#642) * support fail replies * fmt * fix linter issue * fix condition --- src/api/handlers/channel.ts | 1 + src/api/structures/message.ts | 11 +++++++++-- src/types/channel.ts | 2 ++ src/types/message.ts | 21 +++++++++++++++++---- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/api/handlers/channel.ts b/src/api/handlers/channel.ts index 5e1b772d1..8efaf18e7 100644 --- a/src/api/handlers/channel.ts +++ b/src/api/handlers/channel.ts @@ -244,6 +244,7 @@ export async function sendMessage( ? { message_reference: { message_id: content.replyMessageID, + fail_if_not_exists: content.failReplyIfNotExists === true, }, } : {}), diff --git a/src/api/structures/message.ts b/src/api/structures/message.ts index 47e828cae..629d435c4 100644 --- a/src/api/structures/message.ts +++ b/src/api/structures/message.ts @@ -2,6 +2,7 @@ import { Activity, Application, Attachment, + DiscordReferencePayload, Embed, GuildMember, MessageContent, @@ -85,11 +86,17 @@ const baseMessage: Partial = { }, reply(content) { const contentWithMention = typeof content === "string" - ? { content, mentions: { repliedUser: true }, replyMessageID: this.id } + ? { + content, + mentions: { repliedUser: true }, + replyMessageID: this.id, + failReplyIfNotExists: false, + } : { ...content, mentions: { ...(content.mentions || {}), repliedUser: true }, replyMessageID: this.id, + failReplyIfNotExists: content.failReplyIfNotExists === true, }; if (this.guildID) return sendMessage(this.channelID!, contentWithMention); @@ -216,7 +223,7 @@ export interface Message { /** Applications that sent with Rich Presence related chat embeds. */ applications?: Application; /** The reference data sent with crossposted messages */ - messageReference?: Reference; + messageReference?: DiscordReferencePayload; /** The message flags combined like permission bits describe extra features of the message */ flags?: 1 | 2 | 4 | 8 | 16; /** the stickers sent with the message (bots currently can only receive messages with stickers, not send) */ diff --git a/src/types/channel.ts b/src/types/channel.ts index 3743c2ebc..5c0716f11 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -118,6 +118,8 @@ export interface MessageContent { "payload_json"?: string; /** If you want to send a reply message, provide the original message id here */ replyMessageID?: string; + /** When sending a reply to a message that was deleted, should Discord fail and throw an error. By default we make this false to prevent your bot from crashing. */ + failReplyIfNotExists?: boolean; } export interface FileContent { diff --git a/src/types/message.ts b/src/types/message.ts index 86f0e4d63..1d9889921 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -200,11 +200,23 @@ export interface Application { export interface Reference { /** The id of the originating message */ - "message_id"?: string; + // deno-lint-ignore camelcase + message_id?: string; /** The id of the originating message's channel */ - "channel_id": string; + // deno-lint-ignore camelcase + channel_id?: string; /** The id of the originating message's guild */ - "guild_id"?: string; + // deno-lint-ignore camelcase + guild_id?: string; + /** When sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default false */ + // deno-lint-ignore camelcase + fail_if_not_exists?: boolean; +} + +export interface DiscordReferencePayload extends Reference { + /** The id of the originating message's channel */ + // deno-lint-ignore camelcase + channel_id: string; } export enum MessageFlags { @@ -287,7 +299,8 @@ export interface MessageCreateOptions { /** Applications that sent with Rich Presence related chat embeds. */ applications?: Application; /** The reference data sent with crossposted messages */ - "message_reference"?: Reference; + // deno-lint-ignore camelcase + message_reference?: DiscordReferencePayload; /** The message flags combined like permission bits describe extra features of the message */ flags?: 1 | 2 | 4 | 8 | 16; /** the stickers sent with the message (bots currently can only receive messages with stickers, not send) */ From ed85685d35912d4bbbcdc3668019b00ee2f012a1 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:43:30 +0100 Subject: [PATCH 046/200] add type (#619) --- src/types/api/interaction.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/types/api/interaction.ts b/src/types/api/interaction.ts index 993409fc9..177c26802 100644 --- a/src/types/api/interaction.ts +++ b/src/types/api/interaction.ts @@ -59,6 +59,8 @@ export interface DiscordApplicationCommandInteractionDataResolved { export interface DiscordInteractionDataOption { /** the name of the parameter */ name: string; + /** value of ApplicationCommandOptionType */ + type: DiscordApplicationCommandOptionType; /** the value of the pair. present if there was no more options */ value?: string | number; /** present if this option is a group or subcommand */ @@ -70,7 +72,7 @@ export interface DiscordApplicationCommand { /** unique id of the command */ id: string; /** unique id of the parent application */ - "application_id": string; + application_id: string; /** 3-32 character name matching `^[\w-]{3,32}$` */ name: string; /** 1-100 character description */ From 100f7ff9651ade09e99d964daa5bf9060044605a Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:46:23 +0100 Subject: [PATCH 047/200] fix(handlers/channel): add permission handler for startTyping() (#626) * add perm check * Update src/api/handlers/channel.ts Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> --- src/api/handlers/channel.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/api/handlers/channel.ts b/src/api/handlers/channel.ts index 8efaf18e7..b314fbdcf 100644 --- a/src/api/handlers/channel.ts +++ b/src/api/handlers/channel.ts @@ -133,6 +133,30 @@ export async function getPins(channelID: string) { * this endpoint may be called to let the user know that the bot is processing their message. */ export async function startTyping(channelID: string) { + const channel = await cacheHandlers.get("channels", channelID); + // If the channel is cached, we can do extra checks/safety + if (channel) { + if ( + ![ + ChannelTypes.DM, + ChannelTypes.GUILD_NEWS, + ChannelTypes.GUILD_TEXT, + ].includes(channel.type) + ) { + throw new Error(Errors.CHANNEL_NOT_TEXT_BASED); + } + + const hasSendMessagesPerm = await botHasChannelPermissions( + channelID, + ["SEND_MESSAGES"], + ); + if ( + !hasSendMessagesPerm + ) { + throw new Error(Errors.MISSING_SEND_MESSAGES); + } + } + const result = await RequestManager.post(endpoints.CHANNEL_TYPING(channelID)); return result; From bd14075c8a9d6665bd0a1fd06bcb483753023d76 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 18:12:35 +0400 Subject: [PATCH 048/200] fix(types/channel): change FileContent.blob type to Blob (#509) * fix(types/channel): change FileContent.blob type to Blob * idk * change to blob * idk --- src/interactions/types/webhook.ts | 2 +- src/rest/request_manager.ts | 2 +- src/types/api/message.ts | 2 +- src/types/channel.ts | 2 +- src/types/webhook.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interactions/types/webhook.ts b/src/interactions/types/webhook.ts index 9743bb8b9..0a65112a6 100644 --- a/src/interactions/types/webhook.ts +++ b/src/interactions/types/webhook.ts @@ -12,7 +12,7 @@ export interface ExecuteWebhookOptions { /** true if this is a TTS message */ tts?: boolean; /** file contents the contents of the file being sent one of content, file, embeds */ - file?: { blob: unknown; name: string }; + file?: { blob: Blob; name: string }; /** array of up to 10 embed objects embedded rich content. */ embeds?: Embed[]; /** allowed mentions for the message */ diff --git a/src/rest/request_manager.ts b/src/rest/request_manager.ts index af77cb559..02e12b6a9 100644 --- a/src/rest/request_manager.ts +++ b/src/rest/request_manager.ts @@ -166,7 +166,7 @@ function createRequestBody(body: any, method: RequestMethods) { body.file.map((file: FileContent, index: number) => // The key of the form data item must be unique; otherwise, Discordeno only considers the first item in the form data with the same names - form.append(`file${index + 1}`, file.blob as Blob, file.name) + form.append(`file${index + 1}`, file.blob, file.name) ); form.append("payload_json", JSON.stringify({ ...body, file: undefined })); diff --git a/src/types/api/message.ts b/src/types/api/message.ts index d05de3392..a85eaec80 100644 --- a/src/types/api/message.ts +++ b/src/types/api/message.ts @@ -250,7 +250,7 @@ export interface DiscordCreateMessageParams { /** `true` if this is a TTS message */ tts: boolean; /** the contents of the file being sent */ - file?: { blob: unknown; name: string }; + file?: { blob: Blob; name: string }; /** embedded rich content */ embed?: DiscordEmbed; /** JSON encoded body of any additional request fields. */ diff --git a/src/types/channel.ts b/src/types/channel.ts index 5c0716f11..5113da665 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -123,7 +123,7 @@ export interface MessageContent { } export interface FileContent { - blob: unknown; + blob: Blob; name: string; } diff --git a/src/types/webhook.ts b/src/types/webhook.ts index bc52309f9..77932cbea 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -57,7 +57,7 @@ export interface ExecuteWebhookOptions { /** true if this is a TTS message */ tts?: boolean; /** file contents the contents of the file being sent one of content, file, embeds */ - file?: { blob: unknown; name: string }; + file?: { blob: Blob; name: string }; /** array of up to 10 embed objects embedded rich content. */ embeds?: Embed[]; /** allowed mentions for the message */ From fdab0d93baa602e20ca39c9530a77b71d873f873 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 18:13:01 +0400 Subject: [PATCH 049/200] feat: change Guild.emojis to use collection (#388) * feat(guild): change Guild.emojis to use collection * idk --- src/api/controllers/guilds.ts | 7 +++++-- src/api/handlers/guild.ts | 6 ++++-- src/api/structures/guild.ts | 6 +++++- src/types/options.ts | 5 +++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/api/controllers/guilds.ts b/src/api/controllers/guilds.ts index 0f428f597..48d49af19 100644 --- a/src/api/controllers/guilds.ts +++ b/src/api/controllers/guilds.ts @@ -8,6 +8,7 @@ import { UpdateGuildPayload, } from "../../types/mod.ts"; import { cache } from "../../util/cache.ts"; +import { Collection } from "../../util/collection.ts"; import { structures } from "../structures/mod.ts"; import { cacheHandlers } from "./cache.ts"; @@ -115,13 +116,15 @@ export async function handleInternalGuildEmojisUpdate(data: DiscordPayload) { if (!guild) return; const cachedEmojis = guild.emojis; - guild.emojis = payload.emojis; + guild.emojis = new Collection( + payload.emojis.map((emoji) => [emoji.id ?? emoji.name, emoji]), + ); cacheHandlers.set("guilds", payload.guild_id, guild); eventHandlers.guildEmojisUpdate?.( guild, - payload.emojis, + guild.emojis, cachedEmojis, ); } diff --git a/src/api/handlers/guild.ts b/src/api/handlers/guild.ts index 9aff87160..7a9023a9c 100644 --- a/src/api/handlers/guild.ts +++ b/src/api/handlers/guild.ts @@ -402,7 +402,9 @@ export async function getEmojis(guildID: string, addToCache = true) { if (addToCache) { const guild = await cacheHandlers.get("guilds", guildID); if (!guild) throw new Error(Errors.GUILD_NOT_FOUND); - guild.emojis = result; + + result.forEach((emoji) => guild.emojis.set(emoji.id ?? emoji.name, emoji)); + cacheHandlers.set("guilds", guildID, guild); } @@ -426,7 +428,7 @@ export async function getEmoji( if (addToCache) { const guild = await cacheHandlers.get("guilds", guildID); if (!guild) throw new Error(Errors.GUILD_NOT_FOUND); - guild.emojis.push(result); + guild.emojis.set(result.id ?? result.name, result); cacheHandlers.set( "guilds", guildID, diff --git a/src/api/structures/guild.ts b/src/api/structures/guild.ts index f1cc711cb..63e4c9b18 100644 --- a/src/api/structures/guild.ts +++ b/src/api/structures/guild.ts @@ -138,6 +138,7 @@ export async function createGuild(data: CreateGuildPayload, shardID: number) { channels = [], members, presences = [], + emojis, ...rest } = data; @@ -187,6 +188,9 @@ export async function createGuild(data: CreateGuildPayload, shardID: number) { new Collection(presences.map((p: Presence) => [p.user.id, p])), ), memberCount: createNewProp(memberCount), + emojis: createNewProp( + new Collection(emojis.map((emoji) => [emoji.id ?? emoji.name, emoji])), + ), voiceStates: createNewProp( new Collection( voiceStates.map((vs: VoiceState) => [ @@ -229,7 +233,7 @@ export interface Guild { /** Explicit content filter level */ explicitContentFilter: number; /** The custom guild emojis */ - emojis: Emoji[]; + emojis: Collection; /** Enabled guild features */ features: GuildFeatures[]; /** System channel flags */ diff --git a/src/types/options.ts b/src/types/options.ts index 770d031e2..72db39794 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -5,6 +5,7 @@ import { Message, Role, } from "../api/structures/mod.ts"; +import { Collection } from "../util/collection.ts"; import { DiscordPayload, Emoji, @@ -141,8 +142,8 @@ export interface EventHandlers { /** Sent when a guild's emojis have been updated. */ guildEmojisUpdate?: ( guild: Guild, - emojis: Emoji[], - cachedEmojis: Emoji[], + emojis: Collection, + cachedEmojis: Collection, ) => unknown; /** Sent when a new user joins a guild. */ guildMemberAdd?: (guild: Guild, member: Member) => unknown; From 95ac54cee92aac7e959cbc7c4609ade4d78ff7de Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 18:13:27 +0400 Subject: [PATCH 050/200] fix(handlers/member): change editMember() to return a Member object (#578) --- src/api/handlers/member.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/handlers/member.ts b/src/api/handlers/member.ts index 143e48f9c..c877cf1a6 100644 --- a/src/api/handlers/member.ts +++ b/src/api/handlers/member.ts @@ -7,6 +7,7 @@ import { Errors, ImageFormats, ImageSize, + MemberCreatePayload, MessageContent, } from "../../types/mod.ts"; import { endpoints } from "../../util/constants.ts"; @@ -227,9 +228,10 @@ export async function editMember( const result = await RequestManager.patch( endpoints.GUILD_MEMBER(guildID, memberID), options, - ); + ) as MemberCreatePayload; + const member = await structures.createMember(result, guildID); - return result; + return member; } /** From 544c3c3af21555ec73804e3f6fbe90c5ae73226e Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 18:13:55 +0400 Subject: [PATCH 051/200] feat(types/interactions): remove "default" from ApplicationCommandOption interface (#598) --- src/types/interactions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types/interactions.ts b/src/types/interactions.ts index dc405be01..5ac126eff 100644 --- a/src/types/interactions.ts +++ b/src/types/interactions.ts @@ -64,8 +64,6 @@ export interface ApplicationCommandOption { name: string; /** 1-100 character description */ description: string; - /** the first `required` option for the user to complete--only one option can be `default` */ - default?: boolean; /** if the parameter is required or optional--default `false` */ required?: boolean; /** choices for `string` and `int` types for the user to pick from */ From 9715ab4274ebb3a810fcea9f9bdb873464618f3a Mon Sep 17 00:00:00 2001 From: Pawan Akhil Date: Mon, 8 Mar 2021 19:51:17 +0530 Subject: [PATCH 052/200] Fixed field name in GuildTemplate Interface (#505) Replaced "user" field name with "creator". Co-authored-by: ayntee --- src/types/guild.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/guild.ts b/src/types/guild.ts index e31b0147f..8350f152c 100644 --- a/src/types/guild.ts +++ b/src/types/guild.ts @@ -683,7 +683,7 @@ export interface GuildTemplate { /** the ID of the user who created the template */ "creator_id": string; /** the user who created the template */ - user: UserPayload; + creator: UserPayload; /** when this template was created */ "created_at": string; /** when this template was last synced to the source guild */ From ebc48156aa3c85fd3f7f9fb3debb836a69156a14 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Mon, 8 Mar 2021 15:23:04 +0100 Subject: [PATCH 053/200] feat(handlers/guild): change createGuildChannel() to use guild ID (#528) * guild > guildID * fix(test): createGuildChannel test * Update test/mod.test.ts * Update test/mod.test.ts * fmt Co-authored-by: ayntee --- src/api/handlers/guild.ts | 25 ++++++++++++------------- test/mod.test.ts | 5 +---- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/api/handlers/guild.ts b/src/api/handlers/guild.ts index 7a9023a9c..df04dc6b3 100644 --- a/src/api/handlers/guild.ts +++ b/src/api/handlers/guild.ts @@ -112,30 +112,29 @@ export function guildBannerURL( /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ export async function createGuildChannel( - guild: Guild, + guildID: string, name: string, options?: ChannelCreateOptions, ) { const hasPerm = await botHasPermission( - guild.id, + guildID, ["MANAGE_CHANNELS"], ); if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } - const result = - (await RequestManager.post(endpoints.GUILD_CHANNELS(guild.id), { - ...options, - name, - permission_overwrites: options?.permissionOverwrites?.map((perm) => ({ - ...perm, + const result = (await RequestManager.post(endpoints.GUILD_CHANNELS(guildID), { + ...options, + name, + permission_overwrites: options?.permissionOverwrites?.map((perm) => ({ + ...perm, - allow: calculateBits(perm.allow), - deny: calculateBits(perm.deny), - })), - type: options?.type || ChannelTypes.GUILD_TEXT, - })) as ChannelCreatePayload; + allow: calculateBits(perm.allow), + deny: calculateBits(perm.deny), + })), + type: options?.type || ChannelTypes.GUILD_TEXT, + })) as ChannelCreatePayload; const channelStruct = await structures.createChannel(result); diff --git a/test/mod.test.ts b/test/mod.test.ts index 4cc7aab0b..a5632ea36 100644 --- a/test/mod.test.ts +++ b/test/mod.test.ts @@ -138,10 +138,7 @@ Deno.test({ Deno.test({ name: "[channel] create a channel in a guild", async fn() { - const guild = cache.guilds.get(tempData.guildID); - if (!guild) throw new Error("Guild not found"); - - const channel = await createGuildChannel(guild, "test"); + const channel = await createGuildChannel(tempData.guildID, "test"); // Assertions assertExists(channel); From 42c743421b75109bbf197b7bab1f394da1d3bc3b Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Mon, 8 Mar 2021 15:51:12 +0100 Subject: [PATCH 054/200] refactor(handlers/guild): rename guild widget functions (#386) * Update guild.ts * Update guild.ts * this should be named getWidgetSettings * update mod imports --- src/api/handlers/guild.ts | 8 ++++---- src/api/handlers/mod.ts | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/api/handlers/guild.ts b/src/api/handlers/guild.ts index df04dc6b3..99f3bcfa5 100644 --- a/src/api/handlers/guild.ts +++ b/src/api/handlers/guild.ts @@ -694,8 +694,8 @@ export async function getAuditLogs( return result; } -/** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ -export async function getEmbed(guildID: string) { +/** Returns the guild widget object. Requires the MANAGE_GUILD permission. */ +export async function getWidgetSettings(guildID: string) { const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]); if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); @@ -706,8 +706,8 @@ export async function getEmbed(guildID: string) { return result; } -/** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ -export async function editEmbed( +/** Modify a guild widget object for the guild. Requires the MANAGE_GUILD permission. */ +export async function editWidget( guildID: string, enabled: boolean, channelID?: string | null, diff --git a/src/api/handlers/mod.ts b/src/api/handlers/mod.ts index ddd9070a0..cb18f6431 100644 --- a/src/api/handlers/mod.ts +++ b/src/api/handlers/mod.ts @@ -39,6 +39,7 @@ import { editGuildTemplate, editIntegration, editRole, + editWidget, emojiURL, fetchMembers, getAuditLogs, @@ -66,6 +67,7 @@ import { getVanityURL, getVoiceRegions, getWebhooks, + getWidgetSettings, guildBannerURL, guildIconURL, guildSplashURL, @@ -160,7 +162,7 @@ export let handlers = { deleteIntegration, deleteRole, deleteServer, - editEmbed, + editWidget, editEmoji, editGuild, editGuildTemplate, @@ -173,6 +175,7 @@ export let handlers = { getBans, getChannel, getChannels, + getWidgetSettings, getEmbed, getEmoji, getEmojis, From 3a249d91d437824dfcd2a9a0fea7a9d9d77a0b6d Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Mon, 8 Mar 2021 09:52:59 -0500 Subject: [PATCH 055/200] Revert "Revert "fix(handlers/webhook): return Message object instead of raw payload (#611)" (#634)" (#643) This reverts commit a515765abfae94cfcef309f29c9085e19a919291. --- src/api/handlers/webhook.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index ba07414c6..1d2b56d8e 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -255,9 +255,10 @@ export async function editWebhookMessage( const result = await RequestManager.patch( endpoints.WEBHOOK_MESSAGE(webhookID, webhookToken, messageID), { ...options, allowed_mentions: options.allowed_mentions }, - ); + ) as MessageCreateOptions; - return result; + const message = await structures.createMessage(result); + return message; } export async function deleteWebhookMessage( @@ -599,5 +600,11 @@ export async function editSlashResponse( options, ); - return result; + // If the original message was edited, this will not return a message + if (!options.messageID) return result; + + const message = await structures.createMessage( + result as MessageCreateOptions, + ); + return message; } From 23708cc39db6f54a8780dd32ca99c653cc6ba110 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Mon, 8 Mar 2021 09:55:31 -0500 Subject: [PATCH 056/200] fix(types/webhook): update interaction response types (#644) This reverts commit 1b29c92aac3a8a7c55f414dae1b72884fea0489e. --- src/interactions/types/interactions.ts | 10 +++------- src/types/webhook.ts | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/interactions/types/interactions.ts b/src/interactions/types/interactions.ts index 295015e34..e9d291a3c 100644 --- a/src/interactions/types/interactions.ts +++ b/src/interactions/types/interactions.ts @@ -66,12 +66,8 @@ export enum InteractionType { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** ACK a command without sending a message, eating the user's input */ - ACKNOWLEDGE = 2, - /** respond with a message, eating the user's input */ - CHANNEL_MESSAGE = 3, - /** respond with a message, showing the user's input */ + /** Respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK a command without sending a message, showing the user's input */ - ACK_WITH_SOURCE = 5, + /** ACK an interaction and edit to a response later, the user sees a loading state */ + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, } diff --git a/src/types/webhook.ts b/src/types/webhook.ts index 77932cbea..21e6a99cb 100644 --- a/src/types/webhook.ts +++ b/src/types/webhook.ts @@ -198,14 +198,10 @@ export interface SlashCommandCallbackData { export enum InteractionResponseType { /** ACK a `Ping` */ PONG = 1, - /** ACK a command without sending a message, eating the user's input */ - ACKNOWLEDGE = 2, - /** respond with a message, eating the user's input */ - CHANNEL_MESSAGE = 3, - /** respond with a message, showing the user's input */ + /** Respond with a message, showing the user's input */ CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** ACK a command without sending a message, showing the user's input */ - ACK_WITH_SOURCE = 5, + /** ACK an interaction and edit to a response later, the user sees a loading state */ + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, } // TODO: remove this interface for v11 From 419f57ec74a1cd4a427d38bd0ddd6b8ce29cbe17 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 19:09:12 +0400 Subject: [PATCH 057/200] fix(handlers/webhook): change return type of editWebhookMessage() & deleteWebhookMessage() to Message (#636) * fix: change any to message types * Update webhook.ts * Update src/api/handlers/webhook.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> From 03d66147586d889bc2b505cdd4b126792628f84a Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 19:10:19 +0400 Subject: [PATCH 058/200] refactor: rename slowmode to rateLimitPerUser (#645) --- src/api/handlers/channel.ts | 2 +- src/api/structures/channel.ts | 2 +- src/types/channel.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/handlers/channel.ts b/src/api/handlers/channel.ts index b314fbdcf..aaaa998f5 100644 --- a/src/api/handlers/channel.ts +++ b/src/api/handlers/channel.ts @@ -514,7 +514,7 @@ export async function editChannel( const payload = { ...options, // deno-lint-ignore camelcase - rate_limit_per_user: options.slowmode, + rate_limit_per_user: options.rateLimitPerUser, // deno-lint-ignore camelcase parent_id: options.parentID, // deno-lint-ignore camelcase diff --git a/src/api/structures/channel.ts b/src/api/structures/channel.ts index f0706c3ab..71bb8ec27 100644 --- a/src/api/structures/channel.ts +++ b/src/api/structures/channel.ts @@ -135,7 +135,7 @@ export interface Channel { lastMessageID?: string; /** The amount of users allowed in this voice channel. */ userLimit?: number; - /** The rate limit(slowmode) in this text channel that users can send messages. */ + /** The rate limit (slowmode) in this text channel that users can send messages. */ rateLimitPerUser?: number; /** The category id for this channel */ parentID?: string; diff --git a/src/types/channel.ts b/src/types/channel.ts index 5113da665..59b361222 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -11,7 +11,7 @@ export interface ChannelEditOptions { /** whether the channel is nsfw Text */ nsfw?: boolean; /** amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected Text */ - slowmode?: number; + rateLimitPerUser?: number; /** the bitrate (in bits) of the voice channel; 8000 to 96000 (128000 for VIP servers) Voice */ bitrate?: number; /** the user limit of the voice channel; 0 refers to no limit, 1 to 99 refers to a user limit Voice */ From 45a32463704830c44c5e3507ea58bd67532a25bc Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 8 Mar 2021 19:41:20 +0400 Subject: [PATCH 059/200] fix(ws/shard): update status update payload (#559) * fix(ws/shard): update status update payload * :( * Update src/types/discord.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> * it\'s activitypayload * Update src/types/discord.ts * idk * idk Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/discord.ts | 13 +++++++++++++ src/util/utils.ts | 30 ++++++++++++++++-------------- src/ws/shard.ts | 24 +++--------------------- src/ws/shard_manager.ts | 21 +++------------------ 4 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/types/discord.ts b/src/types/discord.ts index cf5efb255..b4906e373 100644 --- a/src/types/discord.ts +++ b/src/types/discord.ts @@ -6,6 +6,7 @@ import { } from "./guild.ts"; import { MemberCreatePayload } from "./member.ts"; import { Activity, Application } from "./message.ts"; +import { ActivityPayload } from "./mod.ts"; import { ClientStatusPayload } from "./presence.ts"; export interface DiscordPayload { @@ -363,3 +364,15 @@ export interface InviteDeleteEvent { /** the unique invite code */ code: string; } + +/** https://discord.com/developers/docs/topics/gateway#update-status-gateway-status-update-structure */ +export interface GatewayStatusUpdatePayload { + /** unix time (in milliseconds) of when the client went idle, or null if the client is not idle */ + since: number | null; + /** null, or the user's activities */ + activities: Pick[] | null; + /** the user's new status */ + status: StatusType; + /** whether or not the client is afk */ + afk: boolean; +} diff --git a/src/util/utils.ts b/src/util/utils.ts index 1b2bb9b1f..348cfbd5e 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -1,30 +1,32 @@ import { encode } from "../../deps.ts"; import { + Activity, ActivityType, + GatewayOpcode, + GatewayStatusUpdatePayload, ImageFormats, ImageSize, StatusType, } from "../types/mod.ts"; -import { sendGatewayCommand } from "../ws/shard_manager.ts"; +import { basicShards, sendWS } from "../ws/shard.ts"; export const sleep = (timeout: number) => { return new Promise((resolve) => setTimeout(resolve, timeout)); }; -export interface BotStatusRequest { - status: StatusType; - game: { - name?: string; - type: ActivityType; - }; -} - -export function editBotsStatus( - status: StatusType, - name?: string, - type = ActivityType.Game, +export function editBotStatus( + data: Pick, ) { - sendGatewayCommand("EDIT_BOTS_STATUS", { status, game: { name, type } }); + basicShards.forEach((shard) => { + sendWS({ + op: GatewayOpcode.StatusUpdate, + d: { + since: null, + afk: false, + ...data, + }, + }, shard.id); + }); } export function chooseRandom(array: T[]) { diff --git a/src/ws/shard.ts b/src/ws/shard.ts index 800348e40..f6627d17a 100644 --- a/src/ws/shard.ts +++ b/src/ws/shard.ts @@ -6,14 +6,15 @@ import { DiscordPayload, FetchMembersOptions, GatewayOpcode, + GatewayStatusUpdatePayload, ReadyPayload, } from "../types/mod.ts"; -import { BotStatusRequest, delay } from "../util/utils.ts"; +import { delay } from "../util/utils.ts"; import { decompressWith } from "./deps.ts"; import { handleDiscordPayload } from "./shard_manager.ts"; import { Collection } from "../util/collection.ts"; -const basicShards = new Collection(); +export const basicShards = new Collection(); const heartbeating = new Map(); const utf8decoder = new TextDecoder(); const RequestMembersQueue: RequestMemberQueuedRequest[] = []; @@ -383,25 +384,6 @@ async function processGatewayQueue() { await processGatewayQueue(); } -export function botGatewayStatusRequest(payload: BotStatusRequest) { - basicShards.forEach((shard) => { - sendWS({ - op: GatewayOpcode.StatusUpdate, - d: { - since: null, - game: payload.game.name - ? { - name: payload.game.name, - type: payload.game.type, - } - : null, - status: payload.status, - afk: false, - }, - }, shard.id); - }); -} - /** Enqueues the specified data to be transmitted to the server over the WebSocket connection, */ export function sendWS(payload: DiscordPayload, shardID = 0) { const shard = basicShards.get(shardID); diff --git a/src/ws/shard_manager.ts b/src/ws/shard_manager.ts index b43a8eaa5..f2b244c75 100644 --- a/src/ws/shard_manager.ts +++ b/src/ws/shard_manager.ts @@ -8,15 +8,12 @@ import { DiscordPayload, FetchMembersOptions, GatewayOpcode, + GatewayStatusUpdatePayload, } from "../types/mod.ts"; import { cache } from "../util/cache.ts"; import { Collection } from "../util/collection.ts"; -import { BotStatusRequest, delay } from "../util/utils.ts"; -import { - botGatewayStatusRequest, - createShard, - requestGuildMembers, -} from "./mod.ts"; +import { delay } from "../util/utils.ts"; +import { createShard, requestGuildMembers } from "./mod.ts"; let createNextShard = true; @@ -108,15 +105,3 @@ export async function requestAllMembers( options, ); } - -export function sendGatewayCommand( - type: "EDIT_BOTS_STATUS", - // deno-lint-ignore no-explicit-any - payload: Record, -) { - if (type === "EDIT_BOTS_STATUS") { - botGatewayStatusRequest(payload as BotStatusRequest); - } - - return; -} From 2c3692810d19f3124edb7669839aef2f5c27db02 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Mon, 8 Mar 2021 17:37:06 +0100 Subject: [PATCH 060/200] refactor(structures): rename create* functions (#395) * structures: rename structure create functions * fix: errors occured because of structure rename * fix(controllers): errors occured because of structure rename * fix * fix createServer to createGuild rename * rename create*structure to create*Struct * update docs * Phew * rename createguildrole * fix that * fmt? * idk * why * fixxess * Update member.ts * Update member.ts * ahh found it * revert this * Update mod.ts Co-authored-by: ayntee --- docs/src/advanced/customizations.md | 38 ++++++++++++++--------------- src/api/controllers/channels.ts | 4 +-- src/api/controllers/guilds.ts | 3 +-- src/api/controllers/interactions.ts | 2 +- src/api/controllers/members.ts | 7 +++--- src/api/controllers/messages.ts | 9 +++---- src/api/controllers/misc.ts | 4 +-- src/api/controllers/reactions.ts | 4 +-- src/api/controllers/roles.ts | 4 +-- src/api/handlers/channel.ts | 12 ++++++--- src/api/handlers/guild.ts | 38 ++++++++++++++++------------- src/api/handlers/member.ts | 4 +-- src/api/handlers/message.ts | 4 +-- src/api/handlers/mod.ts | 11 +++------ src/api/handlers/webhook.ts | 6 ++--- src/api/structures/channel.ts | 2 +- src/api/structures/guild.ts | 12 ++++++--- src/api/structures/member.ts | 5 +++- src/api/structures/message.ts | 2 +- src/api/structures/mod.ts | 33 +++++++++++-------------- src/api/structures/role.ts | 2 +- src/api/structures/template.ts | 2 +- test/mod.test.ts | 9 +++---- 23 files changed, 111 insertions(+), 106 deletions(-) diff --git a/docs/src/advanced/customizations.md b/docs/src/advanced/customizations.md index a8a35b385..bffa913b5 100644 --- a/docs/src/advanced/customizations.md +++ b/docs/src/advanced/customizations.md @@ -34,17 +34,17 @@ To begin customizing, create a file in the structures folder called `member.ts`. The name of the file is not important at all. ```ts -async function createMember() { +async function createMemberStruct() { } ``` We start by declaring a function that will be run to create the structure. Once again the name here is not important. The function must take the same arguments -that the internal function takes. In this case the createMember function takes 2 -arguments. `data: MemberCreatePayload, guildID: string` +that the internal function takes. In this case the createMemberStruct function +takes 2 arguments. `data: MemberCreatePayload, guildID: string` ```ts -async function createMember(data: MemberCreatePayload, guildID: string) { +async function createMemberStruct(data: MemberCreatePayload, guildID: string) { } ``` @@ -53,7 +53,7 @@ want. My recommendation is to start by copying the current code from the internal libraries structure. ```ts -async function createMember(data: MemberCreatePayload, guildID: string) { +async function createMemberStruct(data: MemberCreatePayload, guildID: string) { const { joined_at: joinedAt, premium_since: premiumSince, @@ -92,7 +92,7 @@ and `guild` properties to the member. ```ts import { rawAvatarURL } from "../../deps.ts"; -async function createMember(data: MemberCreatePayload, guildID: string) { +async function createMemberStruct(data: MemberCreatePayload, guildID: string) { // Hidden code here to make it easier to see the changes const member = { @@ -116,11 +116,11 @@ async function createMember(data: MemberCreatePayload, guildID: string) { ``` Now we need to use this function and telling Discordeno to override the internal -createMember function. To do this, we will modify the internal functions. This -is where we reassign the value of the function. +createMemberStruct function. To do this, we will modify the internal functions. +This is where we reassign the value of the function. ```ts -structures.createMember = createMember; +structures.createMemberStruct = createMemberStruct; ``` Awesome. Now, we have one more step to complete which is to declare these new @@ -144,7 +144,7 @@ declare module "../../deps.ts" { The code should look like this right now: ```ts -async function createMember(data: MemberCreatePayload, guildID: string) { +async function createMemberStruct(data: MemberCreatePayload, guildID: string) { const { joined_at: joinedAt, premium_since: premiumSince, @@ -184,7 +184,7 @@ async function createMember(data: MemberCreatePayload, guildID: string) { return member; } -structures.createMember = createMember; +structures.createMemberStruct = createMemberStruct; declare module "../../deps.ts" { interface Member { @@ -268,7 +268,7 @@ import { rawAvatarURL, } from "../../deps.ts"; -async function createMember(data: MemberCreatePayload, guildID: string) { +async function createMemberStruct(data: MemberCreatePayload, guildID: string) { const { id, bot, @@ -291,7 +291,7 @@ async function createMember(data: MemberCreatePayload, guildID: string) { }; } -structures.createMember = createMember; +structures.createMemberStruct = createMemberStruct; declare module "../../deps.ts" { interface Member { @@ -304,15 +304,15 @@ declare module "../../deps.ts" { } ``` -You might be seeing an error on `structures.createMember`. This is happening -because our new member structures is modifying/removing existing properties that -the lib internally said it would have. To solve this, simply just add a -ts-ignore above it as you know better than TS that the typings are being -overwritten. +You might be seeing an error on `structures.createMemberStruct`. This is +happening because our new member structures is modifying/removing existing +properties that the lib internally said it would have. To solve this, simply +just add a ts-ignore above it as you know better than TS that the typings are +being overwritten. ```ts // @ts-ignore -structures.createMember = createMember; +structures.createMemberStruct = createMemberStruct; ``` ## Custom Cache diff --git a/src/api/controllers/channels.ts b/src/api/controllers/channels.ts index 455d13bc7..7b2711cc6 100644 --- a/src/api/controllers/channels.ts +++ b/src/api/controllers/channels.ts @@ -11,8 +11,8 @@ export async function handleInternalChannelCreate(data: DiscordPayload) { if (data.t !== "CHANNEL_CREATE") return; const payload = data.d as ChannelCreatePayload; - const channelStruct = await structures.createChannel(payload); + const channelStruct = await structures.createChannelStruct(payload); await cacheHandlers.set("channels", channelStruct.id, channelStruct); eventHandlers.channelCreate?.(channelStruct); @@ -58,8 +58,8 @@ export async function handleInternalChannelUpdate(data: DiscordPayload) { const payload = data.d as ChannelCreatePayload; const cachedChannel = await cacheHandlers.get("channels", payload.id); - const channelStruct = await structures.createChannel(payload); + const channelStruct = await structures.createChannelStruct(payload); await cacheHandlers.set("channels", channelStruct.id, channelStruct); if (!cachedChannel) return; diff --git a/src/api/controllers/guilds.ts b/src/api/controllers/guilds.ts index 48d49af19..c15337181 100644 --- a/src/api/controllers/guilds.ts +++ b/src/api/controllers/guilds.ts @@ -22,11 +22,10 @@ export async function handleInternalGuildCreate( // When shards resume they emit GUILD_CREATE again. if (await cacheHandlers.has("guilds", payload.id)) return; - const guildStruct = await structures.createGuild( + const guildStruct = await structures.createGuildStruct( data.d as CreateGuildPayload, shardID, ); - await cacheHandlers.set("guilds", guildStruct.id, guildStruct); if (await cacheHandlers.has("unavailableGuilds", payload.id)) { diff --git a/src/api/controllers/interactions.ts b/src/api/controllers/interactions.ts index b6feca382..f1ee04c4e 100644 --- a/src/api/controllers/interactions.ts +++ b/src/api/controllers/interactions.ts @@ -11,7 +11,7 @@ export async function handleInternalInteractionCreate(data: DiscordPayload) { if (data.t !== "INTERACTION_CREATE") return; const payload = data.d as InteractionCommandPayload; - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( payload.member, payload.guild_id, ); diff --git a/src/api/controllers/members.ts b/src/api/controllers/members.ts index 9c2c83aca..0f940203f 100644 --- a/src/api/controllers/members.ts +++ b/src/api/controllers/members.ts @@ -19,7 +19,7 @@ export async function handleInternalGuildMemberAdd(data: DiscordPayload) { if (!guild) return; guild.memberCount++; - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( payload, payload.guild_id, ); @@ -66,7 +66,7 @@ export async function handleInternalGuildMemberUpdate(data: DiscordPayload) { mute: guildMember?.mute || false, roles: payload.roles, }; - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( newMemberData, payload.guild_id, ); @@ -109,11 +109,10 @@ export async function handleInternalGuildMembersChunk(data: DiscordPayload) { const members = await Promise.all( payload.members.map(async (member) => { - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( member, payload.guild_id, ); - await cacheHandlers.set("members", memberStruct.id, memberStruct); return memberStruct; diff --git a/src/api/controllers/messages.ts b/src/api/controllers/messages.ts index cc4a319a4..7f74a9713 100644 --- a/src/api/controllers/messages.ts +++ b/src/api/controllers/messages.ts @@ -21,18 +21,17 @@ export async function handleInternalMessageCreate(data: DiscordPayload) { if (payload.member && guild) { // If in a guild cache the author as a member - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( { ...payload.member, user: payload.author }, guild.id, ); - await cacheHandlers.set("members", memberStruct.id, memberStruct); } await Promise.all(payload.mentions.map(async (mention) => { // Cache the member if its a valid member if (mention.member && guild) { - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( { ...mention.member, user: mention }, guild.id, ); @@ -41,7 +40,7 @@ export async function handleInternalMessageCreate(data: DiscordPayload) { } })); - const message = await structures.createMessage(payload); + const message = await structures.createMessageStruct(payload); // Cache the message await cacheHandlers.set("messages", payload.id, message); @@ -106,7 +105,7 @@ export async function handleInternalMessageUpdate(data: DiscordPayload) { return; } - const message = await structures.createMessage(payload); + const message = await structures.createMessageStruct(payload); await cacheHandlers.set("messages", payload.id, message); diff --git a/src/api/controllers/misc.ts b/src/api/controllers/misc.ts index 3a4a87dd6..95bdc58d0 100644 --- a/src/api/controllers/misc.ts +++ b/src/api/controllers/misc.ts @@ -54,7 +54,7 @@ export async function handleInternalReady( for (const [guildID, members] of initialMemberLoadQueue.entries()) { await Promise.all( members.map(async (member) => { - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( member, guildID, ); @@ -125,7 +125,7 @@ export async function handleInternalVoiceStateUpdate(data: DiscordPayload) { if (!guild) return; const member = payload.member - ? await structures.createMember(payload.member, guild.id) + ? await structures.createMemberStruct(payload.member, guild.id) : await cacheHandlers.get("members", payload.user_id); if (!member) return; diff --git a/src/api/controllers/reactions.ts b/src/api/controllers/reactions.ts index 50e83d0bb..761eb6d65 100644 --- a/src/api/controllers/reactions.ts +++ b/src/api/controllers/reactions.ts @@ -39,7 +39,7 @@ export async function handleInternalMessageReactionAdd(data: DiscordPayload) { if (payload.member && payload.guild_id) { const guild = await cacheHandlers.get("guilds", payload.guild_id); if (guild) { - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( payload.member, guild.id, ); @@ -95,7 +95,7 @@ export async function handleInternalMessageReactionRemove( if (payload.member && payload.guild_id) { const guild = await cacheHandlers.get("guilds", payload.guild_id); if (guild) { - const memberStruct = await structures.createMember( + const memberStruct = await structures.createMemberStruct( payload.member, guild.id, ); diff --git a/src/api/controllers/roles.ts b/src/api/controllers/roles.ts index 63292058e..6debe7372 100644 --- a/src/api/controllers/roles.ts +++ b/src/api/controllers/roles.ts @@ -14,7 +14,7 @@ export async function handleInternalGuildRoleCreate(data: DiscordPayload) { const guild = await cacheHandlers.get("guilds", payload.guild_id); if (!guild) return; - const role = await structures.createRole(payload.role); + const role = await structures.createRoleStruct(payload.role); guild.roles = guild.roles.set(payload.role.id, role); await cacheHandlers.set("guilds", payload.guild_id, guild); @@ -58,7 +58,7 @@ export async function handleInternalGuildRoleUpdate(data: DiscordPayload) { const cachedRole = guild.roles.get(payload.role.id); if (!cachedRole) return; - const role = await structures.createRole(payload.role); + const role = await structures.createRoleStruct(payload.role); guild.roles.set(payload.role.id, role); await cacheHandlers.set("guilds", guild.id, guild); diff --git a/src/api/handlers/channel.ts b/src/api/handlers/channel.ts index aaaa998f5..16c687859 100644 --- a/src/api/handlers/channel.ts +++ b/src/api/handlers/channel.ts @@ -76,7 +76,7 @@ export async function getMessage( endpoints.CHANNEL_MESSAGE(channelID, id), ) as MessageCreateOptions; - return structures.createMessage(result); + return structures.createMessageStruct(result); } /** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ @@ -115,7 +115,9 @@ export async function getMessages( options, )) as MessageCreateOptions[]; - return Promise.all(result.map((res) => structures.createMessage(res))); + return Promise.all( + result.map((res) => structures.createMessageStruct(res)), + ); } /** Get pinned messages in this channel. */ @@ -124,7 +126,9 @@ export async function getPins(channelID: string) { endpoints.CHANNEL_PINS(channelID), )) as MessageCreateOptions[]; - return Promise.all(result.map((res) => structures.createMessage(res))); + return Promise.all( + result.map((res) => structures.createMessageStruct(res)), + ); } /** @@ -275,7 +279,7 @@ export async function sendMessage( }, ) as MessageCreateOptions; - return structures.createMessage(result); + return structures.createMessageStruct(result as MessageCreateOptions); } /** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */ diff --git a/src/api/handlers/guild.ts b/src/api/handlers/guild.ts index 99f3bcfa5..e0795a526 100644 --- a/src/api/handlers/guild.ts +++ b/src/api/handlers/guild.ts @@ -44,13 +44,13 @@ import { cacheHandlers } from "../controllers/cache.ts"; import { Guild, Member, structures } from "../structures/mod.ts"; /** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */ -export async function createServer(options: CreateServerOptions) { +export async function createGuild(options: CreateServerOptions) { const guild = await RequestManager.post( endpoints.GUILDS, options, ) as CreateGuildPayload; - return structures.createGuild(guild, 0); + return structures.createGuildStruct(guild, 0); } /** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event. @@ -136,8 +136,7 @@ export async function createGuildChannel( type: options?.type || ChannelTypes.GUILD_TEXT, })) as ChannelCreatePayload; - const channelStruct = await structures.createChannel(result); - + const channelStruct = await structures.createChannelStruct(result); await cacheHandlers.set("channels", channelStruct.id, channelStruct); return channelStruct; @@ -186,7 +185,7 @@ export async function getChannels(guildID: string, addToCache = true) { ) as ChannelCreatePayload[]; return Promise.all(result.map(async (res) => { - const channelStruct = await structures.createChannel(res, guildID); + const channelStruct = await structures.createChannelStruct(res, guildID); if (addToCache) { await cacheHandlers.set("channels", channelStruct.id, channelStruct); } @@ -204,7 +203,10 @@ export async function getChannel(channelID: string, addToCache = true) { endpoints.CHANNEL_BASE(channelID), ) as ChannelCreatePayload; - const channelStruct = await structures.createChannel(result, result.guild_id); + const channelStruct = await structures.createChannelStruct( + result, + result.guild_id, + ); if (addToCache) { await cacheHandlers.set("channels", channelStruct.id, channelStruct); } @@ -293,8 +295,7 @@ export async function getMember( endpoints.GUILD_MEMBER(guildID, id), ) as MemberCreatePayload; - const memberStruct = await structures.createMember(data, guildID); - + const memberStruct = await structures.createMemberStruct(data, guildID); await cacheHandlers.set("members", memberStruct.id, memberStruct); return memberStruct; @@ -439,7 +440,7 @@ export async function getEmoji( } /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ -export async function createGuildRole( +export async function createRole( guildID: string, options: CreateRoleOptions, reason?: string, @@ -459,7 +460,7 @@ export async function createGuildRole( ); const roleData = result as RoleData; - const role = await structures.createRole(roleData); + const role = await structures.createRoleStruct(roleData); const guild = await cacheHandlers.get("guilds", guildID); guild?.roles.set(role.id, role); @@ -646,7 +647,10 @@ export async function getMembers( const memberStructures = await Promise.all( result.map(async (member) => { - const memberStruct = await structures.createMember(member, guildID); + const memberStruct = await structures.createMemberStruct( + member, + guildID, + ); await cacheHandlers.set("members", memberStruct.id, memberStruct); @@ -962,7 +966,7 @@ export async function getTemplate(templateCode: string) { const result = await RequestManager.get( endpoints.GUILD_TEMPLATE(templateCode), ) as GuildTemplate; - const template = await structures.createTemplate(result); + const template = await structures.createTemplateStruct(result); return template; } @@ -1016,7 +1020,7 @@ export async function getGuildTemplates(guildID: string) { endpoints.GUILD_TEMPLATES(guildID), ) as GuildTemplate[]; - return templates.map((template) => structures.createTemplate(template)); + return templates.map((template) => structures.createTemplateStruct(template)); } /** @@ -1034,7 +1038,7 @@ export async function deleteGuildTemplate( `${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`, ) as GuildTemplate; - return structures.createTemplate(deletedTemplate); + return structures.createTemplateStruct(deletedTemplate); } /** @@ -1066,7 +1070,7 @@ export async function createGuildTemplate( data, ) as GuildTemplate; - return structures.createTemplate(template); + return structures.createTemplateStruct(template); } /** @@ -1081,7 +1085,7 @@ export async function syncGuildTemplate(guildID: string, templateCode: string) { `${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`, ) as GuildTemplate; - return structures.createTemplate(template); + return structures.createTemplateStruct(template); } /** @@ -1112,5 +1116,5 @@ export async function editGuildTemplate( data, ) as GuildTemplate; - return structures.createTemplate(template); + return structures.createTemplateStruct(template); } diff --git a/src/api/handlers/member.ts b/src/api/handlers/member.ts index c877cf1a6..b28170a3c 100644 --- a/src/api/handlers/member.ts +++ b/src/api/handlers/member.ts @@ -132,7 +132,7 @@ export async function sendDirectMessage( endpoints.USER_DM, { recipient_id: memberID }, ) as DMChannelCreatePayload; - const channelStruct = await structures.createChannel( + const channelStruct = await structures.createChannelStruct( dmChannelData as unknown as ChannelCreatePayload, ); // Recreate the channel and add it undert he users id @@ -229,7 +229,7 @@ export async function editMember( endpoints.GUILD_MEMBER(guildID, memberID), options, ) as MemberCreatePayload; - const member = await structures.createMember(result, guildID); + const member = await structures.createMemberStruct(result, guildID); return member; } diff --git a/src/api/handlers/message.ts b/src/api/handlers/message.ts index 24c6579bb..7c6bf553b 100644 --- a/src/api/handlers/message.ts +++ b/src/api/handlers/message.ts @@ -324,7 +324,7 @@ export async function editMessage( content, ); - return structures.createMessage(result as MessageCreateOptions); + return structures.createMessageStruct(result as MessageCreateOptions); } /** Crosspost a message in a News Channel to following channels. */ @@ -333,5 +333,5 @@ export async function publishMessage(channelID: string, messageID: string) { endpoints.CHANNEL_MESSAGE_CROSSPOST(channelID, messageID), ) as MessageCreateOptions; - return structures.createMessage(data); + return structures.createMessageStruct(data); } diff --git a/src/api/handlers/mod.ts b/src/api/handlers/mod.ts index cb18f6431..c291ab020 100644 --- a/src/api/handlers/mod.ts +++ b/src/api/handlers/mod.ts @@ -20,11 +20,11 @@ import { ban, categoryChildrenIDs, createEmoji, + createGuild, createGuildChannel, createGuildFromTemplate, - createGuildRole, createGuildTemplate, - createServer, + createRole, deleteChannel, deleteChannelOverwrite, deleteEmoji, @@ -33,7 +33,6 @@ import { deleteRole, deleteServer, editChannelOverwrite, - editEmbed, editEmoji, editGuild, editGuildTemplate, @@ -48,7 +47,6 @@ import { getBans, getChannel, getChannels, - getEmbed, getEmoji, getEmojis, getGuild, @@ -153,9 +151,9 @@ export let handlers = { createEmoji, createGuildChannel, createGuildFromTemplate, - createGuildRole, + createRole, createGuildTemplate, - createServer, + createGuild, deleteChannel, deleteEmoji, deleteGuildTemplate, @@ -176,7 +174,6 @@ export let handlers = { getChannel, getChannels, getWidgetSettings, - getEmbed, getEmoji, getEmojis, getGuild, diff --git a/src/api/handlers/webhook.ts b/src/api/handlers/webhook.ts index 1d2b56d8e..6047f0114 100644 --- a/src/api/handlers/webhook.ts +++ b/src/api/handlers/webhook.ts @@ -203,7 +203,7 @@ export async function executeWebhook( ); if (!options.wait) return; - return structures.createMessage(result as MessageCreateOptions); + return structures.createMessageStruct(result as MessageCreateOptions); } export async function editWebhookMessage( @@ -257,7 +257,7 @@ export async function editWebhookMessage( { ...options, allowed_mentions: options.allowed_mentions }, ) as MessageCreateOptions; - const message = await structures.createMessage(result); + const message = await structures.createMessageStruct(result); return message; } @@ -603,7 +603,7 @@ export async function editSlashResponse( // If the original message was edited, this will not return a message if (!options.messageID) return result; - const message = await structures.createMessage( + const message = await structures.createMessageStruct( result as MessageCreateOptions, ); return message; diff --git a/src/api/structures/channel.ts b/src/api/structures/channel.ts index 71bb8ec27..35eeb9c53 100644 --- a/src/api/structures/channel.ts +++ b/src/api/structures/channel.ts @@ -77,7 +77,7 @@ const baseChannel: Partial = { }; // deno-lint-ignore require-await -export async function createChannel( +export async function createChannelStruct( data: ChannelCreatePayload, guildID?: string, ) { diff --git a/src/api/structures/guild.ts b/src/api/structures/guild.ts index 63e4c9b18..e7b8f19f9 100644 --- a/src/api/structures/guild.ts +++ b/src/api/structures/guild.ts @@ -107,7 +107,10 @@ const baseGuild: Partial = { }, }; -export async function createGuild(data: CreateGuildPayload, shardID: number) { +export async function createGuildStruct( + data: CreateGuildPayload, + shardID: number, +) { const { disovery_splash: discoverySplash, default_message_notifications: defaultMessageNotifications, @@ -143,11 +146,14 @@ export async function createGuild(data: CreateGuildPayload, shardID: number) { } = data; const roles = await Promise.all( - data.roles.map((role) => structures.createRole(role)), + data.roles.map((role) => structures.createRoleStruct(role)), ); await Promise.all(channels.map(async (channel) => { - const channelStruct = await structures.createChannel(channel, rest.id); + const channelStruct = await structures.createChannelStruct( + channel, + rest.id, + ); return cacheHandlers.set("channels", channelStruct.id, channelStruct); })); diff --git a/src/api/structures/member.ts b/src/api/structures/member.ts index 3b385172f..df18dfa29 100644 --- a/src/api/structures/member.ts +++ b/src/api/structures/member.ts @@ -72,7 +72,10 @@ const baseMember: Partial = { }, }; -export async function createMember(data: MemberCreatePayload, guildID: string) { +export async function createMemberStruct( + data: MemberCreatePayload, + guildID: string, +) { const { joined_at: joinedAt, premium_since: premiumSince, diff --git a/src/api/structures/message.ts b/src/api/structures/message.ts index 629d435c4..c6e0240cc 100644 --- a/src/api/structures/message.ts +++ b/src/api/structures/message.ts @@ -133,7 +133,7 @@ const baseMessage: Partial = { }, }; -export async function createMessage(data: MessageCreateOptions) { +export async function createMessageStruct(data: MessageCreateOptions) { const { guild_id: guildID = "", channel_id: channelID, diff --git a/src/api/structures/mod.ts b/src/api/structures/mod.ts index 8a2896232..8c3e44724 100644 --- a/src/api/structures/mod.ts +++ b/src/api/structures/mod.ts @@ -1,20 +1,22 @@ -import { createChannel } from "./channel.ts"; -import { createGuild } from "./guild.ts"; -import { createMember } from "./member.ts"; -import { createMessage } from "./message.ts"; -import { createRole } from "./role.ts"; -import { createTemplate } from "./template.ts"; +import { Channel, createChannelStruct } from "./channel.ts"; +import { createGuildStruct, Guild } from "./guild.ts"; +import { createMemberStruct, Member } from "./member.ts"; +import { createMessageStruct, Message } from "./message.ts"; +import { createRoleStruct, Role } from "./role.ts"; +import { createTemplateStruct, Template } from "./template.ts"; /** This is the placeholder where the structure creation functions are kept. */ export let structures = { - createChannel, - createGuild, - createMember, - createMessage, - createRole, - createTemplate, + createChannelStruct, + createGuildStruct, + createMemberStruct, + createMessageStruct, + createRoleStruct, + createTemplateStruct, }; +export type { Channel, Guild, Member, Message, Role, Template }; + export type Structures = typeof structures; /** This function is used to update/reload/customize the internal structures of Discordeno. @@ -28,10 +30,3 @@ export function updateStructures(newStructures: Structures) { ...newStructures, }; } - -export type { Channel } from "./channel.ts"; -export type { Guild } from "./guild.ts"; -export type { Member } from "./member.ts"; -export type { Message } from "./message.ts"; -export type { Role } from "./role.ts"; -export type { Template } from "./template.ts"; diff --git a/src/api/structures/role.ts b/src/api/structures/role.ts index ca09e60fd..1b2282cdb 100644 --- a/src/api/structures/role.ts +++ b/src/api/structures/role.ts @@ -67,7 +67,7 @@ const baseRole: Partial = { }; // deno-lint-ignore require-await -export async function createRole({ tags = {}, ...rest }: RoleData) { +export async function createRoleStruct({ tags = {}, ...rest }: RoleData) { const restProps: Record> = {}; for (const key of Object.keys(rest)) { // @ts-ignore index signature diff --git a/src/api/structures/template.ts b/src/api/structures/template.ts index 77e574651..79b45d38e 100644 --- a/src/api/structures/template.ts +++ b/src/api/structures/template.ts @@ -11,7 +11,7 @@ const baseTemplate: Partial