diff --git a/README.md b/README.md index 51c0b85c2..7e5281eeb 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ const baseBot = createBot({ console.log("Successfully connected to gateway"); }, messageCreate(bot, message) { - // Process the message with your command handler here + // Process the message here with your command handler }, }, }); diff --git a/helpers/guilds/automod/createAutomodRule.ts b/helpers/guilds/automod/createAutomodRule.ts index bc6b3d06d..3fbbe99f9 100644 --- a/helpers/guilds/automod/createAutomodRule.ts +++ b/helpers/guilds/automod/createAutomodRule.ts @@ -20,6 +20,7 @@ export async function createAutomodRule(bot: Bot, guildId: bigint, options: Crea trigger_metadata: { keyword_filter: options.triggerMetadata.keywordFilter, presets: options.triggerMetadata.presets, + allow_list: options.triggerMetadata.allowList, }, actions: options.actions.map((action) => ({ type: action.type, @@ -48,11 +49,12 @@ export interface CreateAutoModerationRuleOptions { triggerType: AutoModerationTriggerTypes; /** The metadata to use for the trigger. */ triggerMetadata: { - // TODO: discord is considering renaming this before release /** The keywords needed to match. Only present when TriggerType.Keyword */ keywordFilter?: string[]; /** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */ presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]; + /** The substrings which will exempt from triggering the preset trigger type. Only present when TriggerType.KeywordPreset */ + allowList?: string[]; }; /** The actions that will trigger for this rule */ actions: { diff --git a/helpers/guilds/automod/editAutomodRule.ts b/helpers/guilds/automod/editAutomodRule.ts index 04b522498..19f0c588d 100644 --- a/helpers/guilds/automod/editAutomodRule.ts +++ b/helpers/guilds/automod/editAutomodRule.ts @@ -19,6 +19,7 @@ export async function editAutomodRule(bot: Bot, guildId: bigint, options: Partia ? { keyword_filter: options.triggerMetadata.keywordFilter, presets: options.triggerMetadata.presets, + allow_list: options.triggerMetadata.allowList, } : undefined, actions: options.actions?.map((action) => ({ @@ -44,13 +45,13 @@ export interface EditAutoModerationRuleOptions { eventType: AutoModerationEventTypes; /** The metadata to use for the trigger. */ triggerMetadata: { - // TODO: discord is considering renaming this before release /** The keywords needed to match. Only present when TriggerType.Keyword */ keywordFilter?: string[]; - // TODO: discord is considering renaming this before release // TODO: This may need a special type or enum /** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */ presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]; + /** The substrings which will exempt from triggering the preset trigger type. Only present when TriggerType.KeywordPreset */ + allowList?: string[]; }; /** The actions that will trigger for this rule */ actions: { diff --git a/helpers/interactions/commands/createApplicationCommand.ts b/helpers/interactions/commands/createApplicationCommand.ts index e8109238e..70a1077b1 100644 --- a/helpers/interactions/commands/createApplicationCommand.ts +++ b/helpers/interactions/commands/createApplicationCommand.ts @@ -62,6 +62,8 @@ export function makeOptionsForCommand(options: ApplicationCommandOption[]): Disc autocomplete: option.autocomplete, min_value: option.minValue, max_value: option.maxValue, + min_length: option.minLength, + max_length: option.maxLength, })); } diff --git a/helpers/interactions/commands/editInteractionResponse.ts b/helpers/interactions/commands/editInteractionResponse.ts index 8a287cef2..2fdafbe12 100644 --- a/helpers/interactions/commands/editInteractionResponse.ts +++ b/helpers/interactions/commands/editInteractionResponse.ts @@ -62,6 +62,7 @@ export async function editInteractionResponse( placeholder: subComponent.placeholder, min_values: subComponent.minValues, max_values: subComponent.maxValues, + disabled: "disabled" in subComponent ? subComponent.disabled : undefined, options: subComponent.options.map((option) => ({ label: option.label, value: option.value, diff --git a/helpers/interactions/followups/editFollowupMessage.ts b/helpers/interactions/followups/editFollowupMessage.ts index ce1361808..e0b304d7b 100644 --- a/helpers/interactions/followups/editFollowupMessage.ts +++ b/helpers/interactions/followups/editFollowupMessage.ts @@ -59,6 +59,7 @@ export async function editFollowupMessage( placeholder: subComponent.placeholder, min_values: subComponent.minValues, max_values: subComponent.maxValues, + disabled: "disabled" in subComponent ? subComponent.disabled : undefined, options: subComponent.options.map((option) => ({ label: option.label, value: option.value, diff --git a/helpers/interactions/sendInteractionResponse.ts b/helpers/interactions/sendInteractionResponse.ts index 8ae1665c3..bd7247965 100644 --- a/helpers/interactions/sendInteractionResponse.ts +++ b/helpers/interactions/sendInteractionResponse.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; import { Embed } from "../../mod.ts"; import { DiscordMessage } from "../../types/discord.ts"; import { AllowedMentions, FileContent, MessageComponents } from "../../types/discordeno.ts"; -import { InteractionResponseTypes, MessageComponentTypes } from "../../types/shared.ts"; +import { InteractionResponseTypes } from "../../types/shared.ts"; /** * Send a response to a users application command. The command data will have the id and token necessary to respond. @@ -99,6 +99,14 @@ export interface InteractionApplicationCommandCallbackData { choices?: ApplicationCommandOptionChoice[]; } +/* https://discord.com/developers/docs/resources/channel#message-object-message-flags */ +export enum ApplicationCommandFlags { + /* Do not include any embeds when serialising this message */ + SuppressEmbeds = 1 << 2, + /** Only visible to the user who invoked the interaction */ + Ephemeral = 1 << 6, +} + /** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */ export interface ApplicationCommandOptionChoice { /** 1-100 character choice name */ diff --git a/helpers/members/fetchMembers.ts b/helpers/members/fetchMembers.ts index ee7dc3a5b..7674be312 100644 --- a/helpers/members/fetchMembers.ts +++ b/helpers/members/fetchMembers.ts @@ -1,5 +1,6 @@ import type { Bot } from "../../bot.ts"; import { GatewayIntents, GatewayOpcodes } from "../../types/shared.ts"; +import { calculateShardId } from "../../util/calculateShardId.ts"; /** * Highly recommended to use this function to fetch members instead of getMember from REST. @@ -9,7 +10,6 @@ import { GatewayIntents, GatewayOpcodes } from "../../types/shared.ts"; export function fetchMembers( bot: Bot, guildId: bigint, - shardId: number, options?: Omit, ) { // You can request 1 member without the intent @@ -22,6 +22,8 @@ export function fetchMembers( options.limit = options.userIds.length; } + const shardId = calculateShardId(bot.gateway, guildId); + return new Promise((resolve) => { const nonce = `${guildId}-${Date.now()}`; bot.cache.fetchAllMembersProcessingRequests.set(nonce, resolve); diff --git a/helpers/messages/editMessage.ts b/helpers/messages/editMessage.ts index e870d9ee6..912dffb90 100644 --- a/helpers/messages/editMessage.ts +++ b/helpers/messages/editMessage.ts @@ -53,6 +53,7 @@ export async function editMessage(bot: Bot, channelId: bigint, messageId: bigint placeholder: subComponent.placeholder, min_values: subComponent.minValues, max_values: subComponent.maxValues, + disabled: "disabled" in subComponent ? subComponent.disabled : undefined, options: subComponent.options.map((option) => ({ label: option.label, value: option.value, diff --git a/helpers/messages/sendMessage.ts b/helpers/messages/sendMessage.ts index 169603d85..df404cbf0 100644 --- a/helpers/messages/sendMessage.ts +++ b/helpers/messages/sendMessage.ts @@ -45,6 +45,7 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: CreateMe placeholder: subComponent.placeholder, min_values: subComponent.minValues, max_values: subComponent.maxValues, + disabled: "disabled" in subComponent ? subComponent.disabled : undefined, options: subComponent.options.map((option) => ({ label: option.label, value: option.value, diff --git a/helpers/roles/editRole.ts b/helpers/roles/editRole.ts index cd4f18753..bd7536aa0 100644 --- a/helpers/roles/editRole.ts +++ b/helpers/roles/editRole.ts @@ -13,7 +13,9 @@ export async function editRole(bot: Bot, guildId: bigint, id: bigint, options: E color: options.color, hoist: options.hoist, mentionable: options.mentionable, - permissions: options.permissions ? bot.utils.calculateBits(options.permissions) : undefined, + permissions: bot.utils.calculateBits(options?.permissions || []), + icon: options.icon, + unicode_emoji: options.unicodeEmoji, }, ); @@ -33,4 +35,6 @@ export interface EditGuildRole { mentionable?: boolean; /** The role's unicode emoji (if the guild has the `ROLE_ICONS` feature) */ unicodeEmoji?: string; + /** the role's icon image (if the guild has the `ROLE_ICONS` feature) */ + icon?: string; } diff --git a/helpers/voice/leaveVoiceChannel.ts b/helpers/voice/leaveVoiceChannel.ts new file mode 100644 index 000000000..f28022b27 --- /dev/null +++ b/helpers/voice/leaveVoiceChannel.ts @@ -0,0 +1,24 @@ +import type { Bot } from "../../bot.ts"; +import { AtLeastOne, GatewayOpcodes } from "../../types/shared.ts"; + +/** Connect or join a voice channel inside a guild. By default, the "selfDeaf" option is true. Requires `CONNECT` and `VIEW_CHANNEL` permissions. */ +export async function connectToVoiceChannel( + bot: Bot, + guildId: bigint, +) { + const shardId = bot.utils.calculateShardId(bot.gateway, guildId); + const shard = bot.gateway.manager.shards.get(shardId); + if (!shard) { + throw new Error(`Shard (id: ${shardId} not found`); + } + + shard.send({ + op: GatewayOpcodes.VoiceStateUpdate, + d: { + guild_id: guildId.toString(), + channel_id: null, + self_mute: false, + self_deaf: false, + }, + }); +} diff --git a/helpers/webhooks/editWebhookMessage.ts b/helpers/webhooks/editWebhookMessage.ts index 05fb18bd5..07ce06017 100644 --- a/helpers/webhooks/editWebhookMessage.ts +++ b/helpers/webhooks/editWebhookMessage.ts @@ -62,6 +62,7 @@ export async function editWebhookMessage( placeholder: subComponent.placeholder, min_values: subComponent.minValues, max_values: subComponent.maxValues, + disabled: "disabled" in subComponent ? subComponent.disabled : undefined, options: subComponent.options.map((option) => ({ label: option.label, value: option.value, diff --git a/helpers/webhooks/sendWebhook.ts b/helpers/webhooks/sendWebhook.ts index fcd1fe8dc..23132f118 100644 --- a/helpers/webhooks/sendWebhook.ts +++ b/helpers/webhooks/sendWebhook.ts @@ -21,6 +21,7 @@ export async function sendWebhook(bot: Bot, webhookId: bigint, webhookToken: str { wait: options.wait, thread_id: options.threadId, + thread_name: options.threadName, content: options.content, username: options.username, avatar_url: options.avatarUrl, @@ -42,6 +43,8 @@ export interface ExecuteWebhook { wait?: boolean; /** Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. */ threadId?: bigint; + /** Name of the thread to create (target channel has to be type of forum channel) */ + threadName?: string; /** The message contents (up to 2000 characters) */ content?: string; /** Override the default username of the webhook */ diff --git a/plugins/permissions/src/components.ts b/plugins/permissions/src/components.ts index 8734173b7..cd250d9b4 100644 --- a/plugins/permissions/src/components.ts +++ b/plugins/permissions/src/components.ts @@ -140,6 +140,55 @@ export function validateComponents(bot: Bot, components: MessageComponents) { option.emoji = makeEmojiFromString(option.emoji); } } + + if (subComponent.type === MessageComponentTypes.InputText) { + // Other buttons must have a customId + if ( + !subComponent.customId + ) { + throw new Error( + "The text input requires a custom id", + ); + } + + if (!bot.utils.validateLength(subComponent.label, { max: 45 })) { + throw new Error("The label can not be longer than 45 characters."); + } + + if (subComponent.minLength) { + if (subComponent.minLength < 0) { + throw new Error( + "The min length must be more than 0 in a text input component.", + ); + } + + if (subComponent.minLength > 4000) { + throw new Error( + "The min length must be less than 4000 in a text input component.", + ); + } + + if (subComponent.maxLength && subComponent.minLength > subComponent.maxLength) { + throw new Error( + "The text input component can not have a higher min length than the max length.", + ); + } + } + + if (subComponent.maxLength) { + if (subComponent.maxLength < 1) { + throw new Error( + "The max length must be more than 1 in a text input component.", + ); + } + + if (subComponent.maxLength > 4000) { + throw new Error( + "The max length must be less than 4000 in a text input component.", + ); + } + } + } } } } diff --git a/plugins/permissions/src/webhooks/createWebhook.ts b/plugins/permissions/src/webhooks/createWebhook.ts index 214cde9f0..878534ac6 100644 --- a/plugins/permissions/src/webhooks/createWebhook.ts +++ b/plugins/permissions/src/webhooks/createWebhook.ts @@ -5,15 +5,20 @@ export default function createWebhook(bot: BotWithCache) { const createWebhookOld = bot.helpers.createWebhook; bot.helpers.createWebhook = async function (channelId, options) { - requireBotChannelPermissions(bot, channelId, ["MANAGE_WEBHOOKS"]); + requireBotChannelPermissions(bot, channelId, ["MANAGE_WEBHOOKS", "VIEW_CHANNEL"]); if ( // Specific usernames that discord does not allow - options.name === "clyde" || + ["clyde", "everyone", "here"].includes(options.name) || + options.name.includes("discord") || + options.name.includes("@") || + options.name.includes("#") || + options.name.includes(":") || + options.name.includes("\/") || !bot.utils.validateLength(options.name, { min: 2, max: 32 }) ) { throw new Error( - "The webhook name can not be clyde and it must be between 2 and 32 characters long.", + "The webhook name can not be clyde, everyone, here or include 'discord, @, #, :, ' and it must be between 2 and 32 characters long.", ); } diff --git a/plugins/permissions/src/webhooks/deleteWebhook.ts b/plugins/permissions/src/webhooks/deleteWebhook.ts index 96313b30f..1dc0c5694 100644 --- a/plugins/permissions/src/webhooks/deleteWebhook.ts +++ b/plugins/permissions/src/webhooks/deleteWebhook.ts @@ -5,7 +5,7 @@ export default function deleteWebhook(bot: BotWithCache) { const deleteWebhookOld = bot.helpers.deleteWebhook; bot.helpers.deleteWebhook = async function (channelId, options) { - requireBotChannelPermissions(bot, channelId, ["MANAGE_WEBHOOKS"]); + requireBotChannelPermissions(bot, channelId, ["MANAGE_WEBHOOKS", "VIEW_CHANNEL"]); return await deleteWebhookOld(channelId, options); }; diff --git a/plugins/permissions/src/webhooks/editWebhook.ts b/plugins/permissions/src/webhooks/editWebhook.ts index ec3118b0d..fc2f879e3 100644 --- a/plugins/permissions/src/webhooks/editWebhook.ts +++ b/plugins/permissions/src/webhooks/editWebhook.ts @@ -5,8 +5,8 @@ export default function editWebhook(bot: BotWithCache) { const editWebhookOld = bot.helpers.editWebhook; bot.helpers.editWebhook = async function (webhookId, options, fromChannelId) { - if (options.channelId) requireBotChannelPermissions(bot, options.channelId, ["MANAGE_WEBHOOKS"]); - if (fromChannelId) requireBotChannelPermissions(bot, fromChannelId, ["MANAGE_WEBHOOKS"]); + if (options.channelId) requireBotChannelPermissions(bot, options.channelId, ["MANAGE_WEBHOOKS", "VIEW_CHANNEL"]); + if (fromChannelId) requireBotChannelPermissions(bot, fromChannelId, ["MANAGE_WEBHOOKS", "VIEW_CHANNEL"]); if (options.name) { if ( diff --git a/transformers/applicationCommandOption.ts b/transformers/applicationCommandOption.ts index 38f1d7c94..918784580 100644 --- a/transformers/applicationCommandOption.ts +++ b/transformers/applicationCommandOption.ts @@ -20,7 +20,8 @@ export function transformApplicationCommandOption( channelTypes: payload.channel_types, minValue: payload.min_value, maxValue: payload.max_value, - + minLength: payload.min_length, + maxLength: payload.max_length, options: payload.options?.map((option) => bot.transformers.applicationCommandOption(bot, option)), }; } @@ -52,4 +53,8 @@ export interface ApplicationCommandOption { minValue?: number; /** Maximum number desired. */ maxValue?: number; + /** Minimum length desired. */ + minLength?: number; + /** Maximum length desired. */ + maxLength?: number; } diff --git a/transformers/automodRule.ts b/transformers/automodRule.ts index 0d77c3346..31822b622 100644 --- a/transformers/automodRule.ts +++ b/transformers/automodRule.ts @@ -17,6 +17,7 @@ export function transformAutoModerationRule(bot: Bot, payload: DiscordAutoModera ? { keywordFilter: payload.trigger_metadata.keyword_filter, presets: payload.trigger_metadata.presets, + allowList: payload.trigger_metadata.allow_list, } : undefined, actions: payload.actions.map((action) => ({ diff --git a/transformers/interaction.ts b/transformers/interaction.ts index eb8f8292f..5cbb49c0c 100644 --- a/transformers/interaction.ts +++ b/transformers/interaction.ts @@ -30,6 +30,7 @@ export function transformInteraction(bot: Bot, payload: DiscordInteraction) { user, id: bot.transformers.snowflake(payload.id), applicationId: bot.transformers.snowflake(payload.application_id), + appPermissions: payload.app_permissions ? bot.transformers.snowflake(payload.app_permissions) : undefined, message: payload.message ? bot.transformers.message(bot, payload.message) : undefined, channelId: payload.channel_id ? bot.transformers.snowflake(payload.channel_id) : undefined, member: payload.member && guildId ? bot.transformers.member(bot, payload.member, guildId, user.id) : undefined, diff --git a/types/discord.ts b/types/discord.ts index 47b85414b..540b08833 100644 --- a/types/discord.ts +++ b/types/discord.ts @@ -219,7 +219,7 @@ export interface DiscordMember { premium_since?: string | null; /** The permissions this member has in the guild. Only present on interaction events. */ permissions?: string; - /** when the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out */ + /** when the user's timeout will expire and the user will be able to communicate in the guild again (set null to remove timeout), null or a time in the past if the user is not timed out */ communication_disabled_until?: string | null; } @@ -709,13 +709,13 @@ export interface DiscordChannel { name?: string; /** The channel topic (0-1024 characters) */ topic?: string | null; - /** The bitrate (in bits) of the voice channel */ + /** The bitrate (in bits) of the voice or stage channel */ bitrate?: number; - /** The user limit of the voice channel */ + /** The user limit of the voice or stage channel */ user_limit?: number; /** 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 */ rate_limit_per_user?: number; - /** Voice region id for the voice channel, automatic when set to null */ + /** Voice region id for the voice or stage channel, automatic when set to null */ rtc_region?: string | null; /** The camera video quality mode of the voice channel, 1 when not present */ video_quality_mode?: VideoQualityModes; @@ -1121,7 +1121,7 @@ export interface DiscordMessageInteraction { id: string; /** The type of interaction */ type: InteractionTypes; - /** The name of the ApplicationCommand */ + /** The name of the ApplicationCommand including the name of the subcommand/subcommand group*/ name: string; /** The user who invoked the interaction */ user: DiscordUser; @@ -1217,7 +1217,7 @@ export interface DiscordInputTextComponent { style: TextStyles; /** The customId of the InputText */ custom_id: string; - /** The label of the InputText */ + /** The label of the InputText (max 45 characters)*/ label: string; /** The placeholder of the InputText */ placeholder?: string; @@ -1286,6 +1286,8 @@ export interface DiscordInteraction { locale?: string; /** The guild's preferred locale, if invoked in a guild */ guild_locale?: string; + /** The computed permissions for a bot or app in the context of a specific interaction (including channel overwrites) */ + app_permissions: string; } /** https://discord.com/developers/docs/resources/guild#guild-member-object */ @@ -1407,7 +1409,7 @@ export interface DiscordAuditLog { export interface DiscordAutoModerationRule { /** The id of this rule */ id: string; - /** The guild id */ + /** The guild id of the rule */ guild_id: string; /** The name of the rule */ name: string; @@ -1447,6 +1449,8 @@ export interface DiscordAutoModerationRuleTriggerMetadata { keyword_filter?: string[]; /** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */ presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]; + /** The substrings which will exempt from triggering the preset trigger type. Only present when TriggerType.KeywordPreset */ + allow_list: string[]; } export enum DiscordAutoModerationRuleTriggerMetadataPresets { @@ -1502,7 +1506,7 @@ export interface DiscordAutoModerationActionExecution { alert_system_message_id?: string | null; /** The word or phrase that triggerred the rule. */ matched_keyword: string | null; - /** The substring in content that triggered rule */ + /** The substring in content that triggered the rule */ matched_content: string | null; } @@ -1862,6 +1866,10 @@ export interface DiscordApplicationCommandOption { min_value?: number; /** If the option type is `ApplicationCommandOptionTypes.Integer` or `ApplicationCommandOptionTypes.Number`, the maximum permitted value */ max_value?: number; + /** If the option type is `ApplicationCommandOptionTypes.String`, the minimum permitted length */ + min_length?: number; + /** If the option type is `ApplicationCommandOptionTypes.String`, the maximum permitted length */ + max_length?: number; } /** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure */ diff --git a/types/discordeno.ts b/types/discordeno.ts index 48ec45b74..3e04dc928 100644 --- a/types/discordeno.ts +++ b/types/discordeno.ts @@ -54,6 +54,8 @@ export interface SelectMenuComponent { maxValues?: number; /** The choices! Maximum of 25 items. */ options: SelectOption[]; + /** Whether or not this select is disabled */ + disabled?: boolean; } export interface SelectOption { @@ -84,7 +86,7 @@ export interface InputTextComponent { style: TextStyles; /** The customId of the InputText */ customId: string; - /** The label of the InputText */ + /** The label of the InputText. Maximum 45 characters */ label: string; /** The placeholder of the InputText */ placeholder?: string; diff --git a/types/shared.ts b/types/shared.ts index a9c76de56..af5b7c5fc 100644 --- a/types/shared.ts +++ b/types/shared.ts @@ -316,11 +316,11 @@ export enum MessageTypes { ChannelNameChange, ChannelIconChange, ChannelPinnedMessage, - GuildMemberJoin, - UserPremiumGuildSubscription, - UserPremiumGuildSubscriptionTier1, - UserPremiumGuildSubscriptionTier2, - UserPremiumGuildSubscriptionTier3, + UserJoin, + GuildBoost, + GuildBoostTier1, + GuildBoostTier2, + GuildBoostTier3, ChannelFollowAdd, GuildDiscoveryDisqualified = 14, GuildDiscoveryRequalified, @@ -738,6 +738,181 @@ export enum RpcCloseEventCodes { InvalidEncoding, } +/** https://discord.com/developers/docs/topics/opcodes-and-status-codes#json */ +export enum JsonErrorCodes { + /** General error (such as a malformed request body, amongst other things) */ + GeneralError, + UnknownAccount = 10001, + UnknownApplication, + UnknownChannel, + UnknownGuild, + UnknownIntegration, + UnknownInvite, + UnknownMember, + UnknownMessage, + UnknownPermissionOverwrite, + UnknownProvider, + UnknownRole, + UnknownToken, + UnknownUser, + UnknownEmoji, + UnknownWebhook, + UnknownWebhookService, + UnknownSession = 10020, + UnknownBan = 10026, + UnknownSKU, + UnknownStoreListing, + UnknownEntitlement, + UnknownBuild, + UnknownLobby, + UnknownBranch, + UnknownStoreDirectoryLayout, + UnknownRedistributable = 10036, + UnknownGiftCode = 10038, + UnknownStream = 10049, + UnknownPremiumServerSubscribeCooldown, + UnknownGuildTemplate = 10057, + UnknownDiscoveryCategory = 10059, + UnknownSticker, + UnknownInteraction = 10062, + UnknownApplicationCommand = 10063, + UnknownVoiceState = 10065, + UnknownApplicationCommandPermissions, + UnknownStageInstance, + UnknownGuildMemberVerificationForm, + UnknownGuildWelcomeScreen, + UnknownGuildScheduledEvent, + UnknownGuildScheduledEventUser, + UnknownTag = 10087, + BotsCannotUseThisEndpoint = 20001, + OnlyBotsCanUseThisEndpoint, + ExplicitContentCannotBeSentToTheDesiredRecipient = 20009, + YouAreNotAuthorizedToPerformThisActionOnThisApplication = 20012, + ThisActionCannotBePerformedDueToSlowmodeRateLimit = 20016, + OnlyTheOwnerOfThisAccountCanPerformThisAction = 20018, + ThisMessageCannotBeEditedDueToAnnouncementRateLimits = 20022, + UnderMinimumAge = 20024, + TheChannelYouAreWritingHasHitTheWriteRateLimit = 20028, + TheWriteActionYouArePerformingOnTheServerHasHitTheWriteRateLimit, + YourStageTopicOrServerNameOrServerDescriptionOrChannelNamesContainsWordsThatAreNotAllowedForPublicStages = 20031, + GuildPremiumSubscriptionLevelTooLow = 20035, + MaximumNumberOfGuildsReached = 30001, + MaximumNumberOfFriendsReached, + MaximumNumberOfPinsReachedForTheChannel, + MaximumNumberOfRecipientsReached, + MaximumNumberOfGuildRolesReached, + MaximumNumberOfWebhooksReached = 30007, + MaximumNumberOfEmojisReached, + MaximumNumberOfReactionsReached = 30010, + MaximumNumberOfGuildChannelsReached = 30013, + MaximumNumberOfAttachmentsInAMessageReached = 30015, + MaximumNumberOfInvitesReached, + MaximumNumberOfAnimatedEmojisReached = 30018, + MaximumNumberOfServerMembersReached, + MaximumNumberOfServerCategoriesHasBeenReached = 30030, + GuildAlreadyHasTemplate, + MaximumNumbersOfApplicationCommandsReached, + MaxNumberOfThreadParticipantsHasBeenReached, + MaxNumberOfDailyApplicationCommandCreatesHasBeenReached, + MaximumNumberOfBansForNonGuildMembersHaveBeenExceeded, + MaximumNumberOfBansFetchesHasBeenReached = 30037, + MaximumNumberOfUncompletedGuildScheduledEventsReached = 30038, + MaximumNumberOfStickersReached = 30039, + MaximumNumberOfPruneRequestsHasBeenReachedTryAgainLater, + MaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReachedTryAgainLater = 30042, + MaximumNumberOfEditsToMessagesOlderThan1HourReachedTryAgainLater = 30046, + MaximumNumberOfPinnedThreadsInAForumChannelHasBeenReached, + MaxiumNumberOfTagsInAForumChannelHasBeenReached, + BitrateIsTooHighForChannelOfThisType = 30052, + UnauthorizedProvideAValidTokenAndTryAgain = 40001, + YouNeedToVerifyYourAccountInOrderToPerformThisAction, + YouAreOpeningDirectMessagesTooFast, + SendMessagesHasBeenTemporarilyDisabled, + RequestEntityTooLargeTrySendingSomethingSmallerInSize, + ThisFeatureHasBeenTemporarilyDisabledServerSide, + ThisUserBannedFromThisGuild, + ConnectionHasBeenRevoked = 40012, + TargetUserIsNotConnectedToVoice = 40032, + ThisMessageHasAlreadyBeenCrossposted, + AnApplicationCommandWithThatNameAlreadyExists = 40041, + ApplicationInteractionFailedToSend = 40043, + InteractionHasAlreadyBeenAcknowledged = 40060, + MissingAccess = 50001, + InvalidAccountType, + CannotExecuteActionOnADMChannel, + GuildWidgetDisabled, + CannotEditMessageAuthoredByAnotherUser, + CannotSendAnEmptyMessage, + CannotSendMessagesToThisUser, + CannotSendMessagesInANonTextChannel, + ChannelVerificationLevelIsTooHighForYouToGainAccess, + OAuth2ApplicationDoesNotHaveABot, + OAuth2ApplicationLimitReached, + InvalidOAuth2State, + YouLackPermissionsToPerformThatAction, + InvalidAuthenticationTokenProvided, + NoteWasTooLong, + ProvidedTooFewOrTooManyMessagesToDeleteMustProvideAtLeast2AndFewerThan100MessagesToDelete, + InvalidMFALevel, + AMessageCanOnlyBePinnedInTheChannelItWasSentIn = 50019, + InviteCodeWasEitherInvalidOrTaken, + CannotExecuteActionOnASystemMessage, + CannotExecuteActionOnThisChannelType = 50024, + InvalidOAuth2AccessTokenProvided, + MissingRequiredOAuth2Scope, + InvalidWebhookTokenProvided, + InvalidRole, + InvalidRecipients = 50033, + AMessageProvidedWasTooOldToBulkDelete, + /** Invalid form body (returned for both `application/json` and `multipart/form-data` bodies), or invalid `Content-Type` provided */ + InvalidFormBodyOrContentTypeProvided, + AnInviteWasAcceptedToAGuildTheApplicationsBotIsNotIn, + InvalidApiVersionProvided = 50041, + FileUploadedExceedsTheMaximumSize = 50045, + InvalidFileUploaded, + CannotSelfRedeemThisGift = 50054, + InvalidGuild, + InvalidMessageType = 50068, + PaymentSourceRequiredToRedeemGift = 50070, + CannotDeleteAChannelRequiredForCommunityGuilds = 50074, + CannotEditStickersWithinAMessage = 50080, + InvalidStickerSent, + TriedToPerformAnOperationOnAnArchivedThreadSuchAsEditingAMessageOrAddingAUserToTheThread = 50083, + InvalidThreadNotificationSettings, + BeforeValueIsEarlierThanTheThreadCreationDate, + CommunityServerChannelsMustBeTextChannels, + ThisServerIsNotAvailableInYourLocation = 50095, + ThisServerNeedsMonetizationEnabledInOrderToPerformThisAction = 50097, + ThisServerNeedsMoreBoostsToPerformThisAction = 50101, + TheRequestBodyContainsInvalidJSON = 50109, + OwnershipCannotBeTransferredToABotUser = 50132, + FailedToResizeAssetBelowTheMaximumSize = 50138, + UploadedFileNotFound = 50146, + TwoFactorIsRequiredForThisOperation = 60003, + NoUsersWithDiscordTagExist = 80004, + ReactionWasBlocked = 90001, + ApplicationNotYetAvailable = 110001, + ApiResourceIsCurrentlyOverloadedTryAgainALittleLater = 130000, + TheStageIsAlreadyOpen = 150006, + CannotReplyWithoutPermissionToReadMessageHistory = 160002, + AThreadHasAlreadyBeenCreatedForThisMessage = 160004, + ThreadIsLocked = 160005, + MaximumNumberOfActiveThreadsReached = 160006, + MaximumNumberOfActiveAnnouncementThreadsReached = 160007, + InvalidJsonForUploadedLottieFile = 170001, + UploadedLottiesCannotContainRasterizedImagesSuchAsPngOrJpeg, + StickerMaximumFramerateExceeded, + StickerFrameCountExceedsMaximumOf1000Frames, + LottieAnimationMaximumDimensionsExceeded, + StickerFrameRateIsEitherTooSmallOrTooLarge, + StickerAnimationDurationExceedsMaximumOf5Seconds, + CannotUpdateAFinishedEvent = 180000, + FailedToCreateStageNeededForStageEvent = 180002, + MessageWasBlockedByAutomaticModeration = 200000, + TitleWasBlockedByAutomaticModeration, + WebhooksCanOnlyCreateThreadsInForumChannels = 220003, +} + /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#http */ export enum HTTPResponseCodes { /** The request completed successfully. */ diff --git a/util/calculateShardId.ts b/util/calculateShardId.ts index 820d2124e..90debccce 100644 --- a/util/calculateShardId.ts +++ b/util/calculateShardId.ts @@ -3,5 +3,5 @@ import { GatewayManager } from "../gateway/manager/gatewayManager.ts"; export function calculateShardId(gateway: GatewayManager, guildId: bigint) { if (gateway.manager.totalShards === 1) return 0; - return Number((guildId >> 22n) % BigInt(gateway.manager.totalShards - 1)); + return Number((guildId >> 22n) % BigInt(gateway.manager.totalShards)); } diff --git a/util/routes.ts b/util/routes.ts index b5a4c3ac7..dc72f9d63 100644 --- a/util/routes.ts +++ b/util/routes.ts @@ -370,7 +370,7 @@ export const routes = { if (options) { if (options?.wait !== undefined) url += `wait=${options.wait}`; - if (options.threadId) url += `threadId=${options.threadId}`; + if (options.threadId) url += `thread_id=${options.threadId}`; } return url; @@ -382,7 +382,7 @@ export const routes = { let url = `/webhooks/${webhookId}/${token}/messages/${messageId}?`; if (options) { - if (options.threadId) url += `threadId=${options.threadId}`; + if (options.threadId) url += `thread_id=${options.threadId}`; } return url; @@ -391,7 +391,7 @@ export const routes = { let url = `/webhooks/${webhookId}/${token}/messages/@original?`; if (options) { - if (options.threadId) url += `threadId=${options.threadId}`; + if (options.threadId) url += `thread_id=${options.threadId}`; } return url;