From 4a6e1e46069f526ff4ac16b945d5b9340a500ad2 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 14:20:51 +0400 Subject: [PATCH 01/31] feat(types): add expiresAt field to Invite interface (#880) Reference: https://github.com/discord/discord-api-docs/commit/6dc1b2a3e7aad5145420f965e3ec15f3b7ed7074 --- src/types/invites/invite.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/invites/invite.ts b/src/types/invites/invite.ts index f06dcb266..1b82079f3 100644 --- a/src/types/invites/invite.ts +++ b/src/types/invites/invite.ts @@ -24,4 +24,6 @@ export interface Invite { approximatePresenceCount?: number; /** Approximate count of total members */ approximateMemberCount?: number; + /** The expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true`. */ + expiresAt?: string | null; } From c7b2b4d2dfc2e467dd004ca45d6e3c393979d78e Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 14:26:23 +0400 Subject: [PATCH 02/31] feat(types): add attachments field to EditWebhookMessage (#881) Reference: https://github.com/discord/discord-api-docs/commit/5afbad75892a907726ab47eea4d9bcbeabcdaa18 --- src/types/webhooks/edit_webhook_message.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/webhooks/edit_webhook_message.ts b/src/types/webhooks/edit_webhook_message.ts index 5b8275c98..7cb6f2940 100644 --- a/src/types/webhooks/edit_webhook_message.ts +++ b/src/types/webhooks/edit_webhook_message.ts @@ -1,6 +1,7 @@ import { Embed } from "../embeds/embed.ts"; import { AllowedMentions } from "../messages/allowed_mentions.ts"; -import { FileContent } from "../mod.ts"; +import { FileContent } from "../misc/file_content.ts"; +import { Attachment } from "../messages/attachment.ts"; /** https://discord.com/developers/docs/resources/webhook#edit-webhook-message-jsonform-params */ export interface EditWebhookMessage { @@ -12,4 +13,6 @@ export interface EditWebhookMessage { file: FileContent | FileContent[]; /** Allowed mentions for the message */ allowedMentions?: AllowedMentions | null; + /** Attached files to keep. */ + attachments?: Attachment | null; } From 484c395dd32217b66bb350e5a39219505e60e28b Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 11:33:35 +0100 Subject: [PATCH 03/31] delete: ayntee ayntee has deleted his gh account --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99e1a8ca6..f1ab551df 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @ayntee @Skillz4Killz @itohatweb +* @Skillz4Killz @itohatweb From 1e48e5890859b4baa6c3bf131b6bc20e50abcfd3 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 11:34:07 +0100 Subject: [PATCH 04/31] remove: ayntee ayntee has deleted his gh account --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96406d251..f7e3a5591 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: - name: Cache dependencies run: deno cache --no-check mod.ts - name: Run test script for maintainers - if: ${{ github.actor == 'ayntee' || github.actor == 'Skillz4Killz' || github.actor == 'itohatweb' }} + if: ${{ github.actor == 'Skillz4Killz' || github.actor == 'itohatweb' }} run: deno test --unstable --coverage=coverage -A --no-check tests/mod.ts - name: Run test script if label added if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'run-tests' }} From 605ca0a07e146e44ef95e0636e552fd778edb9a0 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 15:56:55 +0400 Subject: [PATCH 05/31] add(types): attachments field to EditMessage (#882) * feat(types): add attachments field to EditMessage Reference: https://github.com/discord/discord-api-docs/commit/0b2b84d94b9abd4688158edf0534dfdd062591b4 Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/edit_message.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/messages/edit_message.ts b/src/types/messages/edit_message.ts index 9b726a411..41169bc55 100644 --- a/src/types/messages/edit_message.ts +++ b/src/types/messages/edit_message.ts @@ -1,5 +1,6 @@ import { Embed } from "../embeds/embed.ts"; import { AllowedMentions } from "./allowed_mentions.ts"; +import { Attachment } from "./attachment.ts"; /** https://discord.com/developers/docs/resources/channel#edit-message-json-params */ export interface EditMessage { @@ -11,4 +12,6 @@ export interface EditMessage { flags?: 4 | null; /** Allowed mentions for the message */ allowedMentions?: AllowedMentions | null; + /** Attached files to keep */ + attachments?: Attachment | null; } From 102cab643ee055a81f47e38614ea5ff2e12b271d Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 15:58:18 +0400 Subject: [PATCH 06/31] feat(types): add MENTIONABLE to DiscordApplicationCommandOptionTypes (#883) Reference: https://github.com/discord/discord-api-docs/commit/91379ed78710d3020dcb46521485abecdf31e1d0 --- src/types/interactions/application_command_option_types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/interactions/application_command_option_types.ts b/src/types/interactions/application_command_option_types.ts index a3a45451c..da0f2ed76 100644 --- a/src/types/interactions/application_command_option_types.ts +++ b/src/types/interactions/application_command_option_types.ts @@ -8,6 +8,7 @@ export enum DiscordApplicationCommandOptionTypes { USER, CHANNEL, ROLE, + MENTIONABLE, } export type ApplicationCommandOptionTypes = From 684ce6a69317a09dd273cbc11481d006b9a3ad58 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 15:59:57 +0400 Subject: [PATCH 07/31] add(types): withExpiration field to GetInvite (#884) Reference: https://github.com/discord/discord-api-docs/commit/2c9f17887a8e5c95d48b93901f4543beda4c09be --- src/types/invites/get_invite.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/invites/get_invite.ts b/src/types/invites/get_invite.ts index 46a3caaad..44ef3f652 100644 --- a/src/types/invites/get_invite.ts +++ b/src/types/invites/get_invite.ts @@ -2,4 +2,6 @@ export interface GetInvite { /** Whether the invite should contain approximate member counts */ withCounts?: boolean; + /** Whether the invite should contain the expiration date. */ + withExpiration?: boolean; } From 666b8b6c292bb3519af957973c8361aaf4148102 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 16:01:04 +0400 Subject: [PATCH 08/31] add(types): maximum non guild members ban error code Reference: https://github.com/discord/discord-api-docs/commit/77c375643616246f23a84e5f63d9cf9776e9a164 --- src/types/codes/json_error_codes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/codes/json_error_codes.ts b/src/types/codes/json_error_codes.ts index 69fb660f9..8efd3f186 100644 --- a/src/types/codes/json_error_codes.ts +++ b/src/types/codes/json_error_codes.ts @@ -43,6 +43,7 @@ export enum DiscordJsonErrorCodes { MaximumNumberOfInvitesReached, MaximumNumberOfGuildDiscoverySubcategoriesHasBeenReached = 30030, GuildAlreadyHasTemplate = 30031, + MaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035, UnauthorizedProvideAValidTokenAndTryAgain = 40001, YouNeedToVerifyYourAccountInOrderToPerformThisAction, RequestEntityTooLargeTrySendingSomethingSmallerInSize = 40005, From bfb8179f200780a3cbc527f9a01b44b86c563eb7 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 16:02:12 +0400 Subject: [PATCH 09/31] remove(types): previewAsset from MessageSticker --- src/types/messages/message_sticker.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/types/messages/message_sticker.ts b/src/types/messages/message_sticker.ts index bcc369508..d1a9bf04e 100644 --- a/src/types/messages/message_sticker.ts +++ b/src/types/messages/message_sticker.ts @@ -17,11 +17,6 @@ export interface MessageSticker { * Note: The URL for fetching sticker assets is currently private. */ asset: string; - /** - * Sticker preview asset hash - * Note: The URL for fetching sticker assets is currently private. - */ - previewAsset?: string | null; /** Type of sticker format */ formatType: DiscordMessageStickerFormatTypes; } From acc8eeb0742237ce4b1db3d3da0f54abdf7f8711 Mon Sep 17 00:00:00 2001 From: keenkairos <83495194+keenkairos@users.noreply.github.com> Date: Sat, 1 May 2021 16:03:00 +0400 Subject: [PATCH 10/31] add(types): nsfw field * feat(types): add nsfw field to Guild Reference: https://github.com/discord/discord-api-docs/commit/5e63e309bbc7e93b683b47bc8401d2d079580251 Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/guilds/guild.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/guilds/guild.ts b/src/types/guilds/guild.ts index 52f879a36..4e96ddb0f 100644 --- a/src/types/guilds/guild.ts +++ b/src/types/guilds/guild.ts @@ -107,4 +107,6 @@ export interface Guild { approximatePresenceCount?: number; /** The welcome screen of a Community guild, shown to new members, returned when in the invite object */ welcomeScreen?: WelcomeScreen; + /** `true` if this guild is designated as NSFW */ + nsfw: boolean; } From e62db7eefec1c8da886b146274a119f113880896 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 13:04:11 +0100 Subject: [PATCH 11/31] remove(types): no dots in jsdoc comments --- src/types/invites/invite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/invites/invite.ts b/src/types/invites/invite.ts index 1b82079f3..107016a8f 100644 --- a/src/types/invites/invite.ts +++ b/src/types/invites/invite.ts @@ -24,6 +24,6 @@ export interface Invite { approximatePresenceCount?: number; /** Approximate count of total members */ approximateMemberCount?: number; - /** The expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true`. */ + /** The expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true` */ expiresAt?: string | null; } From a594211dfd064f6a240d3ea8dace52f8208a9ab5 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 13:04:27 +0100 Subject: [PATCH 12/31] remove(types): no dots in jsdoc comments --- src/types/webhooks/edit_webhook_message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/webhooks/edit_webhook_message.ts b/src/types/webhooks/edit_webhook_message.ts index 7cb6f2940..4076c5cb8 100644 --- a/src/types/webhooks/edit_webhook_message.ts +++ b/src/types/webhooks/edit_webhook_message.ts @@ -13,6 +13,6 @@ export interface EditWebhookMessage { file: FileContent | FileContent[]; /** Allowed mentions for the message */ allowedMentions?: AllowedMentions | null; - /** Attached files to keep. */ + /** Attached files to keep */ attachments?: Attachment | null; } From 82cff0e201fdcc1c7ef10c082cba8a37bbe68216 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 13:04:49 +0100 Subject: [PATCH 13/31] remove(types): no dots in jsdoc comments --- src/types/invites/get_invite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/invites/get_invite.ts b/src/types/invites/get_invite.ts index 44ef3f652..d071d1569 100644 --- a/src/types/invites/get_invite.ts +++ b/src/types/invites/get_invite.ts @@ -2,6 +2,6 @@ export interface GetInvite { /** Whether the invite should contain approximate member counts */ withCounts?: boolean; - /** Whether the invite should contain the expiration date. */ + /** Whether the invite should contain the expiration date */ withExpiration?: boolean; } From 6de8e1d42856019541ff42a6ed44e53e6e09125d Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 08:16:28 -0400 Subject: [PATCH 14/31] buttons buttons and more buttons --- src/bot.ts | 32 +---------- src/handlers/guilds/GUILD_UPDATE.ts | 2 - src/helpers/guilds/edit_guild.ts | 13 ++++- src/helpers/guilds/get_widget.ts | 4 +- src/helpers/invites/create_invite.ts | 10 +++- src/helpers/members/fetch_members.ts | 4 +- src/helpers/members/get_members.ts | 5 +- src/helpers/messages/send_message.ts | 57 +++++++++++++++++++ src/helpers/type_guards/is_action_row.ts | 8 +++ src/helpers/type_guards/is_button.ts | 10 ++++ src/types/guilds/guild_widget_details.ts | 24 ++++++++ .../application_command_interaction_data.ts | 8 ++- src/types/interactions/interaction.ts | 3 + .../interaction_response_types.ts | 2 + src/types/interactions/interaction_types.ts | 1 + src/types/invites/create_channel_invite.ts | 2 +- src/types/messages/components/action_row.ts | 11 ++++ .../messages/components/button_component.ts | 28 +++++++++ .../messages/components/button_styles.ts | 12 ++++ .../components/message_component_types.ts | 6 ++ .../messages/components/message_components.ts | 6 ++ src/types/messages/create_message.ts | 3 + src/types/messages/message.ts | 3 + src/types/misc/errors.ts | 10 ++++ src/types/mod.ts | 5 ++ src/ws/create_shard.ts | 25 +++----- src/ws/events.ts | 3 +- src/ws/spawn_shards.ts | 1 - 28 files changed, 235 insertions(+), 63 deletions(-) create mode 100644 src/helpers/type_guards/is_action_row.ts create mode 100644 src/helpers/type_guards/is_button.ts create mode 100644 src/types/guilds/guild_widget_details.ts create mode 100644 src/types/messages/components/action_row.ts create mode 100644 src/types/messages/components/button_component.ts create mode 100644 src/types/messages/components/button_styles.ts create mode 100644 src/types/messages/components/message_component_types.ts create mode 100644 src/types/messages/components/message_components.ts diff --git a/src/bot.ts b/src/bot.ts index c1505c1fb..b077dbfc0 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -5,7 +5,6 @@ import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts"; import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts"; import { ws } from "./ws/ws.ts"; -export let authorization = ""; export let secretKey = ""; export let botId = ""; export let applicationId = ""; @@ -14,21 +13,8 @@ export let eventHandlers: EventHandlers = {}; export let proxyWSURL = `wss://gateway.discord.gg`; -export const identifyPayload = { - token: "", - compress: true, - properties: { - $os: "linux", - $browser: "Discordeno", - $device: "Discordeno", - }, - intents: 0, - shard: [0, 0], -}; - export async function startBot(config: BotConfig) { if (config.eventHandlers) eventHandlers = config.eventHandlers; - authorization = `Bot ${config.token}`; ws.identifyPayload.token = `Bot ${config.token}`; rest.token = `Bot ${config.token}`; ws.identifyPayload.intents = config.intents.reduce( @@ -49,17 +35,6 @@ export async function startBot(config: BotConfig) { ws.botGatewayData.url += `?v=${GATEWAY_VERSION}&encoding=json`; proxyWSURL = ws.botGatewayData.url; - identifyPayload.token = config.token; - identifyPayload.intents = config.intents.reduce( - ( - bits, - next, - ) => (bits |= typeof next === "string" - ? DiscordGatewayIntents[next] - : next), - 0, - ); - identifyPayload.shard = [0, ws.botGatewayData.shards]; ws.spawnShards(); } @@ -91,18 +66,15 @@ export function setApplicationId(id: string) { * Advanced Devs: This function will allow you to have an insane amount of customization potential as when you get to large bots you need to be able to optimize every tiny detail to make you bot work the way you need. */ export async function startBigBrainBot(data: BigBrainBotConfig) { - authorization = `Bot ${data.token}`; - identifyPayload.token = `Bot ${data.token}`; - if (data.secretKey) secretKey = data.secretKey; if (data.restURL) baseEndpoints.BASE_URL = data.restURL; if (data.cdnURL) baseEndpoints.CDN_URL = data.cdnURL; if (data.eventHandlers) eventHandlers = data.eventHandlers; if (data.compress) { - identifyPayload.compress = data.compress; + ws.identifyPayload.compress = data.compress; } - identifyPayload.intents = data.intents.reduce( + ws.identifyPayload.intents = data.intents.reduce( ( bits, next, diff --git a/src/handlers/guilds/GUILD_UPDATE.ts b/src/handlers/guilds/GUILD_UPDATE.ts index 186b40c25..607c16e76 100644 --- a/src/handlers/guilds/GUILD_UPDATE.ts +++ b/src/handlers/guilds/GUILD_UPDATE.ts @@ -30,8 +30,6 @@ export async function handleGuildUpdate(data: DiscordGatewayPayload) { if (Array.isArray(cachedValue) && Array.isArray(value)) { const different = (cachedValue.length !== value.length) || cachedValue.find((val) => !value.includes(val)) || - // TODO: check if this really works hehe - // @ts-ignore typescript thinks that this is not an array value.find((val) => !cachedValue.includes(val)); if (!different) return; } diff --git a/src/helpers/guilds/edit_guild.ts b/src/helpers/guilds/edit_guild.ts index 20df19c47..e17206c00 100644 --- a/src/helpers/guilds/edit_guild.ts +++ b/src/helpers/guilds/edit_guild.ts @@ -1,3 +1,4 @@ +import { cacheHandlers } from "../../cache.ts"; import { rest } from "../../rest/rest.ts"; import { structures } from "../../structures/mod.ts"; import { Guild } from "../../types/guilds/guild.ts"; @@ -5,6 +6,7 @@ import { ModifyGuild } from "../../types/guilds/modify_guild.ts"; import { endpoints } from "../../util/constants.ts"; import { requireBotGuildPermissions } from "../../util/permissions.ts"; import { urlToBase64 } from "../../util/utils.ts"; +import { ws } from "../../ws/ws.ts"; /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ export async function editGuild(guildId: string, options: ModifyGuild) { @@ -28,6 +30,13 @@ export async function editGuild(guildId: string, options: ModifyGuild) { options, ); - // TODO: use ws.botGatewayData to calculate the shard ID - return structures.createDiscordenoGuild(result, -1); + const cached = await cacheHandlers.get("guilds", guildId); + return structures.createDiscordenoGuild( + result, + cached?.shardId || + Number( + (BigInt(result.id) >> 22n % BigInt(ws.botGatewayData.shards)) + .toString(), + ), + ); } diff --git a/src/helpers/guilds/get_widget.ts b/src/helpers/guilds/get_widget.ts index c685b8088..2e5b4d84a 100644 --- a/src/helpers/guilds/get_widget.ts +++ b/src/helpers/guilds/get_widget.ts @@ -1,5 +1,6 @@ import { cacheHandlers } from "../../cache.ts"; import { rest } from "../../rest/rest.ts"; +import { GuildWidgetDetails } from "../../types/guilds/guild_widget_details.ts"; import { Errors } from "../../types/misc/errors.ts"; import { endpoints } from "../../util/constants.ts"; @@ -11,6 +12,5 @@ export async function getWidget(guildId: string, options?: { force: boolean }) { if (!guild?.widgetEnabled) throw new Error(Errors.GUILD_WIDGET_NOT_ENABLED); } - // TODO: add return type - return await rest.runMethod("get", `${endpoints.GUILD_WIDGET(guildId)}.json`); + return await rest.runMethod("get", `${endpoints.GUILD_WIDGET(guildId)}.json`) as GuildWidgetDetails; } diff --git a/src/helpers/invites/create_invite.ts b/src/helpers/invites/create_invite.ts index 0c7af679f..0edab07ba 100644 --- a/src/helpers/invites/create_invite.ts +++ b/src/helpers/invites/create_invite.ts @@ -1,6 +1,7 @@ import { rest } from "../../rest/rest.ts"; import { CreateChannelInvite } from "../../types/invites/create_channel_invite.ts"; -import { Invite } from "../../types/mod.ts"; +import { Invite } from "../../types/invites/invite.ts"; +import { Errors } from "../../types/misc/errors.ts"; import { endpoints } from "../../util/constants.ts"; import { requireBotChannelPermissions } from "../../util/permissions.ts"; @@ -11,7 +12,12 @@ export async function createInvite( ) { await requireBotChannelPermissions(channelId, ["CREATE_INSTANT_INVITE"]); - // TODO: add proper options validation + if (options.maxAge && (options.maxAge < 0 || options.maxAge > 604800)) { + throw new Error(Errors.INVITE_MAX_AGE_INVALID); + } + if (options.maxUses && (options.maxUses < 0 || options.maxUses > 100)) { + throw new Error(Errors.INVITE_MAX_USES_INVALID); + } return await rest.runMethod( "post", diff --git a/src/helpers/members/fetch_members.ts b/src/helpers/members/fetch_members.ts index e336d42cf..9a0428aad 100644 --- a/src/helpers/members/fetch_members.ts +++ b/src/helpers/members/fetch_members.ts @@ -1,9 +1,9 @@ -import { identifyPayload } from "../../bot.ts"; import { DiscordenoMember } from "../../structures/member.ts"; import { DiscordGatewayIntents } from "../../types/gateway/gateway_intents.ts"; import { RequestGuildMembers } from "../../types/guilds/request_guild_members.ts"; import { Errors } from "../../types/misc/errors.ts"; import { Collection } from "../../util/collection.ts"; +import { ws } from "../../ws/ws.ts"; /** * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.get() @@ -21,7 +21,7 @@ export function fetchMembers( // You can request 1 member without the intent if ( (!options?.limit || options.limit > 1) && - !(identifyPayload.intents && DiscordGatewayIntents.GUILD_MEMBERS) + !(ws.identifyPayload.intents && DiscordGatewayIntents.GUILD_MEMBERS) ) { throw new Error(Errors.MISSING_INTENT_GUILD_MEMBERS); } diff --git a/src/helpers/members/get_members.ts b/src/helpers/members/get_members.ts index e54173b7c..b51fff45a 100644 --- a/src/helpers/members/get_members.ts +++ b/src/helpers/members/get_members.ts @@ -1,4 +1,4 @@ -import { eventHandlers, identifyPayload } from "../../bot.ts"; +import { eventHandlers } from "../../bot.ts"; import { cacheHandlers } from "../../cache.ts"; import { rest } from "../../rest/rest.ts"; import { DiscordenoMember } from "../../structures/member.ts"; @@ -9,6 +9,7 @@ import { ListGuildMembers } from "../../types/guilds/list_guild_members.ts"; import { Errors } from "../../types/misc/errors.ts"; import { Collection } from "../../util/collection.ts"; import { endpoints } from "../../util/constants.ts"; +import { ws } from "../../ws/ws.ts"; /** * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.get() @@ -19,7 +20,7 @@ import { endpoints } from "../../util/constants.ts"; * GW(fetchMembers): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is 960/m. */ export async function getMembers(guildId: string, options?: ListGuildMembers) { - if (!(identifyPayload.intents && DiscordGatewayIntents.GUILD_MEMBERS)) { + if (!(ws.identifyPayload.intents && DiscordGatewayIntents.GUILD_MEMBERS)) { throw new Error(Errors.MISSING_INTENT_GUILD_MEMBERS); } diff --git a/src/helpers/messages/send_message.ts b/src/helpers/messages/send_message.ts index 73d00246f..a15e97881 100644 --- a/src/helpers/messages/send_message.ts +++ b/src/helpers/messages/send_message.ts @@ -3,6 +3,7 @@ import { rest } from "../../rest/rest.ts"; import { structures } from "../../structures/mod.ts"; import { DiscordChannelTypes } from "../../types/channels/channel_types.ts"; import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts"; +import { ButtonStyles } from "../../types/messages/components/button_styles.ts"; import { CreateMessage } from "../../types/messages/create_message.ts"; import { DiscordMessage, Message } from "../../types/messages/message.ts"; import { Errors } from "../../types/misc/errors.ts"; @@ -11,6 +12,8 @@ import { endpoints } from "../../util/constants.ts"; import { requireBotChannelPermissions } from "../../util/permissions.ts"; import { camelKeysToSnakeCase } from "../../util/utils.ts"; import { validateLength } from "../../util/validate_length.ts"; +import { isActionRow } from "../type_guards/is_action_row.ts"; +import { isButton } from "../type_guards/is_button.ts"; /** Send a message to the channel. Requires SEND_MESSAGES permission. */ export async function sendMessage( @@ -93,6 +96,60 @@ export async function sendMessage( } } + if (content.components?.length) { + let actionRowCounter = 0; + + for (const component of content.components) { + // 5 Link buttons can not have a custom_id + if (isButton(component)) { + if ( + component.type === ButtonStyles.Link && + component.customId + ) { + throw new Error(Errors.LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID); + } + if ( + !component.customId + ) { + if (component.type !== ButtonStyles.Link) { + throw new Error(Errors.BUTTON_REQUIRES_CUSTOM_ID); + } + } + + if (!validateLength(component.label, { max: 80 })) { + throw new Error(Errors.COMPONENT_LABEL_TOO_BIG); + } + + if ( + component.customId && + !validateLength(component.customId, { max: 100 }) + ) { + throw new Error(Errors.COMPONENT_CUSTOM_ID_TOO_BIG); + } + } + + if (!isActionRow(component)) { + continue; + } + + actionRowCounter++; + // Max of 5 ActionRows per message + if (actionRowCounter > 5) throw new Error(Errors.TOO_MANY_ACTION_ROWS); + + // Max of 5 Buttons (or any component type) within an ActionRow + if (component.components?.length > 5) { + throw new Error(Errors.TOO_MANY_COMPONENTS); + } + } + } + + if ( + content.nonce && + !validateLength(content.nonce.toString(), { max: 25 }) + ) { + throw new Error(Errors.NONCE_TOO_LONG); + } + const result = await rest.runMethod( "post", endpoints.CHANNEL_MESSAGES(channelId), diff --git a/src/helpers/type_guards/is_action_row.ts b/src/helpers/type_guards/is_action_row.ts new file mode 100644 index 000000000..ec9779d1e --- /dev/null +++ b/src/helpers/type_guards/is_action_row.ts @@ -0,0 +1,8 @@ +import { ActionRow } from "../../types/messages/components/action_row.ts"; +import { MessageComponent } from "../../types/messages/components/message_components.ts"; +import { MessageComponentTypes } from "../../types/messages/components/message_component_types.ts"; + +/** A type guard function to tell if it is a action row component */ +export function isActionRow(component: MessageComponent): component is ActionRow { + return component.type === MessageComponentTypes.ActionRow +} \ No newline at end of file diff --git a/src/helpers/type_guards/is_button.ts b/src/helpers/type_guards/is_button.ts new file mode 100644 index 000000000..dfe2b92cd --- /dev/null +++ b/src/helpers/type_guards/is_button.ts @@ -0,0 +1,10 @@ +import { ButtonComponent } from "../../types/messages/components/button_component.ts"; +import { MessageComponent } from "../../types/messages/components/message_components.ts"; +import { MessageComponentTypes } from "../../types/messages/components/message_component_types.ts"; + +/** A type guard function to tell if it is a button component */ +export function isButton( + component: MessageComponent, +): component is ButtonComponent { + return component.type === MessageComponentTypes.Button; +} diff --git a/src/types/guilds/guild_widget_details.ts b/src/types/guilds/guild_widget_details.ts new file mode 100644 index 000000000..a0347ee47 --- /dev/null +++ b/src/types/guilds/guild_widget_details.ts @@ -0,0 +1,24 @@ +import { SnakeCasedPropertiesDeep } from "../util.ts"; + +export interface GuildWidgetDetails { + id: string; + name: string; + instantInvite: string; + channels: { + id: string; + name: string; + position: number; + }[]; + members: { + id: string; + username: string; + discriminator: string; + avatar?: string | null; + status: string; + avatar_url: string; + }[]; + presenceCount: number; +} + +/** https://discord.com/developers/docs/resources/guild#get-guild-widget-example-get-guild-widget */ +export type DiscordGuildWidget = SnakeCasedPropertiesDeep; \ No newline at end of file diff --git a/src/types/interactions/application_command_interaction_data.ts b/src/types/interactions/application_command_interaction_data.ts index 5074f1c29..5d7502664 100644 --- a/src/types/interactions/application_command_interaction_data.ts +++ b/src/types/interactions/application_command_interaction_data.ts @@ -4,13 +4,17 @@ import { ApplicationCommandInteractionDataResolved } from "./application_command export interface ApplicationCommandInteractionData { /** The Id of the invoked command */ - id: string; + id?: string; /** The name of the invoked command */ - name: string; + name?: string; /** Converted users + roles + channels */ resolved?: ApplicationCommandInteractionDataResolved; /** The params + values from the user */ options?: ApplicationCommandInteractionDataOption[]; + /** with the value you defined for this component */ + customId?: string; + /** The type of this component */ + componentType?: 2; } /** https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondata */ diff --git a/src/types/interactions/interaction.ts b/src/types/interactions/interaction.ts index a2b8b66f7..8578c7f62 100644 --- a/src/types/interactions/interaction.ts +++ b/src/types/interactions/interaction.ts @@ -1,4 +1,5 @@ import { GuildMemberWithUser } from "../guilds/guild_member.ts"; +import { Message } from "../messages/message.ts"; import { User } from "../users/user.ts"; import { SnakeCasedPropertiesDeep } from "../util.ts"; import { ApplicationCommandInteractionData } from "./application_command_interaction_data.ts"; @@ -25,6 +26,8 @@ export interface Interaction { token: string; /** Read-only property, always `1` */ version: 1; + /** for the message the button was attached to */ + message?: Message; } /** https://discord.com/developers/docs/interactions/slash-commands#interaction */ diff --git a/src/types/interactions/interaction_response_types.ts b/src/types/interactions/interaction_response_types.ts index 8a152ad39..122f22995 100644 --- a/src/types/interactions/interaction_response_types.ts +++ b/src/types/interactions/interaction_response_types.ts @@ -6,4 +6,6 @@ export enum DiscordInteractionResponseTypes { ChannelMessageWithSource = 4, /** ACK an interaction and edit a response later, the user sees a loading state */ DeferredChannelMessageWithSource = 5, + /** It has no data fields. You can send this type **only in response to a button interaction .** It will acknowledge the interaction and update the button to a loading state. */ + DeferredMessageUpdate, } diff --git a/src/types/interactions/interaction_types.ts b/src/types/interactions/interaction_types.ts index fe9408744..099c63999 100644 --- a/src/types/interactions/interaction_types.ts +++ b/src/types/interactions/interaction_types.ts @@ -2,4 +2,5 @@ export enum DiscordInteractionTypes { Ping = 1, ApplicationCommand, + Button } diff --git a/src/types/invites/create_channel_invite.ts b/src/types/invites/create_channel_invite.ts index 50ac50762..4271e6984 100644 --- a/src/types/invites/create_channel_invite.ts +++ b/src/types/invites/create_channel_invite.ts @@ -1,7 +1,7 @@ import { DiscordInviteTargetTypes } from "./invite_target_types.ts"; export interface CreateChannelInvite { - /** Durationi of invite in seconds before expiry, or 0 for never. Between 0 and 604800 (7 days). Default: 86400 (24 hours) */ + /** Duration of invite in seconds before expiry, or 0 for never. Between 0 and 604800 (7 days). Default: 86400 (24 hours) */ maxAge?: number; /** Max number of users or 0 for unlimited. Between 0 and 100. Default: 0 */ maxUses?: number; diff --git a/src/types/messages/components/action_row.ts b/src/types/messages/components/action_row.ts new file mode 100644 index 000000000..f00f3fff6 --- /dev/null +++ b/src/types/messages/components/action_row.ts @@ -0,0 +1,11 @@ +import { SnakeCasedPropertiesDeep } from "../../util.ts"; +import { ButtonComponent } from "./button_component.ts"; + +export interface ActionRow { + /** Action rows are a group of buttons. */ + type: 1; + /** The button components */ + components: ButtonComponent[]; +} + +export type DiscordActionRow = SnakeCasedPropertiesDeep; \ No newline at end of file diff --git a/src/types/messages/components/button_component.ts b/src/types/messages/components/button_component.ts new file mode 100644 index 000000000..7bdcc6be7 --- /dev/null +++ b/src/types/messages/components/button_component.ts @@ -0,0 +1,28 @@ +import { SnakeCasedPropertiesDeep } from "../../util.ts"; +import { ButtonStyles } from "./button_styles.ts"; + +export interface ButtonComponent { + /** All button components have type 2 */ + type: 2; + /** for what the button says (max 80 characters) */ + label: string; + /** a dev-defined unique string sent on click (max 100 characters). type 5 Link buttons can not have a custom_id */ + customId?: string; + /** For different styles/colors of the buttons */ + style: ButtonStyles; + /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */ + emoji?: { + /** Emoji id */ + id: string | null; + /** Emoji name (can only be null in reaction emoji objects) */ + name: string | null; + /** Whether this emoji is animated */ + animated?: boolean; + }; + /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */ + url?: string; + /** Whether or not this button is disabled */ + disabled?: boolean; +} + +export type DiscordButtonComponent = SnakeCasedPropertiesDeep; diff --git a/src/types/messages/components/button_styles.ts b/src/types/messages/components/button_styles.ts new file mode 100644 index 000000000..dae735204 --- /dev/null +++ b/src/types/messages/components/button_styles.ts @@ -0,0 +1,12 @@ +export enum ButtonStyles { + /** A blurple button */ + Primary = 1, + /** A grey button */ + Secondary, + /** A green button */ + Success, + /** A red button */ + Danger, + /** A button that navigates to a URL */ + Link, +} diff --git a/src/types/messages/components/message_component_types.ts b/src/types/messages/components/message_component_types.ts new file mode 100644 index 000000000..aaa89ecb9 --- /dev/null +++ b/src/types/messages/components/message_component_types.ts @@ -0,0 +1,6 @@ +export enum MessageComponentTypes { + /** A row of components at the bottom of a message */ + ActionRow = 1, + /** A button! */ + Button, +} diff --git a/src/types/messages/components/message_components.ts b/src/types/messages/components/message_components.ts new file mode 100644 index 000000000..045b0f75a --- /dev/null +++ b/src/types/messages/components/message_components.ts @@ -0,0 +1,6 @@ +import { ActionRow } from "./action_row.ts"; +import { ButtonComponent } from "./button_component.ts"; + +export type MessageComponent = ActionRow | ButtonComponent; + +export type MessageComponents = MessageComponent[]; diff --git a/src/types/messages/create_message.ts b/src/types/messages/create_message.ts index 1ec41fc32..fa1704f90 100644 --- a/src/types/messages/create_message.ts +++ b/src/types/messages/create_message.ts @@ -3,6 +3,7 @@ import { AllowedMentions } from "../messages/allowed_mentions.ts"; import { MessageReference } from "../messages/message_reference.ts"; import { FileContent } from "../misc/file_content.ts"; import { SnakeCasedPropertiesDeep } from "../util.ts"; +import { MessageComponents } from "./components/message_components.ts"; export interface CreateMessage { /** The message contents (up to 2000 characters) */ @@ -19,6 +20,8 @@ export interface CreateMessage { messageReference?: MessageReference; /** The contents of the file being sent */ file?: FileContent | FileContent[]; + /** The components you would like to have sent in this message */ + components?: MessageComponents; } /** https://discord.com/developers/docs/resources/channel#create-message */ diff --git a/src/types/messages/message.ts b/src/types/messages/message.ts index c00b23d62..bfc456465 100644 --- a/src/types/messages/message.ts +++ b/src/types/messages/message.ts @@ -6,6 +6,7 @@ import { Application } from "../oauth2/application.ts"; import { User } from "../users/user.ts"; import { SnakeCasedPropertiesDeep } from "../util.ts"; import { Attachment } from "./attachment.ts"; +import { MessageComponents } from "./components/message_components.ts"; import { MessageActivity } from "./message_activity.ts"; import { MessageReference } from "./message_reference.ts"; import { MessageSticker } from "./message_sticker.ts"; @@ -82,6 +83,8 @@ export interface Message { referencedMessage?: Message; /** Sent if the message is a response to an Interaction */ interaction?: MessageInteraction; + /** The components related to this message */ + components: MessageComponents; } /** https://discord.com/developers/docs/resources/channel#message-object */ diff --git a/src/types/misc/errors.ts b/src/types/misc/errors.ts index f8e441b72..ee55755c0 100644 --- a/src/types/misc/errors.ts +++ b/src/types/misc/errors.ts @@ -68,9 +68,19 @@ export enum Errors { USERNAME_INVALID_USERNAME = "USERNAME_INVALID_USERNAME", USERNAME_MAX_LENGTH = "USERNAME_MAX_LENGTH", USERNAME_MIN_LENGTH = "USERNAME_MIN_LENGTH", + NONCE_TOO_LONG = "NONCE_TOO_LONG", + INVITE_MAX_AGE_INVALID = "INVITE_MAX_AGE_INVALID", + INVITE_MAX_USES_INVALID = "INVITE_MAX_USES_INVALID", // API Errors RATE_LIMIT_RETRY_MAXED = "RATE_LIMIT_RETRY_MAXED", REQUEST_CLIENT_ERROR = "REQUEST_CLIENT_ERROR", REQUEST_SERVER_ERROR = "REQUEST_SERVER_ERROR", REQUEST_UNKNOWN_ERROR = "REQUEST_UNKNOWN_ERROR", + // Component Errors + TOO_MANY_COMPONENTS = "TOO_MANY_COMPONENTS", + TOO_MANY_ACTION_ROWS = "TOO_MANY_ACTION_ROWS", + LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID = "LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID", + COMPONENT_LABEL_TOO_BIG = "COMPONENT_LABEL_TOO_BIG", + COMPONENT_CUSTOM_ID_TOO_BIG = "COMPONENT_CUSTOM_ID_TOO_BIG", + BUTTON_REQUIRES_CUSTOM_ID = "BUTTON_REQUIRES_CUSTOM_ID", } diff --git a/src/types/mod.ts b/src/types/mod.ts index dd18a8ec4..bc4d06750 100644 --- a/src/types/mod.ts +++ b/src/types/mod.ts @@ -131,6 +131,11 @@ export * from "./members/guild_member_add.ts"; export * from "./members/guild_member_remove.ts"; export * from "./members/guild_member_update.ts"; export * from "./members/search_guild_members.ts"; +export * from "./messages/components/action_row.ts"; +export * from "./messages/components/button_component.ts"; +export * from "./messages/components/button_styles.ts"; +export * from "./messages/components/message_component_types.ts"; +export * from "./messages/components/message_components.ts"; export * from "./messages/allowed_mentions.ts"; export * from "./messages/allowed_mentions_types.ts"; export * from "./messages/attachment.ts"; diff --git a/src/ws/create_shard.ts b/src/ws/create_shard.ts index cecbc20a0..da974d65b 100644 --- a/src/ws/create_shard.ts +++ b/src/ws/create_shard.ts @@ -17,24 +17,17 @@ export async function createShard(shardId: number) { socket.onclose = (event) => { ws.log("CLOSED", { shardId, payload: event }); - if ( - event.code === 3064 || - event.reason === "Discordeno Testing Finished! Do Not RESUME!" - ) { - return; - } - - if ( - event.code === 3065 || - ["Resharded!", "Resuming the shard, closing old shard."].includes( - event.reason, - ) - ) { - return ws.log("CLOSED_RECONNECT", { shardId, payload: event }); - } - // TODO: ENUM FOR THESE CODES? switch (event.code) { + // Discordeno tests finished + case 3061: + return; + case 3063: // Resharded + case 3064: // Resuming + case 3065: // Reidentifying + case 3066: // Missing ACK + // Will restart shard manually + return ws.log("CLOSED_RECONNECT", { shardId, payload: event }); case 4001: case 4002: case 4004: diff --git a/src/ws/events.ts b/src/ws/events.ts index 02b70000d..a64d7e474 100644 --- a/src/ws/events.ts +++ b/src/ws/events.ts @@ -61,5 +61,6 @@ export function log( | "DEBUG", data: unknown, ) { - console.log(type, data); + // This is just a placeholder for the dev to override + if (!type && !data) console.log(type, data); } diff --git a/src/ws/spawn_shards.ts b/src/ws/spawn_shards.ts index b7b449ba0..7dd24eff2 100644 --- a/src/ws/spawn_shards.ts +++ b/src/ws/spawn_shards.ts @@ -50,7 +50,6 @@ export function spawnShards(firstShardId = 0) { } } - console.log("BUCKETS", ws.buckets); // SPREAD THIS OUT TO DIFFERENT CLUSTERS TO BEGIN STARTING UP ws.buckets.forEach(async (bucket, bucketId) => { ws.log( From f6b4528d2c1c8066be2ab2ca7e97c77bcc772311 Mon Sep 17 00:00:00 2001 From: Skillz4Killz Date: Sat, 1 May 2021 12:20:26 +0000 Subject: [PATCH 15/31] Commit from GitHub Actions (Lint) --- src/helpers/guilds/get_widget.ts | 5 ++++- src/helpers/type_guards/is_action_row.ts | 8 +++++--- src/types/guilds/guild_widget_details.ts | 2 +- src/types/interactions/interaction_types.ts | 2 +- src/types/messages/components/action_row.ts | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/helpers/guilds/get_widget.ts b/src/helpers/guilds/get_widget.ts index ed9450510..d98034f7e 100644 --- a/src/helpers/guilds/get_widget.ts +++ b/src/helpers/guilds/get_widget.ts @@ -12,5 +12,8 @@ export async function getWidget(guildId: string, options?: { force: boolean }) { if (!guild?.widgetEnabled) throw new Error(Errors.GUILD_WIDGET_NOT_ENABLED); } - return await rest.runMethod("get", `${endpoints.GUILD_WIDGET(guildId)}.json`); + return await rest.runMethod( + "get", + `${endpoints.GUILD_WIDGET(guildId)}.json`, + ); } diff --git a/src/helpers/type_guards/is_action_row.ts b/src/helpers/type_guards/is_action_row.ts index ec9779d1e..c64fc8a6d 100644 --- a/src/helpers/type_guards/is_action_row.ts +++ b/src/helpers/type_guards/is_action_row.ts @@ -3,6 +3,8 @@ import { MessageComponent } from "../../types/messages/components/message_compon import { MessageComponentTypes } from "../../types/messages/components/message_component_types.ts"; /** A type guard function to tell if it is a action row component */ -export function isActionRow(component: MessageComponent): component is ActionRow { - return component.type === MessageComponentTypes.ActionRow -} \ No newline at end of file +export function isActionRow( + component: MessageComponent, +): component is ActionRow { + return component.type === MessageComponentTypes.ActionRow; +} diff --git a/src/types/guilds/guild_widget_details.ts b/src/types/guilds/guild_widget_details.ts index a0347ee47..d10e75211 100644 --- a/src/types/guilds/guild_widget_details.ts +++ b/src/types/guilds/guild_widget_details.ts @@ -21,4 +21,4 @@ export interface GuildWidgetDetails { } /** https://discord.com/developers/docs/resources/guild#get-guild-widget-example-get-guild-widget */ -export type DiscordGuildWidget = SnakeCasedPropertiesDeep; \ No newline at end of file +export type DiscordGuildWidget = SnakeCasedPropertiesDeep; diff --git a/src/types/interactions/interaction_types.ts b/src/types/interactions/interaction_types.ts index 2d0e4e592..5afcac139 100644 --- a/src/types/interactions/interaction_types.ts +++ b/src/types/interactions/interaction_types.ts @@ -2,7 +2,7 @@ export enum DiscordInteractionTypes { Ping = 1, ApplicationCommand, - Button + Button, } export type InteractionTypes = DiscordInteractionTypes; diff --git a/src/types/messages/components/action_row.ts b/src/types/messages/components/action_row.ts index f00f3fff6..0648347bb 100644 --- a/src/types/messages/components/action_row.ts +++ b/src/types/messages/components/action_row.ts @@ -8,4 +8,4 @@ export interface ActionRow { components: ButtonComponent[]; } -export type DiscordActionRow = SnakeCasedPropertiesDeep; \ No newline at end of file +export type DiscordActionRow = SnakeCasedPropertiesDeep; From 703d5f682e9e1f3c3c63c67bfd257fd7c08ce8f7 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:14:33 -0400 Subject: [PATCH 16/31] Update src/helpers/messages/send_message.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/helpers/messages/send_message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/messages/send_message.ts b/src/helpers/messages/send_message.ts index 331c69560..8dae5ea3c 100644 --- a/src/helpers/messages/send_message.ts +++ b/src/helpers/messages/send_message.ts @@ -100,7 +100,7 @@ export async function sendMessage( let actionRowCounter = 0; for (const component of content.components) { - // 5 Link buttons can not have a custom_id + // 5 Link buttons can not have a customId if (isButton(component)) { if ( component.type === ButtonStyles.Link && From a3d2cddd8c275a5cb504a6ab0fbdec99ced58975 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 10:18:52 -0400 Subject: [PATCH 17/31] 1 less nested if --- src/helpers/messages/send_message.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/helpers/messages/send_message.ts b/src/helpers/messages/send_message.ts index 331c69560..e7441e2d4 100644 --- a/src/helpers/messages/send_message.ts +++ b/src/helpers/messages/send_message.ts @@ -109,11 +109,9 @@ export async function sendMessage( throw new Error(Errors.LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID); } if ( - !component.customId + !component.customId && component.type !== ButtonStyles.Link ) { - if (component.type !== ButtonStyles.Link) { - throw new Error(Errors.BUTTON_REQUIRES_CUSTOM_ID); - } + throw new Error(Errors.BUTTON_REQUIRES_CUSTOM_ID); } if (!validateLength(component.label, { max: 80 })) { From f65cbc0a0c3b9ad6553ad6eec7cc4b8d0dcca7d2 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 10:19:28 -0400 Subject: [PATCH 18/31] typo fix --- src/helpers/messages/send_message.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/messages/send_message.ts b/src/helpers/messages/send_message.ts index e7441e2d4..87dd3eb20 100644 --- a/src/helpers/messages/send_message.ts +++ b/src/helpers/messages/send_message.ts @@ -100,7 +100,7 @@ export async function sendMessage( let actionRowCounter = 0; for (const component of content.components) { - // 5 Link buttons can not have a custom_id + // 5 Link buttons can not have a customId if (isButton(component)) { if ( component.type === ButtonStyles.Link && @@ -108,6 +108,7 @@ export async function sendMessage( ) { throw new Error(Errors.LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID); } + // Other buttons must have a customId if ( !component.customId && component.type !== ButtonStyles.Link ) { From 9124534938bdfc2f0e8413b35de0f82ad5345e30 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:19:50 -0400 Subject: [PATCH 19/31] Update src/types/messages/components/action_row.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/components/action_row.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/messages/components/action_row.ts b/src/types/messages/components/action_row.ts index 0648347bb..993ade6c3 100644 --- a/src/types/messages/components/action_row.ts +++ b/src/types/messages/components/action_row.ts @@ -1,6 +1,7 @@ import { SnakeCasedPropertiesDeep } from "../../util.ts"; import { ButtonComponent } from "./button_component.ts"; +// TODO: add docs link export interface ActionRow { /** Action rows are a group of buttons. */ type: 1; From 3b4348d0e45852853538777ab2f6e895f8c8b224 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:20:05 -0400 Subject: [PATCH 20/31] Update src/types/messages/components/action_row.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/components/action_row.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types/messages/components/action_row.ts b/src/types/messages/components/action_row.ts index 993ade6c3..43eac4f76 100644 --- a/src/types/messages/components/action_row.ts +++ b/src/types/messages/components/action_row.ts @@ -8,5 +8,3 @@ export interface ActionRow { /** The button components */ components: ButtonComponent[]; } - -export type DiscordActionRow = SnakeCasedPropertiesDeep; From c66b809a677409dcf308844a617b8aad33c996c1 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:20:10 -0400 Subject: [PATCH 21/31] Update src/types/messages/components/button_component.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/components/button_component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/messages/components/button_component.ts b/src/types/messages/components/button_component.ts index 7bdcc6be7..d72cdf137 100644 --- a/src/types/messages/components/button_component.ts +++ b/src/types/messages/components/button_component.ts @@ -1,6 +1,7 @@ import { SnakeCasedPropertiesDeep } from "../../util.ts"; import { ButtonStyles } from "./button_styles.ts"; +// TODO: add docs link export interface ButtonComponent { /** All button components have type 2 */ type: 2; From 0da6698233566e10a9f1e439c5f6af2a22751c96 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:20:15 -0400 Subject: [PATCH 22/31] Update src/types/messages/components/button_styles.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/components/button_styles.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/messages/components/button_styles.ts b/src/types/messages/components/button_styles.ts index dae735204..a41a2a491 100644 --- a/src/types/messages/components/button_styles.ts +++ b/src/types/messages/components/button_styles.ts @@ -1,4 +1,5 @@ -export enum ButtonStyles { +// TODO: add docs link +export enum DiscordButtonStyles { /** A blurple button */ Primary = 1, /** A grey button */ From 34bfeab05e394bee74f9704939dd78b3e16a3042 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 10:23:03 -0400 Subject: [PATCH 23/31] fix button styles enum --- src/types/messages/components/button_styles.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/messages/components/button_styles.ts b/src/types/messages/components/button_styles.ts index dae735204..4bb3ac1e9 100644 --- a/src/types/messages/components/button_styles.ts +++ b/src/types/messages/components/button_styles.ts @@ -1,4 +1,4 @@ -export enum ButtonStyles { +export enum DiscordButtonStyles { /** A blurple button */ Primary = 1, /** A grey button */ @@ -10,3 +10,6 @@ export enum ButtonStyles { /** A button that navigates to a URL */ Link, } + +export type ButtonStyles = DiscordButtonStyles; +export const ButtonStyles = DiscordButtonStyles; From c8deb2abb1fff42c9b557255bb38a49d3aa87e65 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 10:23:37 -0400 Subject: [PATCH 24/31] leave todo --- src/types/messages/components/button_styles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/messages/components/button_styles.ts b/src/types/messages/components/button_styles.ts index 4bb3ac1e9..d0fc2b33c 100644 --- a/src/types/messages/components/button_styles.ts +++ b/src/types/messages/components/button_styles.ts @@ -1,3 +1,4 @@ +// TODO: add docs link export enum DiscordButtonStyles { /** A blurple button */ Primary = 1, From 6e8987104ff6e98d7fad0c6a0fdaa02ad4e92786 Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Sat, 1 May 2021 10:24:30 -0400 Subject: [PATCH 25/31] Update src/types/messages/components/message_component_types.ts Co-authored-by: ITOH <72305210+itohatweb@users.noreply.github.com> --- src/types/messages/components/message_component_types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/messages/components/message_component_types.ts b/src/types/messages/components/message_component_types.ts index aaa89ecb9..cf9c2399c 100644 --- a/src/types/messages/components/message_component_types.ts +++ b/src/types/messages/components/message_component_types.ts @@ -1,4 +1,5 @@ -export enum MessageComponentTypes { +// TODO: add docs link +export enum DiscordMessageComponentTypes { /** A row of components at the bottom of a message */ ActionRow = 1, /** A button! */ From 863595f4259a572e5cbc391391f8d736ebfeb8a1 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 10:25:35 -0400 Subject: [PATCH 26/31] fix message components enum --- src/types/messages/components/message_component_types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/messages/components/message_component_types.ts b/src/types/messages/components/message_component_types.ts index aaa89ecb9..858887606 100644 --- a/src/types/messages/components/message_component_types.ts +++ b/src/types/messages/components/message_component_types.ts @@ -1,6 +1,9 @@ -export enum MessageComponentTypes { +export enum DiscordMessageComponentTypes { /** A row of components at the bottom of a message */ ActionRow = 1, /** A button! */ Button, } + +export type MessageComponentTypes = DiscordMessageComponentTypes; +export const MessageComponentTypes = DiscordMessageComponentTypes; From cb83b00c7634c9fdd6b7fcf6c54b4c14a1a9ca36 Mon Sep 17 00:00:00 2001 From: ITOH <72305210+itohatweb@users.noreply.github.com> Date: Sat, 1 May 2021 16:33:19 +0200 Subject: [PATCH 27/31] Update src/types/interactions/interaction.ts --- src/types/interactions/interaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/interactions/interaction.ts b/src/types/interactions/interaction.ts index ed6bcd9f0..975238ac8 100644 --- a/src/types/interactions/interaction.ts +++ b/src/types/interactions/interaction.ts @@ -26,6 +26,6 @@ export interface Interaction { token: string; /** Read-only property, always `1` */ version: 1; - /** for the message the button was attached to */ + /** For the message the button was attached to */ message?: Message; } From 7da202b5bffea40507313d273b7a15094c18df4f Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 11:02:37 -0400 Subject: [PATCH 28/31] Remove unused import --- src/types/messages/components/action_row.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/messages/components/action_row.ts b/src/types/messages/components/action_row.ts index 43eac4f76..27a4a26e2 100644 --- a/src/types/messages/components/action_row.ts +++ b/src/types/messages/components/action_row.ts @@ -1,4 +1,3 @@ -import { SnakeCasedPropertiesDeep } from "../../util.ts"; import { ButtonComponent } from "./button_component.ts"; // TODO: add docs link From a1e40a670ca706391ecbe04d225abea98e768f71 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 11:06:25 -0400 Subject: [PATCH 29/31] fix exports for type guards --- src/helpers/mod.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/mod.ts b/src/helpers/mod.ts index 483f7378f..44bb6a467 100644 --- a/src/helpers/mod.ts +++ b/src/helpers/mod.ts @@ -122,6 +122,9 @@ import { executeWebhook } from "./webhooks/execute_webhook.ts"; import { getWebhook } from "./webhooks/get_webhook.ts"; import { getWebhooks } from "./webhooks/get_webhooks.ts"; import { getWebhookWithToken } from "./webhooks/get_webhook_with_token.ts"; +// Type Guards +import { isActionRow } from "./type_guards/is_action_row.ts"; +import { isButton } from "./type_guards/is_button.ts"; export { addDiscoverySubcategory, @@ -225,6 +228,8 @@ export { guildBannerURL, guildIconURL, guildSplashURL, + isActionRow, + isButton, isChannelSynced, kick, kickMember, From 8a42bc71cb14a8bf025129d0906849bf3f0b6040 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 11:08:57 -0400 Subject: [PATCH 30/31] fix broken import --- tests/messages/add_reaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/messages/add_reaction.ts b/tests/messages/add_reaction.ts index e8df075d9..540cb0776 100644 --- a/tests/messages/add_reaction.ts +++ b/tests/messages/add_reaction.ts @@ -1,11 +1,11 @@ import { defaultTestOptions, tempData } from "../ws/start_bot.ts"; import { assertEquals, assertExists } from "../deps.ts"; import { cache } from "../../src/cache.ts"; -import { DiscordReaction } from "../../src/types/messages/reaction.ts"; import { sendMessage } from "../../src/helpers/messages/send_message.ts"; import { addReaction } from "../../src/helpers/messages/add_reaction.ts"; import { createEmoji } from "../../src/helpers/emojis/create_emoji.ts"; import { delayUntil } from "../util/delay_until.ts"; +import { Reaction } from "../../src/types/messages/reaction.ts"; async function ifItFailsBlameWolf(type: "getter" | "raw", custom = false) { const message = await sendMessage(tempData.channelId, "Hello World!"); @@ -54,7 +54,7 @@ async function ifItFailsBlameWolf(type: "getter" | "raw", custom = false) { await cache.messages .get(message.id) ?.reactions?.filter( - (reaction: DiscordReaction) => + (reaction: Reaction) => reaction.emoji?.name === (custom ? "blamewolf" : "❤"), ).length, 1, From 4b3bc471f2f5993d0a05acc00b284ab36d7ae882 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 1 May 2021 11:11:39 -0400 Subject: [PATCH 31/31] types are good now --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7e3a5591..cfbb40471 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,13 +15,13 @@ jobs: with: deno-version: ${{ matrix.deno }} - name: Cache dependencies - run: deno cache --no-check mod.ts + run: deno cache mod.ts - name: Run test script for maintainers if: ${{ github.actor == 'Skillz4Killz' || github.actor == 'itohatweb' }} - run: deno test --unstable --coverage=coverage -A --no-check tests/mod.ts + run: deno test --unstable --coverage=coverage -A tests/mod.ts - name: Run test script if label added if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'run-tests' }} - run: DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} deno test --unstable --coverage=coverage --allow-net --no-check tests/mod.ts + run: DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} deno test --unstable --coverage=coverage --allow-net tests/mod.ts - name: Create coverage report run: deno --unstable coverage ./coverage --lcov > coverage.lcov - name: Collect and upload the coverage report