From f7f4b7c384a96787d1b6171a557261ecdd9a02e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=B1=E6=81=A9Kane?= <33802653+Gary50613@users.noreply.github.com> Date: Wed, 25 May 2022 21:48:57 +0800 Subject: [PATCH 1/4] targetApplicationId missing (#2244) --- helpers/invites/createInvite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/invites/createInvite.ts b/helpers/invites/createInvite.ts index 03dabb428..4e2a3dfd5 100644 --- a/helpers/invites/createInvite.ts +++ b/helpers/invites/createInvite.ts @@ -15,7 +15,7 @@ export async function createInvite(bot: Bot, channelId: bigint, options: CreateC unique: options.unique, target_type: options.targetType, target_user_id: options.targetUserId, - target_application_id: options.targetUserId, + target_application_id: options.targetApplicationId, }, ); From 53d7b3104154aa7aaeeeab60ef6b3b499395bffe Mon Sep 17 00:00:00 2001 From: ITOH Date: Wed, 25 May 2022 15:50:31 +0200 Subject: [PATCH 2/4] refactor(.github,README,gateway,plugins/fileloader,tests,types)!: make intent calculation manual (#2243) This changes the calculation of intents to be manual to the dev. This is to improve overall consistency of our code base, also it is not a big drawback for users since intents are usually done once and then never (seldom) touched again. --- .github/ISSUE_TEMPLATE/bug_report.md | 4 +--- README.md | 4 ++-- bot.ts | 7 ++----- gateway/README.md | 2 +- gateway/gatewayManager.ts | 7 ++----- plugins/fileloader/README.md | 4 ++-- site/docs/big-bot-guide/events.md | 7 ++++--- site/docs/big-bot-guide/gateway.md | 2 +- site/docs/general/getting-started.md | 4 ++-- site/docs/general/migrating.md | 2 +- tests/mod.ts | 21 ++++++++++----------- testss/mod.ts | 2 +- types/shared.ts | 6 ++++++ 13 files changed, 35 insertions(+), 37 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e86a4123f..9c03e674b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -22,9 +22,7 @@ const bot = createBot({ events: { // ADD EVENTS NEEDED TO SHOW THE BUG HERE }, - intents: [ - // ADD INTENTS NEEDED HERE FOR YOUR TEST - ], + intents: 0, // ADD INTENTS NEEDED HERE FOR YOUR TEST IF NECESSARY }); await startBot(bot); diff --git a/README.md b/README.md index 3ea81f268..b9c8cdd43 100644 --- a/README.md +++ b/README.md @@ -100,12 +100,12 @@ Have your cache setup in any way you like. Redis, PGSQL or any cache layer you w Here is a minimal example to get started with: ```typescript -import { createBot, startBot } from "https://deno.land/x/discordeno@13.0.0-rc18/mod.ts"; +import { createBot, Intents, startBot } from "https://deno.land/x/discordeno@13.0.0-rc18/mod.ts"; import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.18/mod.ts"; const baseBot = createBot({ token: Deno.env.get("DISCORD_TOKEN"), - intents: ["Guilds", "GuildMessages"], + intents: Intents.Guilds | Intents.GuildMessages, botId: Deno.env.get("BOT_ID"), events: { ready() { diff --git a/bot.ts b/bot.ts index 0a7652b9f..e8ba1ac3d 100644 --- a/bot.ts +++ b/bot.ts @@ -145,10 +145,7 @@ export function createBot(options: CreateBotOptions): Bot { applicationId: options.applicationId || options.botId, token: removeTokenPrefix(options.token), events: createEventHandlers(options.events ?? {}), - intents: (options.intents ?? []).reduce( - (bits, next) => (bits |= GatewayIntents[next]), - 0, - ), + intents: options.intents, botGatewayData: options.botGatewayData, activeGuildIds: new Set(), constants: createBotConstants(), @@ -318,7 +315,7 @@ export interface CreateBotOptions { applicationId?: bigint; secretKey?: string; events?: Partial; - intents?: (keyof typeof GatewayIntents)[]; + intents?: GatewayIntents; botGatewayData?: GetGatewayBot; rest?: Omit; handleDiscordPayload?: GatewayManager["handleDiscordPayload"]; diff --git a/gateway/README.md b/gateway/README.md index 9cf62e678..fb5a88f2c 100644 --- a/gateway/README.md +++ b/gateway/README.md @@ -57,7 +57,7 @@ startGateway({ /** Whether or not to use compression for gateway payloads. */ compress: true, /** The intents you would like to enable. */ - intents: ["GUILDS", "GUILD_MESSAGES"], + intents: Intents.Guilds | Intents.GuildMessages, /** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */ maxShards: 885, /** The first shard Id for this group of shards. */ diff --git a/gateway/gatewayManager.ts b/gateway/gatewayManager.ts index cc5312010..0d3748425 100644 --- a/gateway/gatewayManager.ts +++ b/gateway/gatewayManager.ts @@ -59,10 +59,7 @@ export function createGatewayManager( $os: options.$os ?? "linux", $browser: options.$browser ?? "Discordeno", $device: options.$device ?? "Discordeno", - intents: - (Array.isArray(options.intents) - ? options.intents.reduce((bits, next) => (bits |= GatewayIntents[next]), 0) - : options.intents) ?? 0, + intents: options.intents ?? 0, shard: options.shard ?? [0, options.shardsRecommended ?? 1], presence: options.presence, urlWSS: options.urlWSS ?? "wss://gateway.discord.gg/?v=9&encoding=json", @@ -131,7 +128,7 @@ export interface GatewayManager { $os: string; $browser: string; $device: string; - intents: number | (keyof typeof GatewayIntents)[]; + intents: GatewayIntents; shard: [number, number]; presence?: Omit; diff --git a/plugins/fileloader/README.md b/plugins/fileloader/README.md index b82f8ea26..490008f8d 100644 --- a/plugins/fileloader/README.md +++ b/plugins/fileloader/README.md @@ -5,7 +5,7 @@ This plugin leverages the ability to write files, and then import them. ## Code Example ```typescript -import { createBot, enableFileLoaderPlugin, startBot } from "./deps.ts"; // Import discordeno and this plugin. +import { createBot, enableFileLoaderPlugin, Intents, startBot } from "./deps.ts"; // Import discordeno and this plugin. console.log("Starting Up the Bot, this might take awhile..."); @@ -13,7 +13,7 @@ const bot = enableFileLoaderPlugin( createBot({ token: "", // Your bot's token botId: 0n, // Your bot's "Application Id", - intents: [], + intents: Intents.Guilds, events: { ready() { console.log("Bot Ready"); diff --git a/site/docs/big-bot-guide/events.md b/site/docs/big-bot-guide/events.md index 1610e492c..70c9a3fe1 100644 --- a/site/docs/big-bot-guide/events.md +++ b/site/docs/big-bot-guide/events.md @@ -25,14 +25,14 @@ Create a file path like `src/bot/mod.ts`. ```ts import { DISCORD_TOKEN } from "../../configs.ts"; -import { Collection, createBot } from "../../deps.ts"; +import { Collection, createBot, Intents } from "../../deps.ts"; import { psql } from "./cache/mod.ts"; export const bot = createBot({ token: DISCORD_TOKEN, botId: 270010330782892032n, // applicationId: 270010330782892032, - intents: ["Guilds", "GuildMessages"], + intents: Intents.Guilds | Intents.GuildMessages, events: { messageCreate: function (bot, message) { console.log("message arrived"); @@ -126,7 +126,8 @@ Alright that was a lot of code. Now let's break it down little by little. developers have mentioned that this behavior is not documented and not supposed to be relied on to remain stable. Due to these reasons, we chose to just require the bot id be passed here. - `applicationId` is an optional choice if your bot is old and has a unique id different from it's bot id. -- `intents`: Provide the intents you like using strings or a number. String form supports autocomplete and type safety. +- `intents`: Provide the intents you like using a bitwise OR operation (eg. `Intents.Guilds | Intents.GuildsMessages`). + String form supports autocomplete and type safety. - `events`: These are your event handler functions. When a MESSAGE_CREATE event arrives from Discord it will be processed here. We will set up the routing to run these functions later in the guide but for now you can see how to set it up. Note, you can create these functions in separate files and just import them here as you wish. diff --git a/site/docs/big-bot-guide/gateway.md b/site/docs/big-bot-guide/gateway.md index 84bafb68d..76ed03a61 100644 --- a/site/docs/big-bot-guide/gateway.md +++ b/site/docs/big-bot-guide/gateway.md @@ -111,7 +111,7 @@ With this info, we can now create our gateway manager. const gateway = createGatewayManager({ secretKey: EVENT_HANDLER_SECRET_KEY, token: DISCORD_TOKEN, - intents: ["GuildMessages", "Guilds"], + intents: Intents.Guilds | Intents.GuildMessages, shardsRecommended: result.shards, sessionStartLimitTotal: result.sessionStartLimit.total, sessionStartLimitRemaining: result.sessionStartLimit.remaining, diff --git a/site/docs/general/getting-started.md b/site/docs/general/getting-started.md index eaab47683..80d8ac0bc 100644 --- a/site/docs/general/getting-started.md +++ b/site/docs/general/getting-started.md @@ -45,11 +45,11 @@ Starting with Discordeno is very simple, you can start from scratch without any of code into a new TypeScript file: ```ts -import { startBot } from "https://deno.land/x/discordeno/mod.ts"; +import { Intents, startBot } from "https://deno.land/x/discordeno/mod.ts"; startBot({ token: "BOT TOKEN", - intents: ["GUILDS", "GUILD_MESSAGES"], + intents: Intents.Guilds | Intents.GuildMessages, eventHandlers: { ready() { console.log("Successfully connected to gateway"); diff --git a/site/docs/general/migrating.md b/site/docs/general/migrating.md index 746d293bb..69a5411eb 100644 --- a/site/docs/general/migrating.md +++ b/site/docs/general/migrating.md @@ -161,7 +161,7 @@ startBot({ token: configs.token, // Pick the intents you wish to have for your bot. // For instance, to work with guild message reactions, you will have to pass the Intents.GUILD_MESSAGE_REACTIONS intent to the array. - intents: [Intents.GUILDS, Intents.GUILD_MESSAGES], + intents: Intents.Guilds | Intents.GuildMessages, // These are all your event handler functions. Imported from the events folder eventHandlers: botCache.eventHandlers, }); diff --git a/tests/mod.ts b/tests/mod.ts index 4133ced6e..50a7c960e 100644 --- a/tests/mod.ts +++ b/tests/mod.ts @@ -26,6 +26,7 @@ import { categoryChildrenTest } from "./helpers/channels/categoryChannels.ts"; import { deleteChannelOverwriteTests } from "./helpers/channels/deleteChannelOverwrite.ts"; import { editChannelTests } from "./helpers/channels/editChannel.ts"; import { CACHED_COMMUNITY_GUILD_ID, sanitizeMode } from "./constants.ts"; +import { Intents } from "../types/shared.ts"; console.log("[Tests] Starting test preparation"); dotenv({ export: true, path: `${Deno.cwd()}/.env` }); @@ -45,17 +46,15 @@ const baseBot = createBot({ }, // debug: console.log, }), - intents: [ - "Guilds", - "GuildEmojis", - "GuildMessages", - "GuildMessageReactions", - "GuildBans", - "GuildMembers", - "GuildScheduledEvents", - "GuildVoiceStates", - "GuildPresences", - ], + intents: Intents.Guilds | + Intents.GuildEmojis | + Intents.GuildMessages | + Intents.GuildMessageReactions | + Intents.GuildBans | + Intents.GuildMembers | + Intents.GuildScheduledEvents | + Intents.GuildVoiceStates | + Intents.GuildPresences, }); export const bot = enableCachePlugin(baseBot); diff --git a/testss/mod.ts b/testss/mod.ts index c753df119..6b7a87eff 100644 --- a/testss/mod.ts +++ b/testss/mod.ts @@ -10,7 +10,7 @@ export function loadBot() { const botId = BigInt(atob(token.split(".")[0])); const bot = createBot({ events: {}, - intents: [], + intents: 0, botId, token, }); diff --git a/types/shared.ts b/types/shared.ts index 36eeb13fb..51f3b3a73 100644 --- a/types/shared.ts +++ b/types/shared.ts @@ -1151,6 +1151,12 @@ export enum GatewayIntents { GuildScheduledEvents = (1 << 16), } +// ALIASES JUST FOR BETTER UX IN THIS CASE + +/** https://discord.com/developers/docs/topics/gateway#list-of-intents */ +export const Intents = GatewayIntents; +export type Intents = GatewayIntents; + /** https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionresponsetype */ export enum InteractionResponseTypes { /** ACK a `Ping` */ From be5bcb5bbc55a4b74090a15c9d591aed5e850c3b Mon Sep 17 00:00:00 2001 From: ITOH Date: Wed, 25 May 2022 15:53:53 +0200 Subject: [PATCH 3/4] fix(rest)!: `editApplicationCommandPermissions` (#2238) * fix(rest)!: `editApplicationCommandPermissions` - add `bearerToken` option - add headers to `RestPayload` - remove `batchEditApplicationCommandPermissions` since the related endpoint has been removed * fix header prefix --- .../batchEditApplicationCommandPermissions.ts | 29 ------------------- .../editApplicationCommandPermissions.ts | 17 ++++++++++- helpers/interactions/commands/mod.ts | 1 - rest/createRequestBody.ts | 13 +++++++-- rest/rest.ts | 1 + rest/runMethod.ts | 27 +++++++---------- 6 files changed, 38 insertions(+), 50 deletions(-) delete mode 100644 helpers/interactions/commands/batchEditApplicationCommandPermissions.ts diff --git a/helpers/interactions/commands/batchEditApplicationCommandPermissions.ts b/helpers/interactions/commands/batchEditApplicationCommandPermissions.ts deleted file mode 100644 index 1e388db96..000000000 --- a/helpers/interactions/commands/batchEditApplicationCommandPermissions.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { DiscordGuildApplicationCommandPermissions } from "../../../types/discord.ts"; -import { ApplicationCommandPermissionTypes } from "../../../types/shared.ts"; - -/** Batch edits permissions for all commands in a guild. Takes an array of partial GuildApplicationCommandPermissions objects including `id` and `permissions`. */ -export async function batchEditApplicationCommandPermissions( - bot: Bot, - guildId: bigint, - options: { id: string; permissions: ApplicationCommandPermissions[] }[], -) { - const result = await bot.rest.runMethod( - bot.rest, - "put", - bot.constants.endpoints.COMMANDS_PERMISSIONS(bot.applicationId, guildId), - options, - ); - - return result.map((res) => bot.transformers.applicationCommandPermission(bot, res)); -} - -/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandpermissions */ -export interface ApplicationCommandPermissions { - /** The id of the role or user */ - id: string; - /** Role or User */ - type: ApplicationCommandPermissionTypes; - /** `true` to allow, `false`, to disallow */ - permission: boolean; -} diff --git a/helpers/interactions/commands/editApplicationCommandPermissions.ts b/helpers/interactions/commands/editApplicationCommandPermissions.ts index 1f9893e8a..a06af3e1b 100644 --- a/helpers/interactions/commands/editApplicationCommandPermissions.ts +++ b/helpers/interactions/commands/editApplicationCommandPermissions.ts @@ -1,12 +1,14 @@ import type { Bot } from "../../../bot.ts"; import { DiscordGuildApplicationCommandPermissions } from "../../../types/discord.ts"; -import { ApplicationCommandPermissions } from "./batchEditApplicationCommandPermissions.ts"; +import { ApplicationCommandPermissionTypes } from "../../../types/shared.ts"; /** Edits command permissions for a specific command for your application in a guild. */ export async function editApplicationCommandPermissions( bot: Bot, guildId: bigint, commandId: bigint, + /** Bearer token which has the `applications.commands.permissions.update` scope and also access to this guild. */ + bearerToken: string, options: ApplicationCommandPermissions[], ) { const result = await bot.rest.runMethod( @@ -16,7 +18,20 @@ export async function editApplicationCommandPermissions( { permissions: options, }, + { + headers: { authorization: `Bearer ${bearerToken}` }, + }, ); return bot.transformers.applicationCommandPermission(bot, result); } + +/** https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions */ +export interface ApplicationCommandPermissions { + /** The id of the role or user */ + id: string; + /** Role or User */ + type: ApplicationCommandPermissionTypes; + /** `true` to allow, `false`, to disallow */ + permission: boolean; +} diff --git a/helpers/interactions/commands/mod.ts b/helpers/interactions/commands/mod.ts index 2bf6aa764..7d69c7e94 100644 --- a/helpers/interactions/commands/mod.ts +++ b/helpers/interactions/commands/mod.ts @@ -1,4 +1,3 @@ -export * from "./batchEditApplicationCommandPermissions.ts"; export * from "./createApplicationCommand.ts"; export * from "./deleteApplicationCommand.ts"; export * from "./deleteInteractionResponse.ts"; diff --git a/rest/createRequestBody.ts b/rest/createRequestBody.ts index b207d8e7a..c4c34fe48 100644 --- a/rest/createRequestBody.ts +++ b/rest/createRequestBody.ts @@ -5,11 +5,18 @@ import { RestPayload, RestRequest } from "./rest.ts"; /** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */ export function createRequestBody(rest: RestManager, queuedRequest: { request: RestRequest; payload: RestPayload }) { - const headers: { [key: string]: string } = { - Authorization: `Bot ${rest.token}`, - "User-Agent": USER_AGENT, + const headers: Record = { + authorization: `Bot ${rest.token}`, + "user-agent": USER_AGENT, }; + // SOMETIMES SPECIAL HEADERS (E.G. CUSTOM AUTHORIZATION) NEED TO BE USED + if (queuedRequest.payload.headers) { + for (const key in queuedRequest.payload.headers) { + headers[key] = queuedRequest.payload.headers[key]; + } + } + // GET METHODS SHOULD NOT HAVE A BODY if (queuedRequest.request.method.toUpperCase() === "GET") { queuedRequest.payload.body = undefined; diff --git a/rest/rest.ts b/rest/rest.ts index 408673837..a8d32e0ad 100644 --- a/rest/rest.ts +++ b/rest/rest.ts @@ -77,6 +77,7 @@ export interface RestPayload { bucketId?: string; body?: Record; retryCount: number; + headers?: Record; } export interface RestRateLimitedPath { diff --git a/rest/runMethod.ts b/rest/runMethod.ts index f5ca39e5c..c5cab72fd 100644 --- a/rest/runMethod.ts +++ b/rest/runMethod.ts @@ -2,27 +2,21 @@ import { RestManager } from "../bot.ts"; import { API_VERSION, BASE_URL, IMAGE_BASE_URL } from "../util/constants.ts"; import { RestRequestRejection, RestRequestResponse } from "./rest.ts"; -export async function runMethod( - rest: RestManager, - method: "get", - url: string, -): Promise; -export async function runMethod( - rest: RestManager, - method: "post" | "put" | "delete" | "patch", - url: string, - body?: unknown, -): Promise; export async function runMethod( rest: RestManager, method: "get" | "post" | "put" | "delete" | "patch", url: string, body?: unknown, - retryCount = 0, - bucketId?: string, + options?: { + retryCount?: number; + bucketId?: string; + headers?: Record; + }, ): Promise { rest.debug( - `[REST - RequestCreate] Method: ${method} | URL: ${url} | Retry Count: ${retryCount} | Bucket ID: ${bucketId} | Body: ${ + `[REST - RequestCreate] Method: ${method} | URL: ${url} | Retry Count: ${ + options?.retryCount ?? 0 + } | Bucket ID: ${options?.bucketId} | Body: ${ JSON.stringify( body, ) @@ -72,9 +66,10 @@ export async function runMethod( resolve(data.status !== 204 ? JSON.parse(data.body ?? "{}") : (undefined as unknown as T)), }, { - bucketId, + bucketId: options?.bucketId, body: body as Record | undefined, - retryCount, + retryCount: options?.retryCount ?? 0, + headers: options?.headers, }, ); }); From 72fec3a4c61f5cce4b78b18dcb1c7e92f2969740 Mon Sep 17 00:00:00 2001 From: ITOH Date: Wed, 25 May 2022 16:38:13 +0200 Subject: [PATCH 4/4] fix(rest): `sendInteractionResponse` file upload (#2239) * fix(rest): `sendInteractionResponse` file upload * f --- helpers/interactions/sendInteractionResponse.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/interactions/sendInteractionResponse.ts b/helpers/interactions/sendInteractionResponse.ts index 88cb85b6a..ec7220dee 100644 --- a/helpers/interactions/sendInteractionResponse.ts +++ b/helpers/interactions/sendInteractionResponse.ts @@ -32,7 +32,6 @@ export async function sendInteractionResponse( users: options.data.allowedMentions!.users?.map((id) => id.toString()), roles: options.data.allowedMentions!.roles?.map((id) => id.toString()), }, - file: options.data.file, custom_id: options.data.customId, title: options.data.title, components: options.data.components?.map((component) => ({ @@ -105,6 +104,7 @@ export async function sendInteractionResponse( { type: options.type, data, + file: options.data.file, }, ); } @@ -114,7 +114,7 @@ export async function sendInteractionResponse( bot.rest, "post", bot.constants.endpoints.WEBHOOK(bot.applicationId, token), - data, + { ...data, file: options.data.file }, ); return bot.transformers.message(bot, result);