diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 55b408cdc..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "benchmarks"] - path = benchmarks - url = https://github.com/discordeno/benchmarks.git diff --git a/benchmarks b/benchmarks deleted file mode 160000 index e4665ccd4..000000000 --- a/benchmarks +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e4665ccd4fceac87981834752e8362c03b366b60 diff --git a/bot.ts b/bot.ts index c8b6e185b..bf6ba8cd7 100644 --- a/bot.ts +++ b/bot.ts @@ -1,6 +1,26 @@ +import { createGatewayManager } from "./gateway/manager/gatewayManager.ts"; +import * as handlers from "./handlers/mod.ts"; +import * as helpers from "./helpers/mod.ts"; import { createRestManager, CreateRestManagerOptions } from "./rest/mod.ts"; -import { bigintToSnowflake, snowflakeToBigint } from "./util/bigint.ts"; -import { Collection } from "./util/collection.ts"; +import { transformActivity } from "./transformers/activity.ts"; +import { transformApplication } from "./transformers/application.ts"; +import { transformApplicationCommand } from "./transformers/applicationCommand.ts"; +import { transformApplicationCommandOption } from "./transformers/applicationCommandOption.ts"; +import { transformApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; +import { transformAttachment } from "./transformers/attachment.ts"; +import { transformAuditLogEntry } from "./transformers/auditLogEntry.ts"; +import { transformComponent } from "./transformers/component.ts"; +import { transformEmbed } from "./transformers/embed.ts"; +import { Emoji, transformEmoji } from "./transformers/emoji.ts"; +import { GetGatewayBot, transformGatewayBot } from "./transformers/gatewayBot.ts"; +import { Integration, transformIntegration } from "./transformers/integration.ts"; +import { + Interaction, + InteractionDataOption, + transformInteraction, + transformInteractionDataOption, +} from "./transformers/interaction.ts"; +import { Invite, transformInvite } from "./transformers/invite.ts"; import { Channel, Guild, @@ -9,6 +29,8 @@ import { Role, ScheduledEvent, Template, + transformApplicationCommandToDiscordApplicationCommand, + transformAttachmentToDiscordAttachment, transformChannel, transformGuild, transformMember, @@ -20,6 +42,35 @@ import { User, VoiceState, } from "./transformers/mod.ts"; +import { PresenceUpdate, transformPresence } from "./transformers/presence.ts"; +import { transformScheduledEvent } from "./transformers/scheduledEvent.ts"; +import { transformStageInstance } from "./transformers/stageInstance.ts"; +import { StickerPack, transformSticker, transformStickerPack } from "./transformers/sticker.ts"; +import { transformTeam } from "./transformers/team.ts"; +import { ThreadMember, transformThreadMember } from "./transformers/threadMember.ts"; +import { transformVoiceRegion } from "./transformers/voiceRegion.ts"; +import { transformWebhook } from "./transformers/webhook.ts"; +import { transformWelcomeScreen } from "./transformers/welcomeScreen.ts"; +import { transformWidget } from "./transformers/widget.ts"; +import { transformWidgetSettings } from "./transformers/widgetSettings.ts"; +import { + DiscordAllowedMentions, + DiscordApplicationCommandOptionChoice, + DiscordAutoModerationActionExecution, + DiscordAutoModerationRule, + DiscordCreateApplicationCommand, + DiscordEmoji, + DiscordGatewayPayload, + DiscordInteractionDataOption, + DiscordInteractionResponse, + DiscordReady, + DiscordStickerPack, + DiscordTemplate, +} from "./types/discord.ts"; +import { Errors, GatewayDispatchEventNames, GatewayIntents } from "./types/shared.ts"; +import { bigintToSnowflake, snowflakeToBigint } from "./util/bigint.ts"; +import { calculateShardId } from "./util/calculateShardId.ts"; +import { Collection } from "./util/collection.ts"; import { baseEndpoints, CHANNEL_MENTION_REGEX, @@ -29,58 +80,11 @@ import { SLASH_COMMANDS_NAME_REGEX, USER_AGENT, } from "./util/constants.ts"; -import { createGatewayManager, GatewayManager } from "./gateway/manager/gatewayManager.ts"; -import { validateLength } from "./util/validateLength.ts"; -import { delay, formatImageURL } from "./util/utils.ts"; import { iconBigintToHash, iconHashToBigInt } from "./util/hash.ts"; -import { calculateShardId } from "./util/calculateShardId.ts"; -import * as handlers from "./handlers/mod.ts"; -import { - Interaction, - InteractionDataOption, - transformInteraction, - transformInteractionDataOption, -} from "./transformers/interaction.ts"; -import { Integration, transformIntegration } from "./transformers/integration.ts"; -import { transformApplication } from "./transformers/application.ts"; -import { transformTeam } from "./transformers/team.ts"; -import { Invite, transformInvite } from "./transformers/invite.ts"; -import * as helpers from "./helpers/mod.ts"; -import { Emoji, transformEmoji } from "./transformers/emoji.ts"; -import { transformActivity } from "./transformers/activity.ts"; -import { PresenceUpdate, transformPresence } from "./transformers/presence.ts"; -import { urlToBase64 } from "./util/urlToBase64.ts"; -import { transformAttachment } from "./transformers/attachment.ts"; -import { transformEmbed } from "./transformers/embed.ts"; -import { transformComponent } from "./transformers/component.ts"; -import { transformWebhook } from "./transformers/webhook.ts"; -import { transformAuditLogEntry } from "./transformers/auditLogEntry.ts"; -import { transformApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; import { calculateBits, calculatePermissions } from "./util/permissions.ts"; -import { transformScheduledEvent } from "./transformers/scheduledEvent.ts"; -import { ThreadMember, transformThreadMember } from "./transformers/threadMember.ts"; -import { transformApplicationCommandOption } from "./transformers/applicationCommandOption.ts"; -import { transformApplicationCommand } from "./transformers/applicationCommand.ts"; -import { transformWelcomeScreen } from "./transformers/welcomeScreen.ts"; -import { transformVoiceRegion } from "./transformers/voiceRegion.ts"; -import { transformWidget } from "./transformers/widget.ts"; -import { transformWidgetSettings } from "./transformers/widgetSettings.ts"; -import { transformStageInstance } from "./transformers/stageInstance.ts"; -import { StickerPack, transformSticker, transformStickerPack } from "./transformers/sticker.ts"; -import { GetGatewayBot, transformGatewayBot } from "./transformers/gatewayBot.ts"; -import { - DiscordAllowedMentions, - DiscordApplicationCommandOptionChoice, - DiscordAutoModerationActionExecution, - DiscordAutoModerationRule, - DiscordEmoji, - DiscordGatewayPayload, - DiscordInteractionDataOption, - DiscordReady, - DiscordStickerPack, - DiscordTemplate, -} from "./types/discord.ts"; -import { Errors, GatewayDispatchEventNames, GatewayIntents } from "./types/shared.ts"; +import { urlToBase64 } from "./util/urlToBase64.ts"; +import { delay, formatImageURL } from "./util/utils.ts"; +import { validateLength } from "./util/validateLength.ts"; import { DiscordActivity, @@ -116,49 +120,52 @@ import { DiscordWelcomeScreen, } from "./types/discord.ts"; -import { Application } from "./transformers/application.ts"; -import { Team } from "./transformers/team.ts"; -import { Activity } from "./transformers/activity.ts"; -import { Attachment } from "./transformers/attachment.ts"; -import { Embed } from "./transformers/embed.ts"; -import { Webhook } from "./transformers/webhook.ts"; -import { Component } from "./transformers/component.ts"; -import { ApplicationCommand } from "./transformers/applicationCommand.ts"; -import { AuditLogEntry } from "./transformers/auditLogEntry.ts"; -import { ApplicationCommandOption } from "./transformers/applicationCommandOption.ts"; -import { ApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; -import { WelcomeScreen } from "./transformers/welcomeScreen.ts"; -import { VoiceRegions } from "./transformers/voiceRegion.ts"; -import { GuildWidget } from "./transformers/widget.ts"; -import { GuildWidgetSettings } from "./transformers/widgetSettings.ts"; -import { StageInstance } from "./transformers/stageInstance.ts"; -import { Sticker } from "./transformers/sticker.ts"; -import { - ApplicationCommandOptionChoice, - transformApplicationCommandOptionChoice, -} from "./transformers/applicationCommandOptionChoice.ts"; -import { transformEmbedToDiscordEmbed } from "./transformers/reverse/embed.ts"; -import { transformComponentToDiscordComponent } from "./transformers/reverse/component.ts"; -import { transformActivityToDiscordActivity } from "./transformers/reverse/activity.ts"; -import { transformTeamToDiscordTeam } from "./transformers/reverse/team.ts"; -import { transformMemberToDiscordMember, transformUserToDiscordUser } from "./transformers/reverse/member.ts"; -import { transformApplicationToDiscordApplication } from "./transformers/reverse/application.ts"; -import { getBotIdFromToken, removeTokenPrefix } from "./util/token.ts"; import { CreateShardManager } from "./gateway/manager/shardManager.ts"; -import { AutoModerationRule, transformAutoModerationRule } from "./transformers/automodRule.ts"; -import { - AutoModerationActionExecution, - transformAutoModerationActionExecution, -} from "./transformers/automodActionExecution.ts"; -import { routes } from "./util/routes.ts"; -import { transformAllowedMentionsToDiscordAllowedMentions } from "./transformers/reverse/allowedMentions.ts"; import { AllowedMentions, + CreateApplicationCommand, + InteractionResponse, ShardSocketCloseCodes, transformApplicationCommandOptionChoiceToDiscordApplicationCommandOptionChoice, transformApplicationCommandOptionToDiscordApplicationCommandOption, } from "./mod.ts"; -import { transformAttachmentToDiscordAttachment } from "./transformers/reverse/attachment.ts"; +import { Activity } from "./transformers/activity.ts"; +import { Application } from "./transformers/application.ts"; +import { ApplicationCommand } from "./transformers/applicationCommand.ts"; +import { ApplicationCommandOption } from "./transformers/applicationCommandOption.ts"; +import { + ApplicationCommandOptionChoice, + transformApplicationCommandOptionChoice, +} from "./transformers/applicationCommandOptionChoice.ts"; +import { ApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; +import { Attachment } from "./transformers/attachment.ts"; +import { AuditLogEntry } from "./transformers/auditLogEntry.ts"; +import { + AutoModerationActionExecution, + transformAutoModerationActionExecution, +} from "./transformers/automodActionExecution.ts"; +import { AutoModerationRule, transformAutoModerationRule } from "./transformers/automodRule.ts"; +import { Component } from "./transformers/component.ts"; +import { Embed } from "./transformers/embed.ts"; +import { transformActivityToDiscordActivity } from "./transformers/reverse/activity.ts"; +import { transformAllowedMentionsToDiscordAllowedMentions } from "./transformers/reverse/allowedMentions.ts"; +import { transformApplicationToDiscordApplication } from "./transformers/reverse/application.ts"; +import { transformComponentToDiscordComponent } from "./transformers/reverse/component.ts"; +import { transformCreateApplicationCommandToDiscordCreateApplicationCommand } from "./transformers/reverse/createApplicationCommand.ts"; +import { transformEmbedToDiscordEmbed } from "./transformers/reverse/embed.ts"; +import { transformInteractionResponseToDiscordInteractionResponse } from "./transformers/reverse/interactionResponse.ts"; +import { transformMemberToDiscordMember, transformUserToDiscordUser } from "./transformers/reverse/member.ts"; +import { transformTeamToDiscordTeam } from "./transformers/reverse/team.ts"; +import { StageInstance } from "./transformers/stageInstance.ts"; +import { Sticker } from "./transformers/sticker.ts"; +import { Team } from "./transformers/team.ts"; +import { VoiceRegions } from "./transformers/voiceRegion.ts"; +import { Webhook } from "./transformers/webhook.ts"; +import { WelcomeScreen } from "./transformers/welcomeScreen.ts"; +import { GuildWidget } from "./transformers/widget.ts"; +import { GuildWidgetSettings } from "./transformers/widgetSettings.ts"; +import { routes } from "./util/routes.ts"; +import { getBotIdFromToken, removeTokenPrefix } from "./util/token.ts"; export function createBot(options: CreateBotOptions): Bot { const bot = { @@ -415,11 +422,14 @@ export interface Transformers { team: (bot: Bot, payload: Team) => DiscordTeam; application: (bot: Bot, payload: Application) => DiscordApplication; snowflake: (snowflake: bigint) => string; + createApplicationCommand: (bot: Bot, payload: CreateApplicationCommand) => DiscordCreateApplicationCommand; + applicationCommand: (bot: Bot, payload: ApplicationCommand) => DiscordApplicationCommand; applicationCommandOption: (bot: Bot, payload: ApplicationCommandOption) => DiscordApplicationCommandOption; applicationCommandOptionChoice: ( bot: Bot, payload: ApplicationCommandOptionChoice, ) => DiscordApplicationCommandOptionChoice; + interactionResponse: (bot: Bot, payload: InteractionResponse) => DiscordInteractionResponse; attachment: (bot: Bot, payload: Attachment) => DiscordAttachment; }; snowflake: (snowflake: string) => bigint; @@ -481,10 +491,16 @@ export function createTransformers(options: Partial) { team: options.reverse?.team || transformTeamToDiscordTeam, application: options.reverse?.application || transformApplicationToDiscordApplication, snowflake: options.reverse?.snowflake || bigintToSnowflake, + createApplicationCommand: options.reverse?.createApplicationCommand || + transformCreateApplicationCommandToDiscordCreateApplicationCommand, + applicationCommand: options.reverse?.applicationCommand || + transformApplicationCommandToDiscordApplicationCommand, applicationCommandOption: options.reverse?.applicationCommandOption || transformApplicationCommandOptionToDiscordApplicationCommandOption, applicationCommandOptionChoice: options.reverse?.applicationCommandOptionChoice || transformApplicationCommandOptionChoiceToDiscordApplicationCommandOptionChoice, + interactionResponse: options.reverse?.interactionResponse || + transformInteractionResponseToDiscordInteractionResponse, attachment: options.reverse?.attachment || transformAttachmentToDiscordAttachment, }, automodRule: options.automodRule || transformAutoModerationRule, diff --git a/helpers/channels/announcements/followAnnouncementChannel.ts b/helpers/channels/announcements/followAnnouncementChannel.ts new file mode 100644 index 000000000..c155c5084 --- /dev/null +++ b/helpers/channels/announcements/followAnnouncementChannel.ts @@ -0,0 +1,34 @@ +import type { Bot } from "../../../bot.ts"; +import { DiscordFollowedChannel } from "../../../types/discord.ts"; + +/** + * Follows an announcement channel, allowing messages posted within it to be cross-posted into the target channel. + * + * @param bot - The bot instance to use to make the request. + * @param sourceChannelId - The ID of the announcement channel to follow. + * @param targetChannelId - The ID of the target channel - the channel to cross-post to. + * @returns An instance of {@link FollowedChannel}. + * + * @remarks + * Requires the `MANAGE_WEBHOOKS` permission in the __target channel__. + * + * Fires a _Webhooks Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#follow-announcement-channel} + */ +export async function followAnnouncementChannel( + bot: Bot, + sourceChannelId: bigint, + targetChannelId: bigint, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "POST", + bot.constants.routes.CHANNEL_FOLLOW(sourceChannelId), + { + webhook_channel_id: targetChannelId, + }, + ); + + return bot.transformers.snowflake(result.webhook_id); +} diff --git a/helpers/channels/announcements/mod.ts b/helpers/channels/announcements/mod.ts new file mode 100644 index 000000000..47fa8cc58 --- /dev/null +++ b/helpers/channels/announcements/mod.ts @@ -0,0 +1 @@ +export * from "./followAnnouncementChannel.ts"; diff --git a/helpers/channels/createChannel.ts b/helpers/channels/createChannel.ts index 2868adc8d..3ba1cb098 100644 --- a/helpers/channels/createChannel.ts +++ b/helpers/channels/createChannel.ts @@ -2,9 +2,27 @@ import type { Bot } from "../../bot.ts"; import { Channel } from "../../transformers/channel.ts"; import { DiscordChannel } from "../../types/discord.ts"; import { ChannelTypes } from "../../types/shared.ts"; -import { OverwriteReadable } from "./editChannelOverwrite.ts"; +import { OverwriteReadable } from "./editChannelPermissionOverrides.ts"; -/** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ +/** + * Creates a channel within a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to create the channel within. + * @param options - The parameters for the creation of the channel. + * @returns An instance of the created {@link Channel}. + * + * @remarks + * Requires the `MANAGE_CHANNELS` permission. + * + * If setting permission overwrites, only the permissions the bot user has in the guild can be allowed or denied. + * + * Setting the `MANAGE_ROLES` permission is only possible for guild administrators. + * + * Fires a _Channel Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#create-guild-channel} + */ export async function createChannel( bot: Bot, guildId: bigint, diff --git a/helpers/channels/createStageInstance.ts b/helpers/channels/createStageInstance.ts deleted file mode 100644 index 98125a20b..000000000 --- a/helpers/channels/createStageInstance.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { StageInstance } from "../../transformers/stageInstance.ts"; -import { DiscordStageInstance } from "../../types/discord.ts"; - -/** Creates a new Stage instance associated to a Stage channel. Requires the user to be a moderator of the Stage channel. */ -export async function createStageInstance(bot: Bot, options: CreateStageInstance): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "POST", - bot.constants.routes.STAGE_INSTANCES(), - { - channel_id: options.channelId.toString(), - topic: options.topic, - send_start_notification: options.sendStartNotification, - }, - ); - - return bot.transformers.stageInstance(bot, result); -} - -export interface CreateStageInstance { - channelId: bigint; - topic: string; - /** Notify @everyone that the stage instance has started. Requires the MENTION_EVERYONE permission. */ - sendStartNotification?: boolean; -} diff --git a/helpers/channels/deleteChannel.ts b/helpers/channels/deleteChannel.ts index 335e6c426..6385806a8 100644 --- a/helpers/channels/deleteChannel.ts +++ b/helpers/channels/deleteChannel.ts @@ -1,6 +1,30 @@ import type { Bot } from "../../bot.ts"; -/** Delete a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. Bot needs MANAGE_THREADS permissions in the server if deleting thread. */ +/** + * Deletes a channel from within a guild. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to delete. + * @returns An instance of the deleted {@link Channel}. + * + * @remarks + * For community guilds, the _Rules_, _Guidelines_ and _Community Update_ channels cannot be deleted. + * + * If the channel is a thread: + * - Requires the `MANAGE_THREADS` permission. + * + * - Fires a _Thread Delete_ gateway event. + * + * Otherwise: + * - Requires the `MANAGE_CHANNELS` permission. + * + * - ⚠️ Deleting a category channel does not delete its child channels. + * Instead, they will have their `parent_id` property removed, and a `Channel Update` gateway event will fire for each of them. + * + * - Fires a _Channel Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#deleteclose-channel} + */ export async function deleteChannel(bot: Bot, channelId: bigint, reason?: string): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/channels/deleteChannelOverwrite.ts b/helpers/channels/deleteChannelOverwrite.ts deleted file mode 100644 index 5c5f59ba1..000000000 --- a/helpers/channels/deleteChannelOverwrite.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Delete the channel permission overwrites for a user or role in this channel. Requires `MANAGE_ROLES` permission. */ -export async function deleteChannelOverwrite(bot: Bot, channelId: bigint, overwriteId: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - bot.constants.routes.CHANNEL_OVERWRITE(channelId, overwriteId), - ); -} diff --git a/helpers/channels/deleteChannelPermissionOverride.ts b/helpers/channels/deleteChannelPermissionOverride.ts new file mode 100644 index 000000000..e21efbd2f --- /dev/null +++ b/helpers/channels/deleteChannelPermissionOverride.ts @@ -0,0 +1,23 @@ +import type { Bot } from "../../bot.ts"; + +/** + * Deletes a permission override for a user or role in a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to delete the permission override of. + * @param overwriteId - The ID of the permission override to delete. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Channel Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-channel-permission} + */ +export async function deleteChannelPermissionOverride(bot: Bot, channelId: bigint, overwriteId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.CHANNEL_OVERWRITE(channelId, overwriteId), + ); +} diff --git a/helpers/channels/deleteStageInstance.ts b/helpers/channels/deleteStageInstance.ts deleted file mode 100644 index 39bc14fe6..000000000 --- a/helpers/channels/deleteStageInstance.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Deletes the Stage instance. Requires the user to be a moderator of the Stage channel. */ -export async function deleteStageInstance(bot: Bot, channelId: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.STAGE_INSTANCE(channelId)); -} diff --git a/helpers/channels/editChannel.ts b/helpers/channels/editChannel.ts index 86f3f8ec6..a45f48815 100644 --- a/helpers/channels/editChannel.ts +++ b/helpers/channels/editChannel.ts @@ -2,9 +2,38 @@ import type { Bot } from "../../bot.ts"; import { Channel } from "../../transformers/channel.ts"; import { DiscordChannel } from "../../types/discord.ts"; import { ChannelTypes, VideoQualityModes } from "../../types/shared.ts"; -import { OverwriteReadable } from "./editChannelOverwrite.ts"; +import { OverwriteReadable } from "./editChannelPermissionOverrides.ts"; -/** Update a channel's settings. Requires the `MANAGE_CHANNELS` permission for the guild. */ +/** + * Edits a channel's settings. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to edit. + * @param options - The parameters for the edit of the channel. + * @returns An instance of the edited {@link Channel}. + * + * @remarks + * If editing a channel of type {@link ChannelTypes.GroupDm}: + * - Fires a _Channel Update_ gateway event. + * + * If editing a thread channel: + * - Requires the `MANAGE_THREADS` permission __unless__ if setting the `archived` property to `false` when the `locked` property is also `false`, in which case only the `SEND_MESSAGES` permission is required. + * + * - Fires a _Thread Update_ gateway event. + * + * If editing a guild channel: + * - Requires the `MANAGE_CHANNELS` permission. + * + * - If modifying permission overrides: + * - Requires the `MANAGE_ROLES` permission. + * + * - Only permissions the bot user has in the guild or parent channel can be allowed/denied __unless__ the bot user has a `MANAGE_ROLES` permission override in the channel. + * + * - If modifying a channel of type {@link ChannelTypes.GuildCategory}: + * - Fires a _Channel Update_ gateway event for each child channel impacted in this change. + * - Otherwise: + * - Fires a _Channel Update_ gateway event. + */ export async function editChannel( bot: Bot, channelId: bigint, diff --git a/helpers/channels/editChannelOverwrite.ts b/helpers/channels/editChannelPermissionOverrides.ts similarity index 59% rename from helpers/channels/editChannelOverwrite.ts rename to helpers/channels/editChannelPermissionOverrides.ts index 341c07014..fd9e22e84 100644 --- a/helpers/channels/editChannelOverwrite.ts +++ b/helpers/channels/editChannelPermissionOverrides.ts @@ -1,8 +1,23 @@ import type { Bot } from "../../bot.ts"; import { OverwriteTypes, PermissionStrings } from "../../types/shared.ts"; -/** Edit the channel permission overwrites for a user or role in this channel. Requires `MANAGE_ROLES` permission. */ -export async function editChannelOverwrite( +/** + * Edits the permission overrides for a user or role in a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to edit the permission overrides of. + * @param overwrite - The permission override. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Only permissions the bot user has in the guild or parent channel can be allowed/denied __unless__ the bot user has a `MANAGE_ROLES` permission override in the channel. + * + * Fires a _Channel Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#edit-channel-permissions} + */ +export async function editChannelPermissionOverrides( bot: Bot, channelId: bigint, overwrite: OverwriteReadable, diff --git a/helpers/channels/swapChannels.ts b/helpers/channels/editChannelPositions.ts similarity index 61% rename from helpers/channels/swapChannels.ts rename to helpers/channels/editChannelPositions.ts index 606986f8a..5ebdbbdce 100644 --- a/helpers/channels/swapChannels.ts +++ b/helpers/channels/editChannelPositions.ts @@ -1,7 +1,22 @@ import type { Bot } from "../../bot.ts"; -/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permission. Only channels to be modified are required. */ -export async function swapChannels( +export const swapChannels = editChannelPositions; + +/** + * Edits the positions of a set of channels in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild in which to edit the positions of the channels. + * @param channelPositions - A set of objects defining the updated positions of the channels. + * + * @remarks + * Requires the `MANAGE_CHANNELS` permission. + * + * Fires a _Channel Update_ gateway event for every channel impacted in this change. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions} + */ +export async function editChannelPositions( bot: Bot, guildId: bigint, channelPositions: ModifyGuildChannelPositions[], diff --git a/helpers/channels/followChannel.ts b/helpers/channels/followChannel.ts deleted file mode 100644 index d4f21ab3c..000000000 --- a/helpers/channels/followChannel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { DiscordFollowedChannel } from "../../types/discord.ts"; - -/** Follow a News Channel to send messages to a target channel. Requires the `MANAGE_WEBHOOKS` permission in the target channel. Returns the webhook id. */ -export async function followChannel(bot: Bot, sourceChannelId: bigint, targetChannelId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "POST", - bot.constants.routes.CHANNEL_FOLLOW(sourceChannelId), - { - webhook_channel_id: targetChannelId, - }, - ); - - return bot.transformers.snowflake(result.webhook_id); -} diff --git a/helpers/channels/forums/createForumPost.ts b/helpers/channels/forums/createForumThread.ts similarity index 77% rename from helpers/channels/forums/createForumPost.ts rename to helpers/channels/forums/createForumThread.ts index 4d1710aad..14dd6cca3 100644 --- a/helpers/channels/forums/createForumPost.ts +++ b/helpers/channels/forums/createForumThread.ts @@ -4,8 +4,25 @@ import { Embed } from "../../../transformers/embed.ts"; import { DiscordChannel } from "../../../types/discord.ts"; import { AllowedMentions, FileContent, MessageComponents } from "../../../types/mod.ts"; -/** Creates a new public thread from an existing message. Returns a thread channel. */ -export async function createForumPost( +/** + * Creates a new thread in a forum channel, and sends a message within the created thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the forum channel to create the thread within. + * @param options - The parameters for the creation of the thread. + * @returns An instance of {@link Channel} with a nested {@link Message} object. + * + * @remarks + * Requires the `CREATE_MESSAGES` permission. + * + * Fires a _Thread Create_ gateway event. + * Fires a _Message Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel} + * + * @experimental + */ +export async function createForumThread( bot: Bot, channelId: bigint, options: CreateForumPostWithMessage, diff --git a/helpers/channels/forums/mod.ts b/helpers/channels/forums/mod.ts index a768a60f5..c1408122b 100644 --- a/helpers/channels/forums/mod.ts +++ b/helpers/channels/forums/mod.ts @@ -1 +1 @@ -export * from "./createForumPost.ts"; +export * from "./createForumThread.ts"; diff --git a/helpers/channels/getChannel.ts b/helpers/channels/getChannel.ts index c97f4490d..3a2b33e09 100644 --- a/helpers/channels/getChannel.ts +++ b/helpers/channels/getChannel.ts @@ -2,7 +2,18 @@ import type { Bot } from "../../bot.ts"; import { Channel } from "../../transformers/channel.ts"; import { DiscordChannel } from "../../types/discord.ts"; -/** Fetches a single channel object from the api. */ +/** + * Gets a channel by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get. + * @returns An instance of {@link Channel}. + * + * @remarks + * If the channel is a thread, a {@link ThreadMember} object is included in the result. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-channel} + */ export async function getChannel(bot: Bot, channelId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/invites/getChannelInvites.ts b/helpers/channels/getChannelInvites.ts similarity index 79% rename from helpers/invites/getChannelInvites.ts rename to helpers/channels/getChannelInvites.ts index 0b212704f..691edb14b 100644 --- a/helpers/invites/getChannelInvites.ts +++ b/helpers/channels/getChannelInvites.ts @@ -2,9 +2,22 @@ import type { Bot } from "../../bot.ts"; import { DiscordInviteMetadata } from "../../types/discord.ts"; import { TargetTypes } from "../../types/shared.ts"; import { Collection } from "../../util/collection.ts"; -import { InviteMetadata } from "./getInvite.ts"; +import { InviteMetadata } from "../guilds/invites/mod.ts"; -/** Gets the invites for this channel. Requires MANAGE_CHANNEL */ +/** + * Gets the list of invites for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get the invites of. + * @returns A collection of {@link InviteMetadata} objects assorted by invite code. + * + * @remarks + * Requires the `MANAGE_CHANNELS` permission. + * + * Only usable for guild channels. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-channel-invites} + */ export async function getChannelInvites(bot: Bot, channelId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/channels/getChannelWebhooks.ts b/helpers/channels/getChannelWebhooks.ts index 6fad113a7..394041de6 100644 --- a/helpers/channels/getChannelWebhooks.ts +++ b/helpers/channels/getChannelWebhooks.ts @@ -3,7 +3,18 @@ import { Webhook } from "../../transformers/webhook.ts"; import { DiscordWebhook } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; -/** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */ +/** + * Gets a list of webhooks for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel which to get the webhooks of. + * @returns A collection of {@link Webhook} objects assorted by webhook ID. + * + * @remarks + * Requires the `MANAGE_WEBHOOKS` permission. + * + * @see {@link https://discord.com/developers/docs/resources/webhook#get-channel-webhooks} + */ export async function getChannelWebhooks(bot: Bot, channelId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/channels/getChannels.ts b/helpers/channels/getChannels.ts index eec306919..8b5c2886e 100644 --- a/helpers/channels/getChannels.ts +++ b/helpers/channels/getChannels.ts @@ -3,7 +3,18 @@ import { Channel } from "../../transformers/channel.ts"; import { DiscordChannel } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; -/** Returns a list of guild channel objects. */ +/** + * Gets the list of channels for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the channels of. + * @returns A collection of {@link Channel} objects assorted by channel ID. + * + * @remarks + * Excludes threads. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-channels} + */ export async function getChannels(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/channels/getPins.ts b/helpers/channels/getPins.ts deleted file mode 100644 index fc93a9aad..000000000 --- a/helpers/channels/getPins.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { Message } from "../../transformers/message.ts"; -import { DiscordMessage } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; - -/** Get pinned messages in this channel. */ -export async function getPins(bot: Bot, channelId: bigint): Promise> { - const results = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.CHANNEL_PINS(channelId), - ); - - return new Collection( - results.map((result) => { - const message = bot.transformers.message(bot, result); - return [message.id, message]; - }), - ); -} diff --git a/helpers/channels/getStageInstance.ts b/helpers/channels/getStageInstance.ts deleted file mode 100644 index e96f10444..000000000 --- a/helpers/channels/getStageInstance.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { StageInstance } from "../../transformers/stageInstance.ts"; -import { DiscordStageInstance } from "../../types/discord.ts"; - -/** Gets the stage instance associated with the Stage channel, if it exists. */ -export async function getStageInstance(bot: Bot, channelId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.STAGE_INSTANCE(channelId), - ); - - return bot.transformers.stageInstance(bot, result); -} diff --git a/helpers/channels/mod.ts b/helpers/channels/mod.ts index 06019007e..6f5c56bee 100644 --- a/helpers/channels/mod.ts +++ b/helpers/channels/mod.ts @@ -1,20 +1,16 @@ -export * from "./threads/mod.ts"; +export * from "./announcements/mod.ts"; export * from "./forums/mod.ts"; +export * from "./stages/mod.ts"; +export * from "./threads/mod.ts"; export * from "./createChannel.ts"; -export * from "./createStageInstance.ts"; export * from "./deleteChannel.ts"; -export * from "./deleteChannelOverwrite.ts"; -export * from "./deleteStageInstance.ts"; +export * from "./deleteChannelPermissionOverride.ts"; export * from "./editChannel.ts"; -export * from "./editChannelOverwrite.ts"; -export * from "./followChannel.ts"; +export * from "./editChannelPermissionOverrides.ts"; export * from "./getChannel.ts"; +export * from "./getChannelInvites.ts"; export * from "./getChannels.ts"; export * from "./getChannelWebhooks.ts"; -export * from "./getPins.ts"; -export * from "./getStageInstance.ts"; -export * from "./startTyping.ts"; -export * from "./swapChannels.ts"; -export * from "./updateStageInstance.ts"; -export * from "./updateVoiceState.ts"; +export * from "./triggerTypingIndicator.ts"; +export * from "./editChannelPositions.ts"; diff --git a/helpers/channels/stages/createStageInstance.ts b/helpers/channels/stages/createStageInstance.ts new file mode 100644 index 000000000..d422777bc --- /dev/null +++ b/helpers/channels/stages/createStageInstance.ts @@ -0,0 +1,39 @@ +import type { Bot } from "../../../bot.ts"; +import { StageInstance } from "../../../transformers/stageInstance.ts"; +import { DiscordStageInstance } from "../../../types/discord.ts"; + +/** + * Creates a stage instance associated with a stage channel. + * + * @param bot - The bot instance to use to make the request. + * @param options - The parameters for the creation of the stage instance. + * @returns An instance of the created {@link StageInstance}. + * + * @remarks + * Requires the user to be a moderator of the stage channel. + * + * Fires a _Stage Instance Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/stage-instance#create-stage-instance} + */ +export async function createStageInstance(bot: Bot, options: CreateStageInstance): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "POST", + bot.constants.routes.STAGE_INSTANCES(), + { + channel_id: options.channelId.toString(), + topic: options.topic, + send_start_notification: options.sendStartNotification, + }, + ); + + return bot.transformers.stageInstance(bot, result); +} + +export interface CreateStageInstance { + channelId: bigint; + topic: string; + /** Notify @everyone that the stage instance has started. Requires the MENTION_EVERYONE permission. */ + sendStartNotification?: boolean; +} diff --git a/helpers/channels/stages/deleteStageInstance.ts b/helpers/channels/stages/deleteStageInstance.ts new file mode 100644 index 000000000..480ec087d --- /dev/null +++ b/helpers/channels/stages/deleteStageInstance.ts @@ -0,0 +1,18 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes the stage instance associated with a stage channel, if one exists. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the stage channel the stage instance is associated with. + * + * @remarks + * Requires the user to be a moderator of the stage channel. + * + * Fires a _Stage Instance Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance} + */ +export async function deleteStageInstance(bot: Bot, channelId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.STAGE_INSTANCE(channelId)); +} diff --git a/helpers/channels/stages/editStageInstance.ts b/helpers/channels/stages/editStageInstance.ts new file mode 100644 index 000000000..a2c855589 --- /dev/null +++ b/helpers/channels/stages/editStageInstance.ts @@ -0,0 +1,35 @@ +import type { Bot } from "../../../bot.ts"; +import { StageInstance } from "../../../transformers/stageInstance.ts"; +import { DiscordStageInstance } from "../../../types/discord.ts"; +import { AtLeastOne } from "../../../types/shared.ts"; + +/** + * Edits a stage instance. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the stage channel the stage instance is associated with. + * @returns An instance of the updated {@link StageInstance}. + * + * @remarks + * Requires the user to be a moderator of the stage channel. + * + * Fires a _Stage Instance Update_ event. + * + * @see {@link https://discord.com/developers/docs/resources/stage-instance#modify-stage-instance} + */ +export async function editStageInstance( + bot: Bot, + channelId: bigint, + data: AtLeastOne>, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.STAGE_INSTANCE(channelId), + { + topic: data.topic, + }, + ); + + return bot.transformers.stageInstance(bot, result); +} diff --git a/helpers/channels/stages/getStageInstance.ts b/helpers/channels/stages/getStageInstance.ts new file mode 100644 index 000000000..90f528d12 --- /dev/null +++ b/helpers/channels/stages/getStageInstance.ts @@ -0,0 +1,22 @@ +import type { Bot } from "../../../bot.ts"; +import { StageInstance } from "../../../transformers/stageInstance.ts"; +import { DiscordStageInstance } from "../../../types/discord.ts"; + +/** + * Gets the stage instance associated with a stage channel, if one exists. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the stage channel the stage instance is associated with. + * @returns An instance of {@link StageInstance}. + * + * @see {@link https://discord.com/developers/docs/resources/stage-instance#get-stage-instance} + */ +export async function getStageInstance(bot: Bot, channelId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.STAGE_INSTANCE(channelId), + ); + + return bot.transformers.stageInstance(bot, result); +} diff --git a/helpers/channels/stages/mod.ts b/helpers/channels/stages/mod.ts new file mode 100644 index 000000000..2fa6aee4d --- /dev/null +++ b/helpers/channels/stages/mod.ts @@ -0,0 +1,4 @@ +export * from "./createStageInstance.ts"; +export * from "./deleteStageInstance.ts"; +export * from "./editStageInstance.ts"; +export * from "./getStageInstance.ts"; diff --git a/helpers/channels/startTyping.ts b/helpers/channels/startTyping.ts deleted file mode 100644 index 963de045b..000000000 --- a/helpers/channels/startTyping.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** - * Trigger a typing indicator for the specified channel. Generally bots should **NOT** implement this route. - * However, if a bot is responding to a command and expects the computation to take a few seconds, - * this endpoint may be called to let the user know that the bot is processing their message. - */ -export async function startTyping(bot: Bot, channelId: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.CHANNEL_TYPING(channelId)); -} diff --git a/helpers/channels/threads/addThreadMember.ts b/helpers/channels/threads/addThreadMember.ts new file mode 100644 index 000000000..622803f08 --- /dev/null +++ b/helpers/channels/threads/addThreadMember.ts @@ -0,0 +1,20 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Adds a member to a thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to add the member to. + * @param userId - The user ID of the member to add to the thread. + * + * @remarks + * Requires the ability to send messages in the thread. + * Requires the thread not be archived. + * + * Fires a _Thread Members Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#add-thread-member} + */ +export async function addThreadMember(bot: Bot, channelId: bigint, userId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "PUT", bot.constants.routes.THREAD_USER(channelId, userId)); +} diff --git a/helpers/channels/threads/addToThread.ts b/helpers/channels/threads/addToThread.ts deleted file mode 100644 index 1f593373d..000000000 --- a/helpers/channels/threads/addToThread.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Bot } from "../../../bot.ts"; - -/** Adds a user to a thread. Requires the ability to send messages in the thread. Requires the thread is not archived. */ -export async function addToThread(bot: Bot, threadId: bigint, userId: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "PUT", bot.constants.routes.THREAD_USER(threadId, userId)); -} diff --git a/helpers/channels/threads/getActiveThreads.ts b/helpers/channels/threads/getActiveThreads.ts index f20379076..d2ca413f9 100644 --- a/helpers/channels/threads/getActiveThreads.ts +++ b/helpers/channels/threads/getActiveThreads.ts @@ -3,12 +3,20 @@ import { Channel, ThreadMember } from "../../../mod.ts"; import { DiscordListActiveThreads } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -export type ActiveThreads = { - threads: Collection; - members: Collection; -}; - -/** Returns all active threads in the guild, including public and private threads. Threads are ordered by their `id`, in descending order. */ +/** + * Gets the list of all active threads for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the threads of. + * @returns An instance of {@link ActiveThreads}. + * + * @remarks + * Returns both public and private threads. + * + * Threads are ordered by the `id` property in descending order. + * + * @see {@link https://discord.com/developers/docs/resources/guild#list-active-guild-threads} + */ export async function getActiveThreads(bot: Bot, guildId: bigint): Promise { const results = await bot.rest.runMethod( bot.rest, @@ -31,3 +39,8 @@ export async function getActiveThreads(bot: Bot, guildId: bigint): Promise; + members: Collection; +}; diff --git a/helpers/channels/threads/getPrivateArchivedThreads.ts b/helpers/channels/threads/getPrivateArchivedThreads.ts new file mode 100644 index 000000000..eb9b27185 --- /dev/null +++ b/helpers/channels/threads/getPrivateArchivedThreads.ts @@ -0,0 +1,50 @@ +import type { Bot } from "../../../bot.ts"; +import { DiscordListArchivedThreads } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; +import { ArchivedThreads, ListArchivedThreads } from "./getPublicArchivedThreads.ts"; + +/** + * Gets the list of private archived threads for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get the archived threads for. + * @param options - The parameters for the fetching of threads. + * @returns An instance of {@link ArchivedThreads}. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * Requires the `MANAGE_THREADS` permission. + * + * Returns threads of type {@link ChannelTypes.GuildPrivateThread}. + * + * Threads are ordered by the `archive_timestamp` property included in the metadata of the object in descending order. + * + * @see {@link https://discord.com/developers/docs/resources/channel#list-private-archived-threads} + */ +export async function getPrivateArchivedThreads( + bot: Bot, + channelId: bigint, + options?: ListArchivedThreads, +): Promise { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.THREAD_ARCHIVED_PRIVATE(channelId, options), + ); + + return { + threads: new Collection( + results.threads.map((result) => { + const thread = bot.transformers.channel(bot, { channel: result }); + return [thread.id, thread]; + }), + ), + members: new Collection( + results.members.map((result) => { + const member = bot.transformers.threadMember(bot, result); + return [member.id!, member]; + }), + ), + hasMore: results.has_more, + }; +} diff --git a/helpers/channels/threads/getPrivateJoinedArchivedThreads.ts b/helpers/channels/threads/getPrivateJoinedArchivedThreads.ts new file mode 100644 index 000000000..13ed4b3e7 --- /dev/null +++ b/helpers/channels/threads/getPrivateJoinedArchivedThreads.ts @@ -0,0 +1,49 @@ +import type { Bot } from "../../../bot.ts"; +import { DiscordListArchivedThreads } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; +import { ArchivedThreads, ListArchivedThreads } from "./getPublicArchivedThreads.ts"; + +/** + * Gets the list of private archived threads the bot is a member of for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get the archived threads for. + * @param options - The parameters for the fetching of threads. + * @returns An instance of {@link ArchivedThreads}. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * Returns threads of type {@link ChannelTypes.GuildPrivateThread}. + * + * Threads are ordered by the `id` property in descending order. + * + * @see {@link https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads} + */ +export async function getPrivateJoinedArchivedThreads( + bot: Bot, + channelId: bigint, + options?: ListArchivedThreads, +): Promise { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.THREAD_ARCHIVED_PRIVATE_JOINED(channelId, options), + ); + + return { + threads: new Collection( + results.threads.map((result) => { + const thread = bot.transformers.channel(bot, { channel: result }); + return [thread.id, thread]; + }), + ), + members: new Collection( + results.members.map((result) => { + const member = bot.transformers.threadMember(bot, result); + return [member.id!, member]; + }), + ), + hasMore: results.has_more, + }; +} diff --git a/helpers/channels/threads/getArchivedThreads.ts b/helpers/channels/threads/getPublicArchivedThreads.ts similarity index 53% rename from helpers/channels/threads/getArchivedThreads.ts rename to helpers/channels/threads/getPublicArchivedThreads.ts index d62cf24f4..02d826846 100644 --- a/helpers/channels/threads/getArchivedThreads.ts +++ b/helpers/channels/threads/getPublicArchivedThreads.ts @@ -3,28 +3,33 @@ import { DiscordListArchivedThreads } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; import { ActiveThreads } from "./getActiveThreads.ts"; -export type ArchivedThreads = ActiveThreads & { - hasMore: boolean; -}; - -/** Get the archived threads for this channel, defaults to public */ -export async function getArchivedThreads( +/** + * Gets the list of public archived threads for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get the archived threads for. + * @param options - The parameters for the fetching of threads. + * @returns An instance of {@link ArchivedThreads}. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * If called on a channel of type {@link ChannelTypes.GuildText}, returns threads of type {@link ChannelTypes.GuildPublicThread}. + * If called on a channel of type {@link ChannelTypes.GuildNews}, returns threads of type {@link ChannelTypes.GuildNewsThread}. + * + * Threads are ordered by the `archive_timestamp` property included in the metadata of the object in descending order. + * + * @see {@link https://discord.com/developers/docs/resources/channel#list-public-archived-threads} + */ +export async function getPublicArchivedThreads( bot: Bot, channelId: bigint, - options?: ListArchivedThreads & { - type?: "public" | "private" | "privateJoinedThreads"; - }, + options?: ListArchivedThreads, ): Promise { - const url = options?.type === "privateJoinedThreads" - ? bot.constants.routes.THREAD_ARCHIVED_PRIVATE_JOINED(channelId, options) - : options?.type === "private" - ? bot.constants.routes.THREAD_ARCHIVED_PRIVATE(channelId, options) - : bot.constants.routes.THREAD_ARCHIVED_PUBLIC(channelId, options); - const results = await bot.rest.runMethod( bot.rest, "GET", - url, + bot.constants.routes.THREAD_ARCHIVED_PUBLIC(channelId, options), ); return { @@ -51,3 +56,7 @@ export interface ListArchivedThreads { /** Optional maximum number of threads to return */ limit?: number; } + +export type ArchivedThreads = ActiveThreads & { + hasMore: boolean; +}; diff --git a/helpers/channels/threads/getThreadMember.ts b/helpers/channels/threads/getThreadMember.ts index 8c41eca7e..d8315b4da 100644 --- a/helpers/channels/threads/getThreadMember.ts +++ b/helpers/channels/threads/getThreadMember.ts @@ -2,12 +2,21 @@ import type { Bot } from "../../../bot.ts"; import { ThreadMember } from "../../../transformers/threadMember.ts"; import { DiscordThreadMember } from "../../../types/discord.ts"; -/** Returns thread members objects that are members of the thread. */ -export async function getThreadMember(bot: Bot, threadId: bigint, userId: bigint): Promise { +/** + * Gets a thread member by their user ID. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to get the thread member of. + * @param userId - The user ID of the thread member to get. + * @returns An instance of {@link ThreadMember}. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-thread-member} + */ +export async function getThreadMember(bot: Bot, channelId: bigint, userId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, "GET", - bot.constants.routes.THREAD_USER(threadId, userId), + bot.constants.routes.THREAD_USER(channelId, userId), ); return bot.transformers.threadMember(bot, result); diff --git a/helpers/channels/threads/getThreadMembers.ts b/helpers/channels/threads/getThreadMembers.ts index 2f07993cc..b6bcfd0ac 100644 --- a/helpers/channels/threads/getThreadMembers.ts +++ b/helpers/channels/threads/getThreadMembers.ts @@ -3,12 +3,23 @@ import { ThreadMember } from "../../../transformers/threadMember.ts"; import { DiscordThreadMember } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -/** Returns thread members objects that are members of the thread. */ -export async function getThreadMembers(bot: Bot, threadId: bigint): Promise> { +/** + * Gets the list of thread members for a thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to get the thread members of. + * @returns A collection of {@link ThreadMember} assorted by user ID. + * + * @remarks + * Requires the application to have the `GUILD_MEMBERS` privileged intent enabled. + * + * @see {@link https://discord.com/developers/docs/resources/channel#list-thread-members} + */ +export async function getThreadMembers(bot: Bot, channelId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, "GET", - bot.constants.routes.THREAD_MEMBERS(threadId), + bot.constants.routes.THREAD_MEMBERS(channelId), ); return new Collection( diff --git a/helpers/channels/threads/joinThread.ts b/helpers/channels/threads/joinThread.ts index 3b4053d9c..b30fd2bc2 100644 --- a/helpers/channels/threads/joinThread.ts +++ b/helpers/channels/threads/joinThread.ts @@ -1,6 +1,18 @@ import type { Bot } from "../../../bot.ts"; -/** Adds the bot to the thread. Cannot join an archived thread. */ -export async function joinThread(bot: Bot, threadId: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "PUT", bot.constants.routes.THREAD_ME(threadId)); +/** + * Adds the bot user to a thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to add the bot user to. + * + * @remarks + * Requires the thread not be archived. + * + * Fires a _Thread Members Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#join-thread} + */ +export async function joinThread(bot: Bot, channelId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "PUT", bot.constants.routes.THREAD_ME(channelId)); } diff --git a/helpers/channels/threads/leaveThread.ts b/helpers/channels/threads/leaveThread.ts index 4c506618e..272e933a8 100644 --- a/helpers/channels/threads/leaveThread.ts +++ b/helpers/channels/threads/leaveThread.ts @@ -1,6 +1,18 @@ import type { Bot } from "../../../bot.ts"; -/** Removes the bot from a thread. Requires the thread is not archived. */ -export async function leaveThread(bot: Bot, threadId: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.THREAD_ME(threadId)); +/** + * Removes the bot user from a thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to remove the bot user from. + * + * @remarks + * Requires the thread not be archived. + * + * Fires a _Thread Members Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#leave-thread} + */ +export async function leaveThread(bot: Bot, channelId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.THREAD_ME(channelId)); } diff --git a/helpers/channels/threads/mod.ts b/helpers/channels/threads/mod.ts index 91883f161..dcbaf9fe5 100644 --- a/helpers/channels/threads/mod.ts +++ b/helpers/channels/threads/mod.ts @@ -1,6 +1,8 @@ -export * from "./addToThread.ts"; +export * from "./addThreadMember.ts"; export * from "./getActiveThreads.ts"; -export * from "./getArchivedThreads.ts"; +export * from "./getPrivateArchivedThreads.ts"; +export * from "./getPrivateJoinedArchivedThreads.ts"; +export * from "./getPublicArchivedThreads.ts"; export * from "./getThreadMember.ts"; export * from "./getThreadMembers.ts"; export * from "./joinThread.ts"; diff --git a/helpers/channels/threads/removeThreadMember.ts b/helpers/channels/threads/removeThreadMember.ts index 0afa3e894..a82aeb5e7 100644 --- a/helpers/channels/threads/removeThreadMember.ts +++ b/helpers/channels/threads/removeThreadMember.ts @@ -1,10 +1,26 @@ import type { Bot } from "../../../bot.ts"; -/** Removes a user from a thread. Requires the MANAGE_THREADS permission or that you are the creator of the thread. Also requires the thread is not archived. */ -export async function removeThreadMember(bot: Bot, threadId: bigint, userId: bigint): Promise { +/** + * Removes a member from a thread. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the thread to remove the thread member of. + * @param userId - The user ID of the thread member to remove. + * + * @remarks + * If the thread is of type {@link ChannelTypes.GuildPrivateThread}, requires to be the creator of the thread. + * Otherwise, requires the `MANAGE_THREADS` permission. + * + * Requires the thread not be archived. + * + * Fires a _Thread Members Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#remove-thread-member} + */ +export async function removeThreadMember(bot: Bot, channelId: bigint, userId: bigint): Promise { return await bot.rest.runMethod( bot.rest, "DELETE", - bot.constants.routes.THREAD_USER(threadId, userId), + bot.constants.routes.THREAD_USER(channelId, userId), ); } diff --git a/helpers/channels/threads/startThreadWithMessage.ts b/helpers/channels/threads/startThreadWithMessage.ts index 1b29b921e..93cae4472 100644 --- a/helpers/channels/threads/startThreadWithMessage.ts +++ b/helpers/channels/threads/startThreadWithMessage.ts @@ -2,7 +2,26 @@ import type { Bot } from "../../../bot.ts"; import { Channel } from "../../../transformers/channel.ts"; import { DiscordChannel } from "../../../types/discord.ts"; -/** Creates a new public thread from an existing message. Returns a thread channel. */ +/** + * Creates a thread, using an existing message as its point of origin. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel in which to create the thread. + * @param messageId - The ID of the message to use as the thread's point of origin. + * @param options - The parameters to use for the creation of the thread. + * @returns An instance of the created {@link Channel | Thread}. + * + * @remarks + * If called on a channel of type {@link ChannelTypes.GuildText}, creates a {@link ChannelTypes.GuildPublicThread}. + * If called on a channel of type {@link ChannelTypes.GuildNews}, creates a {@link ChannelTypes.GuildNewsThread}. + * Does not work on channels of type {@link ChannelTypes.GuildForum}. + * + * The ID of the created thread will be the same as the ID of the source message. + * + * Fires a _Thread Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#start-thread-from-message} + */ export async function startThreadWithMessage( bot: Bot, channelId: bigint, diff --git a/helpers/channels/threads/startThreadWithoutMessage.ts b/helpers/channels/threads/startThreadWithoutMessage.ts index 6decdc4d1..2e611c018 100644 --- a/helpers/channels/threads/startThreadWithoutMessage.ts +++ b/helpers/channels/threads/startThreadWithoutMessage.ts @@ -3,7 +3,21 @@ import { Channel } from "../../../transformers/channel.ts"; import { DiscordChannel } from "../../../types/discord.ts"; import { ChannelTypes } from "../../../types/shared.ts"; -/** Creates a new private thread. Returns a thread channel. */ +/** + * Creates a thread without using a message as the thread's point of origin. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel in which to create the thread. + * @param options - The parameters to use for the creation of the thread. + * @returns An instance of the created {@link Channel | Thread}. + * + * @remarks + * Creating a private thread requires the server to be boosted. + * + * Fires a _Thread Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#start-thread-without-message} + */ export async function startThreadWithoutMessage( bot: Bot, channelId: bigint, diff --git a/helpers/channels/triggerTypingIndicator.ts b/helpers/channels/triggerTypingIndicator.ts new file mode 100644 index 000000000..a02d25608 --- /dev/null +++ b/helpers/channels/triggerTypingIndicator.ts @@ -0,0 +1,20 @@ +import type { Bot } from "../../bot.ts"; + +export const startTyping = triggerTypingIndicator; + +/** + * Triggers a typing indicator for the bot user. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel in which to trigger the typing indicator. + * + * @remarks + * Generally, bots should _not_ use this route. + * + * Fires a _Typing Start_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#trigger-typing-indicator} + */ +export async function triggerTypingIndicator(bot: Bot, channelId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.CHANNEL_TYPING(channelId)); +} diff --git a/helpers/channels/updateStageInstance.ts b/helpers/channels/updateStageInstance.ts deleted file mode 100644 index 9621ed2fd..000000000 --- a/helpers/channels/updateStageInstance.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { StageInstance } from "../../transformers/stageInstance.ts"; -import { DiscordStageInstance } from "../../types/discord.ts"; -import { AtLeastOne } from "../../types/shared.ts"; - -/** Updates fields of an existing Stage instance. Requires the user to be a moderator of the Stage channel. */ -export async function updateStageInstance( - bot: Bot, - channelId: bigint, - data: AtLeastOne>, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "PATCH", - bot.constants.routes.STAGE_INSTANCE(channelId), - { - topic: data.topic, - }, - ); - - return bot.transformers.stageInstance(bot, result); -} diff --git a/helpers/channels/updateVoiceState.ts b/helpers/channels/updateVoiceState.ts deleted file mode 100644 index 261f29263..000000000 --- a/helpers/channels/updateVoiceState.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** - * Updates the bot's voice state - * Caveats: - * - `channel_id` must currently point to a stage channel. - * - Bot must already have joined `channel_id`. - * - You must have the `MUTE_MEMBERS` permission. But can always suppress yourself. - * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. - * - You are able to set `request_to_speak_timestamp` to any present or future time. - * - When suppressed, the user will have their `request_to_speak_timestamp` removed. - */ -export async function updateBotVoiceState(bot: Bot, guildId: bigint, options: UpdateSelfVoiceState): Promise { - return await bot.rest.runMethod(bot.rest, "PATCH", bot.constants.routes.UPDATE_VOICE_STATE(guildId), { - channel_id: options.channelId, - suppress: options.suppress, - request_to_speak_timestamp: options.requestToSpeakTimestamp - ? new Date(options.requestToSpeakTimestamp).toISOString() - : options.requestToSpeakTimestamp, - }); -} - -/** - * Updates the a user's voice state - * Caveats: - * - `channel_id` must currently point to a stage channel. - * - User must already have joined `channel_id`. - * - You must have the `MUTE_MEMBERS` permission. But can always suppress yourself. - * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. - * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. - * - You are able to set `request_to_speak_timestamp` to any present or future time. - * - When suppressed, the user will have their `request_to_speak_timestamp` removed. - */ -export async function updateUserVoiceState(bot: Bot, guildId: bigint, options: UpdateOthersVoiceState): Promise { - return await bot.rest.runMethod( - bot.rest, - "PATCH", - bot.constants.routes.UPDATE_VOICE_STATE(guildId, options.userId), - { - channel_id: options.channelId, - suppress: options.suppress, - user_id: options.userId, - }, - ); -} - -/** https://discord.com/developers/docs/resources/guild#update-current-user-voice-state */ -export interface UpdateSelfVoiceState { - /** The id of the channel the user is currently in */ - channelId: bigint; - /** Toggles the user's suppress state */ - suppress?: boolean; - /** Sets the user's request to speak */ - requestToSpeakTimestamp?: number | null; -} - -/** https://discord.com/developers/docs/resources/guild#update-user-voice-state */ -export interface UpdateOthersVoiceState { - /** The id of the channel the user is currently in */ - channelId: bigint; - /** Toggles the user's suppress state */ - suppress?: boolean; - /** The user id to target */ - userId: bigint; -} diff --git a/helpers/discovery/addDiscoverySubcategory.ts b/helpers/discovery/addDiscoverySubcategory.ts index bfbcbfa6d..8f5689fab 100644 --- a/helpers/discovery/addDiscoverySubcategory.ts +++ b/helpers/discovery/addDiscoverySubcategory.ts @@ -1,6 +1,18 @@ import type { Bot } from "../../bot.ts"; -/** Add a discovery subcategory to the guild. Requires the `MANAGE_GUILD` permission. */ +/** + * Adds a discovery subcategory to a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to add the subcategory to. + * @param categoryId - The ID of the category to add to the guild. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function addDiscoverySubcategory(bot: Bot, guildId: bigint, categoryId: number): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/discovery/deleteDiscoverySubcategory.ts b/helpers/discovery/deleteDiscoverySubcategory.ts index cfa6c6153..5e3a96b5c 100644 --- a/helpers/discovery/deleteDiscoverySubcategory.ts +++ b/helpers/discovery/deleteDiscoverySubcategory.ts @@ -1,6 +1,18 @@ import type { Bot } from "../../bot.ts"; -/** Deletes a discovery subcategory from the guild. Requires the MANAGE_GUILD permission. Returns a 204 No Content on success. */ +/** + * Deletes a discovery subcategory from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete the subcategory from. + * @param categoryId - The ID of the category to delete from the guild. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function deleteDiscoverySubcategory(bot: Bot, guildId: bigint, categoryId: number): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/discovery/editDiscovery.ts b/helpers/discovery/editDiscovery.ts index fb1c6942c..42b210964 100644 --- a/helpers/discovery/editDiscovery.ts +++ b/helpers/discovery/editDiscovery.ts @@ -2,20 +2,32 @@ import type { Bot } from "../../bot.ts"; import { DiscordDiscoveryMetadata } from "../../types/discord.ts"; import { DiscoveryMetadata } from "./getDiscovery.ts"; -/** Modify the discovery metadata for the guild. Requires the MANAGE_GUILD permission. Returns the updated discovery metadata object on success. */ +/** + * Edits the discovery settings of a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the discovery settings of. + * @param options - The parameters for the edit of the discovery settings. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function editDiscovery( bot: Bot, guildId: bigint, - data: ModifyGuildDiscoveryMetadata, + options: ModifyGuildDiscoveryMetadata, ): Promise { const result = await bot.rest.runMethod( bot.rest, "PATCH", bot.constants.routes.DISCOVERY_METADATA(guildId), { - primary_category_id: data.primaryCategoryId, - keywords: data.keywords, - emoji_discoverability_enabled: data.emojiDiscoverabilityEnabled, + primary_category_id: options.primaryCategoryId, + keywords: options.keywords, + emoji_discoverability_enabled: options.emojiDiscoverabilityEnabled, }, ); diff --git a/helpers/discovery/getDiscovery.ts b/helpers/discovery/getDiscovery.ts index 868aa5017..cc2d60fb8 100644 --- a/helpers/discovery/getDiscovery.ts +++ b/helpers/discovery/getDiscovery.ts @@ -11,7 +11,15 @@ export type DiscoveryMetadata = { categoryIds: number[]; }; -/** Returns the discovery metadata object for the guild. Requires the `MANAGE_GUILD` permission. */ +/** + * Gets the discovery settings of a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the discovery settings of. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function getDiscovery(bot: Bot, guildId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/discovery/getDiscoveryCategories.ts b/helpers/discovery/getDiscoveryCategories.ts index 3f359edf7..99780388d 100644 --- a/helpers/discovery/getDiscoveryCategories.ts +++ b/helpers/discovery/getDiscoveryCategories.ts @@ -13,7 +13,14 @@ export type DiscoveryName = { localizations?: Record; }; -/** Returns a Collection (mapped by Id of the discovery category object) of discovery category objects that can be used when editing guilds */ +/** + * Gets the list of available discovery categories. + * + * @param bot - The bot instance to use to make the request. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function getDiscoveryCategories(bot: Bot): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/discovery/getIsValidDiscoveryTerm.ts b/helpers/discovery/getIsValidDiscoveryTerm.ts index 260107295..3f908bf74 100644 --- a/helpers/discovery/getIsValidDiscoveryTerm.ts +++ b/helpers/discovery/getIsValidDiscoveryTerm.ts @@ -1,6 +1,15 @@ import type { Bot } from "../../bot.ts"; import { DiscordValidateDiscoverySearchTerm } from "../../types/discord.ts"; +/** + * Gets the validity of a discovery term. + * + * @param bot - The bot instance to use to make the request. + * @param term - The term to validate. + * + * @privateRemarks + * This endpoint is not formally documented. + */ export async function getIsValidDiscoveryTerm(bot: Bot, term: string): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/emojis/createEmoji.ts b/helpers/emojis/createEmoji.ts index 46ab09787..751330a66 100644 --- a/helpers/emojis/createEmoji.ts +++ b/helpers/emojis/createEmoji.ts @@ -2,7 +2,23 @@ import type { Bot } from "../../bot.ts"; import { Emoji } from "../../transformers/emoji.ts"; import { DiscordEmoji } from "../../types/discord.ts"; -/** Create an emoji in the server */ +/** + * Creates an emoji in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild in which to create the emoji. + * @param options - The parameters for the creation of the emoji. + * @returns An instance of the created {@link Emoji}. + * + * @remarks + * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * + * Emojis have a maximum file size of 256 kilobits. Attempting to upload a larger emoji will cause the route to return 400 Bad Request. + * + * Fires a _Guild Emojis Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/emoji#create-guild-emoji} + */ export async function createEmoji(bot: Bot, guildId: bigint, options: CreateGuildEmoji): Promise { if (options.image && !options.image.startsWith("data:image/")) { options.image = await bot.utils.urlToBase64(options.image); diff --git a/helpers/emojis/deleteEmoji.ts b/helpers/emojis/deleteEmoji.ts index 617952bcf..2094d7c5f 100644 --- a/helpers/emojis/deleteEmoji.ts +++ b/helpers/emojis/deleteEmoji.ts @@ -1,6 +1,19 @@ import type { Bot } from "../../bot.ts"; -/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ +/** + * Deletes an emoji from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild from which to delete the emoji. + * @param id - The ID of the emoji to delete. + * + * @remarks + * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * + * Fires a _Guild Emojis Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/emoji#delete-guild-emoji} + */ export async function deleteEmoji(bot: Bot, guildId: bigint, id: bigint, reason?: string): Promise { return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_EMOJI(guildId, id), { reason, diff --git a/helpers/emojis/editEmoji.ts b/helpers/emojis/editEmoji.ts index 7fe5b5d7f..05697ba93 100644 --- a/helpers/emojis/editEmoji.ts +++ b/helpers/emojis/editEmoji.ts @@ -2,7 +2,22 @@ import type { Bot } from "../../bot.ts"; import { Emoji } from "../../transformers/emoji.ts"; import { DiscordEmoji } from "../../types/discord.ts"; -/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ +/** + * Edits an emoji. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild in which to edit the emoji. + * @param id - The ID of the emoji to edit. + * @param options - The parameters for the edit of the emoji. + * @returns An instance of the updated {@link Emoji}. + * + * @remarks + * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * + * Fires a `Guild Emojis Update` gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/emoji#modify-guild-emoji} + */ export async function editEmoji(bot: Bot, guildId: bigint, id: bigint, options: ModifyGuildEmoji): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/emojis/getEmoji.ts b/helpers/emojis/getEmoji.ts index d40fbc705..2058e3f08 100644 --- a/helpers/emojis/getEmoji.ts +++ b/helpers/emojis/getEmoji.ts @@ -3,7 +3,14 @@ import { Emoji } from "../../transformers/emoji.ts"; import { DiscordEmoji } from "../../types/discord.ts"; /** - * Returns an emoji for the given guild and emoji Id. + * Gets an emoji by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild from which to get the emoji. + * @param emojiId - The ID of the emoji to get. + * @returns An instance of {@link Emoji}. + * + * @see {@link https://discord.com/developers/docs/resources/emoji#get-guild-emoji} */ export async function getEmoji(bot: Bot, guildId: bigint, emojiId: bigint): Promise { const result = await bot.rest.runMethod( diff --git a/helpers/emojis/getEmojiUrl.ts b/helpers/emojis/getEmojiUrl.ts index 5c40f6c45..698cd7e51 100644 --- a/helpers/emojis/getEmojiUrl.ts +++ b/helpers/emojis/getEmojiUrl.ts @@ -1,6 +1,12 @@ import { Bot } from "../../bot.ts"; -/** Creates a url to the emoji from the Discord CDN. */ -export function getEmojiURL(_bot: Bot, id: bigint, animated = false): string { - return `https://cdn.discordapp.com/emojis/${id}.${animated ? "gif" : "png"}`; +/** + * Builds a URL to an emoji in the Discord CDN. + * + * @param emojiId - The ID of the emoji to access. + * @param animated - Whether the emoji is animated or static. + * @returns The link to the resource. + */ +export function getEmojiURL(_bot: Bot, emojiId: bigint, animated = false): string { + return `https://cdn.discordapp.com/emojis/${emojiId}.${animated ? "gif" : "png"}`; } diff --git a/helpers/emojis/getEmojis.ts b/helpers/emojis/getEmojis.ts index 518072c42..a6b630ddc 100644 --- a/helpers/emojis/getEmojis.ts +++ b/helpers/emojis/getEmojis.ts @@ -4,7 +4,13 @@ import { DiscordEmoji } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; /** - * Returns a list of emojis for the given guild. + * Gets the list of emojis for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild which to get the emojis of. + * @returns A collection of {@link Emoji} objects assorted by emoji ID. + * + * @see {@link https://discord.com/developers/docs/resources/emoji#list-guild-emojis} */ export async function getEmojis(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod( diff --git a/helpers/guilds/automod/createAutomodRule.ts b/helpers/guilds/automod/createAutomodRule.ts index c40413cca..387abf860 100644 --- a/helpers/guilds/automod/createAutomodRule.ts +++ b/helpers/guilds/automod/createAutomodRule.ts @@ -8,7 +8,21 @@ import { DiscordAutoModerationRuleTriggerMetadataPresets, } from "../../../types/discord.ts"; -/** Get a rule currently configured for guild. */ +/** + * Creates an automod rule in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to create the rule in. + * @param options - The parameters for the creation of the rule. + * @returns An instance of the created {@link AutoModerationRule}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Fires an _Auto Moderation Rule Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#create-auto-moderation-rule} + */ export async function createAutomodRule( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/automod/deleteAutomodRule.ts b/helpers/guilds/automod/deleteAutomodRule.ts index fbd2db336..100deda37 100644 --- a/helpers/guilds/automod/deleteAutomodRule.ts +++ b/helpers/guilds/automod/deleteAutomodRule.ts @@ -1,6 +1,19 @@ import { Bot } from "../../../bot.ts"; -/** Delete a rule currently configured for guild. */ +/** + * Deletes an automod rule. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete the rule from. + * @param ruleId - The ID of the automod rule to delete. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Fires an _Auto Moderation Rule Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#delete-auto-moderation-rule} + */ export async function deleteAutomodRule(bot: Bot, guildId: bigint, ruleId: bigint, reason?: string): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/automod/editAutomodRule.ts b/helpers/guilds/automod/editAutomodRule.ts index 04563b002..35bcdc87b 100644 --- a/helpers/guilds/automod/editAutomodRule.ts +++ b/helpers/guilds/automod/editAutomodRule.ts @@ -7,16 +7,32 @@ import { DiscordAutoModerationRuleTriggerMetadataPresets, } from "../../../types/discord.ts"; -/** Edit a rule currently configured for guild. */ +/** + * Edits an automod rule. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the rule in. + * @param ruleId - The ID of the rule to edit. + * @param options - The parameters for the edit of the rule. + * @returns An instance of the edited {@link AutoModerationRule}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Fires an _Auto Moderation Rule Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#modify-auto-moderation-rule} + */ export async function editAutomodRule( bot: Bot, guildId: bigint, + ruleId: bigint, options: Partial, ): Promise { const result = await bot.rest.runMethod( bot.rest, "PATCH", - bot.constants.routes.AUTOMOD_RULES(guildId), + bot.constants.routes.AUTOMOD_RULE(guildId, ruleId), { name: options.name, event_type: options.eventType, diff --git a/helpers/guilds/automod/getAutomodRule.ts b/helpers/guilds/automod/getAutomodRule.ts index e1f2e9749..41169f802 100644 --- a/helpers/guilds/automod/getAutomodRule.ts +++ b/helpers/guilds/automod/getAutomodRule.ts @@ -2,7 +2,19 @@ import { Bot } from "../../../bot.ts"; import { AutoModerationRule } from "../../../transformers/automodRule.ts"; import { DiscordAutoModerationRule } from "../../../types/discord.ts"; -/** Get a rule currently configured for guild. */ +/** + * Gets an automod rule by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the rule of. + * @param ruleId - The ID of the rule to get. + * @returns An instance of {@link AutoModerationRule}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#get-auto-moderation-rule} + */ export async function getAutomodRule(bot: Bot, guildId: bigint, ruleId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/automod/getAutomodRules.ts b/helpers/guilds/automod/getAutomodRules.ts index fcbbcf12f..fb9ccf226 100644 --- a/helpers/guilds/automod/getAutomodRules.ts +++ b/helpers/guilds/automod/getAutomodRules.ts @@ -3,7 +3,18 @@ import { AutoModerationRule } from "../../../transformers/automodRule.ts"; import { DiscordAutoModerationRule } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -/** Get a list of all rules currently configured for guild. */ +/** + * Gets the list of automod rules for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the rules from. + * @returns A collection of {@link AutoModerationRule} objects assorted by rule ID. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#list-auto-moderation-rules-for-guild} + */ export async function getAutomodRules(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/createGuild.ts b/helpers/guilds/createGuild.ts index cabf641f1..a1e1b4f64 100644 --- a/helpers/guilds/createGuild.ts +++ b/helpers/guilds/createGuild.ts @@ -10,7 +10,20 @@ import { VerificationLevels, } from "../../types/shared.ts"; -/** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */ +/** + * Creates a guild. + * + * @param bot - The bot instance to use to make the request. + * @param options - The parameters for the creation of the guild. + * @returns An instance of the created {@link Guild}. + * + * @remarks + * ⚠️ This route can only be used by bots in __fewer than 10 guilds__. + * + * Fires a _Guild Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#create-guild} + */ export async function createGuild(bot: Bot, options: CreateGuild): Promise { const result = await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.GUILDS(), { name: options.name, diff --git a/helpers/guilds/deleteGuild.ts b/helpers/guilds/deleteGuild.ts index baad7d28d..a41cc0cfd 100644 --- a/helpers/guilds/deleteGuild.ts +++ b/helpers/guilds/deleteGuild.ts @@ -1,6 +1,18 @@ import type { Bot } from "../../bot.ts"; -/** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event. */ +/** + * Deletes a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete. + * + * @remarks + * The bot user must be the owner of the guild. + * + * Fires a _Guild Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#delete-guild} + */ export async function deleteGuild(bot: Bot, guildId: bigint): Promise { return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD(guildId)); } diff --git a/helpers/guilds/editGuild.ts b/helpers/guilds/editGuild.ts index a699170af..b68e8e362 100644 --- a/helpers/guilds/editGuild.ts +++ b/helpers/guilds/editGuild.ts @@ -9,7 +9,27 @@ import { VerificationLevels, } from "../../types/shared.ts"; -/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ +// TODO: Put the `shardId` parameter before `options`. + +/** + * Edits a guild's settings. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit. + * @param shardId - The ID of the shard the guild is in. + * @param options - The parameters for the edit of the guild. + * @returns An instance of the edited {@link Guild}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * If attempting to add or remove the {@link GuildFeatures.Community} feature: + * - Requires the `ADMINISTRATOR` permission. + * + * Fires a _Guild Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild} + */ export async function editGuild(bot: Bot, guildId: bigint, options: ModifyGuild, shardId: number): Promise { if (options.icon && !options.icon.startsWith("data:image/")) { options.icon = await bot.utils.urlToBase64(options.icon); diff --git a/helpers/guilds/editWelcomeScreen.ts b/helpers/guilds/editWelcomeScreen.ts index 4d4e15bcb..33bb923d6 100644 --- a/helpers/guilds/editWelcomeScreen.ts +++ b/helpers/guilds/editWelcomeScreen.ts @@ -2,6 +2,21 @@ import type { Bot } from "../../bot.ts"; import { WelcomeScreen } from "../../transformers/welcomeScreen.ts"; import { DiscordWelcomeScreen } from "../../types/discord.ts"; +/** + * Edits a guild's welcome screen. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the welcome screen of. + * @param options - The parameters for the edit of the welcome screen. + * @returns An instance of the edited {@link WelcomeScreen}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Fires a _Guild Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen} + */ export async function editWelcomeScreen( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/editWidget.ts b/helpers/guilds/editWidget.ts deleted file mode 100644 index 5f7725f46..000000000 --- a/helpers/guilds/editWidget.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { GuildWidgetSettings } from "../../transformers/widgetSettings.ts"; -import { DiscordGuildWidgetSettings } from "../../types/discord.ts"; - -/** Modify a guild widget object for the guild. Requires the MANAGE_GUILD permission. */ -export async function editWidget( - bot: Bot, - guildId: bigint, - enabled: boolean, - channelId?: string | null, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "PATCH", - bot.constants.routes.GUILD_WIDGET(guildId), - { - enabled, - channel_id: channelId, - }, - ); - - return bot.transformers.widgetSettings(bot, result); -} diff --git a/helpers/guilds/scheduledEvents/createScheduledEvent.ts b/helpers/guilds/events/createScheduledEvent.ts similarity index 81% rename from helpers/guilds/scheduledEvents/createScheduledEvent.ts rename to helpers/guilds/events/createScheduledEvent.ts index b445003ca..10b444ff8 100644 --- a/helpers/guilds/scheduledEvents/createScheduledEvent.ts +++ b/helpers/guilds/events/createScheduledEvent.ts @@ -3,7 +3,23 @@ import { ScheduledEvent } from "../../../transformers/scheduledEvent.ts"; import { DiscordScheduledEvent } from "../../../types/discord.ts"; import { ScheduledEventEntityType, ScheduledEventPrivacyLevel } from "../../../types/shared.ts"; -/** Create a guild scheduled event in the guild. A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` status at any time. */ +/** + * Creates a scheduled event in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to create the scheduled event in. + * @param options - The parameters for the creation of the scheduled event. + * @returns An instance of the created {@link ScheduledEvent}. + * + * @remarks + * Requires the `MANAGE_EVENTS` permission. + * + * A guild can only have a maximum of 100 events with a status of {@link ScheduledEventStatus.Active} or {@link ScheduledEventStatus.Scheduled} (inclusive). + * + * Fires a _Guild Scheduled Event Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event} + */ export async function createScheduledEvent( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/events/deleteScheduledEvent.ts b/helpers/guilds/events/deleteScheduledEvent.ts new file mode 100644 index 000000000..cea5c619d --- /dev/null +++ b/helpers/guilds/events/deleteScheduledEvent.ts @@ -0,0 +1,23 @@ +import { Bot } from "../../../bot.ts"; + +/** + * Deletes a scheduled event from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete the scheduled event from. + * @param eventId - The ID of the scheduled event to delete. + * + * @remarks + * Requires the `MANAGE_EVENTS` permission. + * + * Fires a _Guild Scheduled Event Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event} + */ +export async function deleteScheduledEvent(bot: Bot, guildId: bigint, eventId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.GUILD_SCHEDULED_EVENT(guildId, eventId), + ); +} diff --git a/helpers/guilds/scheduledEvents/editScheduledEvent.ts b/helpers/guilds/events/editScheduledEvent.ts similarity index 79% rename from helpers/guilds/scheduledEvents/editScheduledEvent.ts rename to helpers/guilds/events/editScheduledEvent.ts index 28972e8f9..fedd92aa2 100644 --- a/helpers/guilds/scheduledEvents/editScheduledEvent.ts +++ b/helpers/guilds/events/editScheduledEvent.ts @@ -3,7 +3,25 @@ import { ScheduledEvent } from "../../../transformers/scheduledEvent.ts"; import { DiscordScheduledEvent } from "../../../types/discord.ts"; import { ScheduledEventEntityType, ScheduledEventPrivacyLevel, ScheduledEventStatus } from "../../../types/shared.ts"; -/** Modify a guild scheduled event. To start or end an event, use this endpoint to modify the event's status. */ +/** + * Edits a scheduled event. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the scheduled event in. + * @param eventId - The ID of the scheduled event to edit. + * @returns An instance of the edited {@link ScheduledEvent}. + * + * @remarks + * Requires the `MANAGE_EVENTS` permission. + * + * To start or end an event, modify the event's `status` property. + * + * The `entity_metadata` property is discarded for events whose `entity_type` is not {@link ScheduledEventEntityType.External}. + * + * Fires a _Guild Scheduled Event Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event} + */ export async function editScheduledEvent( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/scheduledEvents/getScheduledEvent.ts b/helpers/guilds/events/getScheduledEvent.ts similarity index 54% rename from helpers/guilds/scheduledEvents/getScheduledEvent.ts rename to helpers/guilds/events/getScheduledEvent.ts index fee78a390..49b65d08b 100644 --- a/helpers/guilds/scheduledEvents/getScheduledEvent.ts +++ b/helpers/guilds/events/getScheduledEvent.ts @@ -2,7 +2,17 @@ import { Bot } from "../../../bot.ts"; import { ScheduledEvent } from "../../../transformers/scheduledEvent.ts"; import { DiscordScheduledEvent } from "../../../types/discord.ts"; -/** Get a guild scheduled event. */ +/** + * Gets a scheduled event by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the scheduled event from. + * @param eventId - The ID of the scheduled event to get. + * @param options - The parameters for the fetching of the scheduled event. + * @returns An instance of {@link ScheduledEvent}. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event} + */ export async function getScheduledEvent( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts b/helpers/guilds/events/getScheduledEventUsers.ts similarity index 74% rename from helpers/guilds/scheduledEvents/getScheduledEventUsers.ts rename to helpers/guilds/events/getScheduledEventUsers.ts index 9e71ff184..b79dd7c1c 100644 --- a/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts +++ b/helpers/guilds/events/getScheduledEventUsers.ts @@ -3,6 +3,25 @@ import { Member, User } from "../../../transformers/member.ts"; import { DiscordMember, DiscordUser } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; +// TODO: This endpoint discards certain data from the result. +// Create `ScheduledEventUser` type and parse the data to it. + +/** + * Gets the list of subscribers to a scheduled event from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the subscribers to the scheduled event from. + * @param eventId - The ID of the scheduled event to get the subscribers of. + * @param options - The parameters for the fetching of the subscribers. + * @returns A collection of {@link User} objects assorted by user ID. + * + * @remarks + * Requires the `MANAGE_EVENTS` permission. + * + * Users are ordered by their IDs in _ascending_ order. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users} + */ export async function getScheduledEventUsers( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/scheduledEvents/getScheduledEvents.ts b/helpers/guilds/events/getScheduledEvents.ts similarity index 64% rename from helpers/guilds/scheduledEvents/getScheduledEvents.ts rename to helpers/guilds/events/getScheduledEvents.ts index d06522296..67d6eeb17 100644 --- a/helpers/guilds/scheduledEvents/getScheduledEvents.ts +++ b/helpers/guilds/events/getScheduledEvents.ts @@ -3,7 +3,16 @@ import { ScheduledEvent } from "../../../transformers/scheduledEvent.ts"; import { DiscordScheduledEvent } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -/** Get a list of guild scheduled event for the given guild. */ +/** + * Gets the list of scheduled events for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the scheduled events from. + * @param options - The parameters for the fetching of the scheduled events. + * @returns A collection of {@link ScheduledEvent} objects assorted by event ID. + * + * @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild} + */ export async function getScheduledEvents( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/scheduledEvents/mod.ts b/helpers/guilds/events/mod.ts similarity index 100% rename from helpers/guilds/scheduledEvents/mod.ts rename to helpers/guilds/events/mod.ts diff --git a/helpers/guilds/getAuditLogs.ts b/helpers/guilds/getAuditLog.ts similarity index 86% rename from helpers/guilds/getAuditLogs.ts rename to helpers/guilds/getAuditLog.ts index acf467e1b..58602c44d 100644 --- a/helpers/guilds/getAuditLogs.ts +++ b/helpers/guilds/getAuditLog.ts @@ -21,8 +21,22 @@ export type AuditLog = { applicationCommands: ApplicationCommand[]; }; -/** Returns the audit logs for the guild. Requires VIEW_AUDIT_LOGS permission */ -export async function getAuditLogs(bot: Bot, guildId: bigint, options?: GetGuildAuditLog): Promise { +// TODO: Move `AuditLog` into its own transformer file. + +/** + * Gets a guild's audit log. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the audit log of. + * @param options - The parameters for the fetching of the audit log. + * @returns An instance of {@link AuditLog}. + * + * @remarks + * Requires the `VIEW_AUDIT_LOG` permission. + * + * @see {@link https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log} + */ +export async function getAuditLog(bot: Bot, guildId: bigint, options?: GetGuildAuditLog): Promise { if (options?.limit) { options.limit = options.limit >= 1 && options.limit <= 100 ? options.limit : 50; } diff --git a/helpers/guilds/getAvailableVoiceRegions.ts b/helpers/guilds/getAvailableVoiceRegions.ts deleted file mode 100644 index 1605d7c79..000000000 --- a/helpers/guilds/getAvailableVoiceRegions.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { VoiceRegions } from "../../transformers/voiceRegion.ts"; -import { DiscordVoiceRegion } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; - -/** Returns an array of voice regions that can be used when creating servers. */ -export async function getAvailableVoiceRegions(bot: Bot): Promise> { - const results = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.VOICE_REGIONS(), - ); - - return new Collection( - results.map((result) => { - const region = bot.transformers.voiceRegion(bot, result); - return [region.id, region]; - }), - ); -} diff --git a/helpers/guilds/getBan.ts b/helpers/guilds/getBan.ts index 948e07c2f..041353eb8 100644 --- a/helpers/guilds/getBan.ts +++ b/helpers/guilds/getBan.ts @@ -7,12 +7,26 @@ export type Ban = { user: User; }; -/** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */ -export async function getBan(bot: Bot, guildId: bigint, memberId: bigint): Promise { +// TODO: Move `Ban` into its own transformer file. + +/** + * Gets a ban by user ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the ban from. + * @param userId - The ID of the user to get the ban for. + * @returns An instance of {@link Ban}. + * + * @remarks + * Requires the `BAN_MEMBERS` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-ban} + */ +export async function getBan(bot: Bot, guildId: bigint, userId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, "GET", - bot.constants.routes.GUILD_BAN(guildId, memberId), + bot.constants.routes.GUILD_BAN(guildId, userId), ); return { diff --git a/helpers/guilds/getBans.ts b/helpers/guilds/getBans.ts index 6c7e785fe..72b5042d9 100644 --- a/helpers/guilds/getBans.ts +++ b/helpers/guilds/getBans.ts @@ -3,7 +3,21 @@ import { DiscordBan } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; import { Ban } from "./getBan.ts"; -/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ +/** + * Gets the list of bans for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the list of bans for. + * @param options - The parameters for the fetching of the list of bans. + * @returns A collection of {@link Ban} objects assorted by user ID. + * + * @remarks + * Requires the `BAN_MEMBERS` permission. + * + * Users are ordered by their IDs in _ascending_ order. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-bans} + */ export async function getBans(bot: Bot, guildId: bigint, options?: GetBans): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/getGuild.ts b/helpers/guilds/getGuild.ts index 211658388..7ab0df1a0 100644 --- a/helpers/guilds/getGuild.ts +++ b/helpers/guilds/getGuild.ts @@ -3,8 +3,14 @@ import { Guild } from "../../transformers/guild.ts"; import { DiscordGuild } from "../../types/discord.ts"; /** - * This function fetches a guild's data. This is not the same data as a GUILD_CREATE. - * So it does not cache the guild, you must do it manually. + * Gets a guild by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get. + * @param options - The parameters for the fetching of the guild. + * @returns An instance of {@link Guild}. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild} */ export async function getGuild( bot: Bot, diff --git a/helpers/guilds/getGuildBannerUrl.ts b/helpers/guilds/getGuildBannerUrl.ts index f81fab2c2..5b4acdcd1 100644 --- a/helpers/guilds/getGuildBannerUrl.ts +++ b/helpers/guilds/getGuildBannerUrl.ts @@ -1,10 +1,19 @@ import type { Bot } from "../../bot.ts"; import { ImageFormat, ImageSize } from "../members/getAvatarUrl.ts"; -/** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ +// TODO: Move `banner` from `options` into the parameters and rename to `imageHash`. + +/** + * Builds a URL to the guild banner stored in the Discord CDN. + * + * @param bot - The bot instance to use to build the URL. + * @param guildId - The ID of the guild to get the link to the banner for. + * @param options - The parameters for the building of the URL. + * @returns The link to the resource or `undefined` if no banner has been set. + */ export function getGuildBannerURL( bot: Bot, - id: bigint, + guildId: bigint, options: { banner?: string | bigint; size?: ImageSize; @@ -14,7 +23,7 @@ export function getGuildBannerURL( return options.banner ? bot.utils.formatImageURL( bot.constants.routes.GUILD_BANNER( - id, + guildId, typeof options.banner === "string" ? options.banner : bot.utils.iconBigintToHash(options.banner), ), options.size || 128, diff --git a/helpers/guilds/getGuildIconUrl.ts b/helpers/guilds/getGuildIconUrl.ts index 60e62fdb4..793efd4d9 100644 --- a/helpers/guilds/getGuildIconUrl.ts +++ b/helpers/guilds/getGuildIconUrl.ts @@ -1,21 +1,28 @@ import type { Bot } from "../../bot.ts"; import { ImageFormat, ImageSize } from "../members/getAvatarUrl.ts"; -/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ +/** + * Builds a URL to the guild icon stored in the Discord CDN. + * + * @param bot - The bot instance to use to build the URL. + * @param guildId - The ID of the guild to get the link to the banner for. + * @param options - The parameters for the building of the URL. + * @returns The link to the resource or `undefined` if no banner has been set. + */ export function getGuildIconURL( bot: Bot, - id: bigint, - icon: bigint | undefined, + guildId: bigint, + imageHash: bigint | undefined, options?: { size?: ImageSize; format?: ImageFormat; }, ): string | undefined { - return icon + return imageHash ? bot.utils.formatImageURL( bot.constants.routes.GUILD_ICON( - id, - typeof icon === "string" ? icon : bot.utils.iconBigintToHash(icon), + guildId, + typeof imageHash === "string" ? imageHash : bot.utils.iconBigintToHash(imageHash), ), options?.size || 128, options?.format, diff --git a/helpers/guilds/getGuildPreview.ts b/helpers/guilds/getGuildPreview.ts index e52a0e2a4..2baa2391a 100644 --- a/helpers/guilds/getGuildPreview.ts +++ b/helpers/guilds/getGuildPreview.ts @@ -18,7 +18,20 @@ export type GuildPreview = { stickers: Sticker[]; }; -/** Returns the guild preview object for the given id. If the bot is not in the guild, then the guild must be Discoverable. */ +// TODO: Move `GuildPreview` into its own transformer file. + +/** + * Gets the preview of a guild by a guild's ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the preview of. + * @returns An instance of {@link GuildPreview}. + * + * @remarks + * If the bot user is not in the guild, the guild must be lurkable. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-preview} + */ export async function getGuildPreview(bot: Bot, guildId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/getGuildSplashUrl.ts b/helpers/guilds/getGuildSplashUrl.ts index 83885a38d..4d3672579 100644 --- a/helpers/guilds/getGuildSplashUrl.ts +++ b/helpers/guilds/getGuildSplashUrl.ts @@ -1,21 +1,29 @@ import type { Bot } from "../../bot.ts"; import { ImageFormat, ImageSize } from "../members/getAvatarUrl.ts"; -/** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ +/** + * Builds the URL to a guild splash stored in the Discord CDN. + * + * @param bot - The bot instance to use to build the URL. + * @param guildId - The ID of the guild to get the splash of. + * @param imageHash - The hash identifying the splash image. + * @param options - The parameters for the building of the URL. + * @returns The link to the resource or `undefined` if the guild does not have a splash image set. + */ export function getGuildSplashURL( bot: Bot, - id: bigint, - splash: bigint | undefined, + guildId: bigint, + imageHash: bigint | undefined, options?: { size?: ImageSize; format?: ImageFormat; }, ): string | undefined { - return splash + return imageHash ? bot.utils.formatImageURL( bot.constants.routes.GUILD_SPLASH( - id, - typeof splash === "string" ? splash : bot.utils.iconBigintToHash(splash), + guildId, + typeof imageHash === "string" ? imageHash : bot.utils.iconBigintToHash(imageHash), ), options?.size || 128, options?.format, diff --git a/helpers/guilds/getPruneCount.ts b/helpers/guilds/getPruneCount.ts index dcbd6149c..7454345b3 100644 --- a/helpers/guilds/getPruneCount.ts +++ b/helpers/guilds/getPruneCount.ts @@ -4,7 +4,19 @@ interface DiscordPrunedCount { pruned: number; } -/** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ +/** + * Gets the number of members that would be kicked from a guild during pruning. + * + * @param bot - The bot instance used to make the request + * @param guildId - The ID of the guild to get the prune count of. + * @param options - The parameters for the fetching of the prune count. + * @returns A number indicating the number of members that would be kicked. + * + * @remarks + * Requires the `KICK_MEMBERS` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-prune-count} + */ export async function getPruneCount(bot: Bot, guildId: bigint, options?: GetGuildPruneCountQuery): Promise { if (options?.days && options.days < 1) throw new Error(bot.constants.Errors.PRUNE_MIN_DAYS); if (options?.days && options.days > 30) throw new Error(bot.constants.Errors.PRUNE_MAX_DAYS); diff --git a/helpers/guilds/getVanityUrl.ts b/helpers/guilds/getVanityUrl.ts index e3096bc6e..b8d9c0817 100644 --- a/helpers/guilds/getVanityUrl.ts +++ b/helpers/guilds/getVanityUrl.ts @@ -1,11 +1,26 @@ import type { Bot } from "../../bot.ts"; export type VanityUrl = { - code?: string; + code: string | null; uses: number; }; -/** Returns the code and uses of the vanity url for this server if it is enabled else `code` will be null. Requires the `MANAGE_GUILD` permission. */ +// TODO: Move `VanityUrl` into its own transformer file. + +/** + * Gets information about the vanity url of a guild. + * + * @param bot - The bot instance used to make the request + * @param guildId - The ID of the guild to get the vanity url information for. + * @returns An instance of {@link VanityUrl}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * The `code` property will be `null` if the guild does not have a set vanity url. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-vanity-url} + */ export async function getVanityUrl(bot: Bot, guildId: bigint): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/getVoiceRegions.ts b/helpers/guilds/getVoiceRegions.ts deleted file mode 100644 index b73bd5554..000000000 --- a/helpers/guilds/getVoiceRegions.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { VoiceRegions } from "../../transformers/voiceRegion.ts"; -import { DiscordVoiceRegion } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; - -/** Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when the guild is VIP-enabled. */ -export async function getVoiceRegions(bot: Bot, guildId: bigint): Promise> { - const results = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.GUILD_REGIONS(guildId), - ); - - return new Collection( - results.map((result) => { - const region = bot.transformers.voiceRegion(bot, result); - return [region.id, region]; - }), - ); -} diff --git a/helpers/guilds/getWelcomeScreen.ts b/helpers/guilds/getWelcomeScreen.ts index cb450de1e..c95d22c8c 100644 --- a/helpers/guilds/getWelcomeScreen.ts +++ b/helpers/guilds/getWelcomeScreen.ts @@ -2,7 +2,19 @@ import type { Bot } from "../../bot.ts"; import { WelcomeScreen } from "../../transformers/welcomeScreen.ts"; import { DiscordWelcomeScreen } from "../../types/discord.ts"; -/** Returns the Welcome Screen object for the guild. Requires the `MANAGE_GUILD` permission. */ +/** + * Gets the welcome screen for a guild. + * + * @param bot - The bot instance used to make the request + * @param guildId - The ID of the guild to get the welcome screen for. + * @returns An instance of {@link WelcomeScreen}. + * + * @remarks + * If the welcome screen is not enabled: + * - Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen} + */ export async function getWelcomeScreen(bot: Bot, guildId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/guilds/getWidget.ts b/helpers/guilds/getWidget.ts deleted file mode 100644 index d329a1fe3..000000000 --- a/helpers/guilds/getWidget.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { GuildWidget } from "../../transformers/widget.ts"; -import { DiscordGuildWidget } from "../../types/discord.ts"; - -/** Returns the widget for the guild. */ -export async function getWidget(bot: Bot, guildId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.GUILD_WIDGET_JSON(guildId), - ); - - return bot.transformers.widget(bot, result); -} diff --git a/helpers/guilds/getWidgetSettings.ts b/helpers/guilds/getWidgetSettings.ts deleted file mode 100644 index 194f1386f..000000000 --- a/helpers/guilds/getWidgetSettings.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { GuildWidgetSettings } from "../../transformers/widgetSettings.ts"; -import { DiscordGuildWidgetSettings } from "../../types/discord.ts"; - -/** Returns a guild widget settings object. Requires the MANAGE_GUILD permission. */ -export async function getWidgetSettings(bot: Bot, guildId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.GUILD_WIDGET(guildId), - ); - - return bot.transformers.widgetSettings(bot, result); -} diff --git a/helpers/guilds/integrations/deleteIntegration.ts b/helpers/guilds/integrations/deleteIntegration.ts new file mode 100644 index 000000000..f6f2889f7 --- /dev/null +++ b/helpers/guilds/integrations/deleteIntegration.ts @@ -0,0 +1,26 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes an integration attached to a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild from which to delete the integration. + * @param integrationId - The ID of the integration to delete from the guild. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Deletes all webhooks associated with the integration, and kicks the associated bot if there is one. + * + * Fires a _Guild Integrations Update_ gateway event. + * Fires a _Integration Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#delete-guild-integration} + */ +export async function deleteIntegration(bot: Bot, guildId: bigint, integrationId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.GUILD_INTEGRATION(guildId, integrationId), + ); +} diff --git a/helpers/integrations/getIntegrations.ts b/helpers/guilds/integrations/getIntegrations.ts similarity index 61% rename from helpers/integrations/getIntegrations.ts rename to helpers/guilds/integrations/getIntegrations.ts index 47a34feac..21fc2a8e4 100644 --- a/helpers/integrations/getIntegrations.ts +++ b/helpers/guilds/integrations/getIntegrations.ts @@ -1,9 +1,20 @@ -import type { Bot } from "../../bot.ts"; -import { Integration } from "../../transformers/integration.ts"; -import { DiscordIntegration } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; +import type { Bot } from "../../../bot.ts"; +import { Integration } from "../../../transformers/integration.ts"; +import { DiscordIntegration } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; -/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ +/** + * Gets the list of integrations attached to a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the list of integrations from. + * @returns A collection of {@link Integration} objects assorted by integration ID. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-integrations} + */ export async function getIntegrations(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/integrations/mod.ts b/helpers/guilds/integrations/mod.ts similarity index 100% rename from helpers/integrations/mod.ts rename to helpers/guilds/integrations/mod.ts diff --git a/helpers/invites/createInvite.ts b/helpers/guilds/invites/createInvite.ts similarity index 73% rename from helpers/invites/createInvite.ts rename to helpers/guilds/invites/createInvite.ts index bb9e8b043..f02148e63 100644 --- a/helpers/invites/createInvite.ts +++ b/helpers/guilds/invites/createInvite.ts @@ -1,9 +1,26 @@ -import type { Bot } from "../../bot.ts"; -import { DiscordInvite } from "../../types/discord.ts"; -import { TargetTypes } from "../../types/shared.ts"; +import type { Bot } from "../../../bot.ts"; +import { DiscordInvite } from "../../../types/discord.ts"; +import { TargetTypes } from "../../../types/shared.ts"; import { BaseInvite } from "./getInvite.ts"; -/** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */ +/** + * Creates an invite to a channel in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to create the invite to. + * @param options - The parameters for the creation of the invite. + * @returns An instance of the created {@link BaseInvite | Invite}. + * + * @remarks + * Requires the `CREATE_INSTANT_INVITE` permission. + * + * Fires an _Invite Create_ gateway event. + * + * @privateRemarks + * The request body is not optional, and an empty JSON object must be sent regardless of whether any fields are being transmitted. + * + * @see {@link https://discord.com/developers/docs/resources/channel#create-channel-invite} + */ export async function createInvite( bot: Bot, channelId: bigint, diff --git a/helpers/guilds/invites/deleteInvite.ts b/helpers/guilds/invites/deleteInvite.ts new file mode 100644 index 000000000..b99194256 --- /dev/null +++ b/helpers/guilds/invites/deleteInvite.ts @@ -0,0 +1,18 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes an invite to a channel. + * + * @param bot - The bot instance to use to make the request. + * @param inviteCode - The invite code of the invite to delete. + * + * @remarks + * Requires the `MANAGE_CHANNELS` permission. + * + * Fires an _Invite Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-channel-invite} + */ +export async function deleteInvite(bot: Bot, inviteCode: string): Promise { + return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.INVITE(inviteCode)); +} diff --git a/helpers/invites/getInvite.ts b/helpers/guilds/invites/getInvite.ts similarity index 77% rename from helpers/invites/getInvite.ts rename to helpers/guilds/invites/getInvite.ts index 7002fc6da..d515999df 100644 --- a/helpers/invites/getInvite.ts +++ b/helpers/guilds/invites/getInvite.ts @@ -1,6 +1,6 @@ -import type { Bot } from "../../bot.ts"; -import { ScheduledEvent, TargetTypes, User } from "../../mod.ts"; -import { DiscordInviteMetadata } from "../../types/discord.ts"; +import type { Bot } from "../../../bot.ts"; +import { ScheduledEvent, TargetTypes, User } from "../../../mod.ts"; +import { DiscordInviteMetadata } from "../../../types/discord.ts"; export type BaseInvite = { code: string; @@ -24,7 +24,16 @@ export type InviteMetadata = BaseInvite & { createdAt: number; }; -/** Returns an invite for the given code or throws an error if the invite doesn't exists. */ +/** + * Gets an invite to a channel by its invite code. + * + * @param bot - The bot instance to use to make the request. + * @param inviteCode - The invite code of the invite to get. + * @param options - The parameters for the fetching of the invite. + * @returns An instance of {@link BaseInvite | Invite}. + * + * @see {@link https://discord.com/developers/docs/resources/invite#get-invite} + */ export async function getInvite(bot: Bot, inviteCode: string, options?: GetInvite): Promise { const result = await bot.rest.runMethod( bot.rest, diff --git a/helpers/invites/getInvites.ts b/helpers/guilds/invites/getInvites.ts similarity index 74% rename from helpers/invites/getInvites.ts rename to helpers/guilds/invites/getInvites.ts index bb1be0b49..ca745ca06 100644 --- a/helpers/invites/getInvites.ts +++ b/helpers/guilds/invites/getInvites.ts @@ -1,10 +1,21 @@ -import type { Bot } from "../../bot.ts"; -import { TargetTypes } from "../../mod.ts"; -import { DiscordInviteMetadata } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; +import type { Bot } from "../../../bot.ts"; +import { TargetTypes } from "../../../mod.ts"; +import { DiscordInviteMetadata } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; import { InviteMetadata } from "./getInvite.ts"; -/** Get all the invites for this guild. Requires MANAGE_GUILD permission */ +/** + * Gets the list of invites for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the invites from. + * @returns A collection of {@link InviteMetadata | Invite} objects assorted by invite code. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/invite#get-invites} + */ export async function getInvites(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod( bot.rest, diff --git a/helpers/invites/mod.ts b/helpers/guilds/invites/mod.ts similarity index 77% rename from helpers/invites/mod.ts rename to helpers/guilds/invites/mod.ts index 0bb798e21..e0bccf784 100644 --- a/helpers/invites/mod.ts +++ b/helpers/guilds/invites/mod.ts @@ -1,5 +1,4 @@ export * from "./createInvite.ts"; export * from "./deleteInvite.ts"; -export * from "./getChannelInvites.ts"; export * from "./getInvite.ts"; export * from "./getInvites.ts"; diff --git a/helpers/guilds/leaveGuild.ts b/helpers/guilds/leaveGuild.ts index de7495a71..7fa242763 100644 --- a/helpers/guilds/leaveGuild.ts +++ b/helpers/guilds/leaveGuild.ts @@ -1,6 +1,16 @@ import type { Bot } from "../../bot.ts"; -/** Leave a guild */ +/** + * Leaves a guild. + * + * @param bot - The bot instance used to make the request + * @param guildId - The ID of the guild to leave. + * + * @remarks + * Fires a _Guild Delete_ event. + * + * @see {@link https://discord.com/developers/docs/resources/user#leave-guild} + */ export async function leaveGuild(bot: Bot, guildId: bigint): Promise { return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_LEAVE(guildId)); } diff --git a/helpers/guilds/mod.ts b/helpers/guilds/mod.ts index 3d7a56687..5820019ac 100644 --- a/helpers/guilds/mod.ts +++ b/helpers/guilds/mod.ts @@ -1,12 +1,16 @@ export * from "./automod/mod.ts"; +export * from "./integrations/mod.ts"; +export * from "./invites/mod.ts"; +export * from "./events/mod.ts"; +export * from "./voice/mod.ts"; +export * from "./widget/mod.ts"; + export * from "./createGuild.ts"; export * from "./deleteGuild.ts"; export * from "./editGuild.ts"; export * from "./editGuildMfaLevel.ts"; export * from "./editWelcomeScreen.ts"; -export * from "./editWidget.ts"; -export * from "./getAuditLogs.ts"; -export * from "./getAvailableVoiceRegions.ts"; +export * from "./getAuditLog.ts"; export * from "./getBan.ts"; export * from "./getBans.ts"; export * from "./getGuild.ts"; @@ -16,10 +20,5 @@ export * from "./getGuildPreview.ts"; export * from "./getGuildSplashUrl.ts"; export * from "./getPruneCount.ts"; export * from "./getVanityUrl.ts"; -export * from "./getVoiceRegions.ts"; export * from "./getWelcomeScreen.ts"; -export * from "./getWidget.ts"; -export * from "./getWidgetImageUrl.ts"; -export * from "./getWidgetSettings.ts"; export * from "./leaveGuild.ts"; -export * from "./scheduledEvents/mod.ts"; diff --git a/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts b/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts deleted file mode 100644 index ca99eed7f..000000000 --- a/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Bot } from "../../../bot.ts"; - -/** Delete a scheduled event. */ -export async function deleteScheduledEvent(bot: Bot, guildId: bigint, eventId: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - bot.constants.routes.GUILD_SCHEDULED_EVENT(guildId, eventId), - ); -} diff --git a/helpers/voice/connectToVoiceChannel.ts b/helpers/guilds/voice/connectToVoiceChannel.ts similarity index 62% rename from helpers/voice/connectToVoiceChannel.ts rename to helpers/guilds/voice/connectToVoiceChannel.ts index 04bd0da13..125f3f038 100644 --- a/helpers/voice/connectToVoiceChannel.ts +++ b/helpers/guilds/voice/connectToVoiceChannel.ts @@ -1,7 +1,21 @@ -import type { Bot } from "../../bot.ts"; -import { AtLeastOne, GatewayOpcodes } from "../../types/shared.ts"; +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. */ +/** + * Connects the bot user to a voice or stage channel. + * + * This function sends the _Update Voice State_ gateway command over the gateway behind the scenes. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the voice channel to leave is in. + * + * @remarks + * Requires the `CONNECT` permission. + * + * Fires a _Voice State Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state} + */ export async function connectToVoiceChannel( bot: Bot, guildId: bigint, diff --git a/helpers/guilds/voice/editVoiceState.ts b/helpers/guilds/voice/editVoiceState.ts new file mode 100644 index 000000000..b72d72d11 --- /dev/null +++ b/helpers/guilds/voice/editVoiceState.ts @@ -0,0 +1,80 @@ +import type { Bot } from "../../../bot.ts"; + +export const updateBotVoiceState = editOwnVoiceState; + +/** + * Edits the voice state of the bot user. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild in which to edit the voice state of the bot user. + * @param options - The parameters for the edit of the voice state. + * + * @remarks + * The {@link EditOwnVoiceState.channelId | channelId} property of the {@link options} object parameter must point to a stage channel, and the bot user must already have joined it. + * + * If attempting to unmute oneself: + * - Requires the `MUTE_MEMBERS` permission. + * + * If attempting to request to speak: + * - Requires the `REQUEST_TO_SPEAK` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state} + */ +export async function editOwnVoiceState(bot: Bot, guildId: bigint, options: EditOwnVoiceState): Promise { + return await bot.rest.runMethod(bot.rest, "PATCH", bot.constants.routes.UPDATE_VOICE_STATE(guildId), { + channel_id: options.channelId, + suppress: options.suppress, + request_to_speak_timestamp: options.requestToSpeakTimestamp + ? new Date(options.requestToSpeakTimestamp).toISOString() + : options.requestToSpeakTimestamp, + }); +} + +// TODO: Make the `userId` property of `options` its own parameter. + +/** + * Edits the voice state of another user. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild in which to edit the voice state of the bot user. + * @param options - The parameters for the edit of the voice state. + * + * @remarks + * The {@link EditOwnVoiceState.channelId | channelId} property of the {@link options} object parameter must point to a stage channel, and the user must already have joined it. + * + * Requires the `MUTE_MEMBERS` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state} + */ +export async function editUserVoiceState(bot: Bot, guildId: bigint, options: EditUserVoiceState): Promise { + return await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.UPDATE_VOICE_STATE(guildId, options.userId), + { + channel_id: options.channelId, + suppress: options.suppress, + user_id: options.userId, + }, + ); +} + +/** https://discord.com/developers/docs/resources/guild#update-current-user-voice-state */ +export interface EditOwnVoiceState { + /** The id of the channel the user is currently in */ + channelId: bigint; + /** Toggles the user's suppress state */ + suppress?: boolean; + /** Sets the user's request to speak */ + requestToSpeakTimestamp?: number | null; +} + +/** https://discord.com/developers/docs/resources/guild#update-user-voice-state */ +export interface EditUserVoiceState { + /** The id of the channel the user is currently in */ + channelId: bigint; + /** Toggles the user's suppress state */ + suppress?: boolean; + /** The user id to target */ + userId: bigint; +} diff --git a/helpers/guilds/voice/getAvailableVoiceRegions.ts b/helpers/guilds/voice/getAvailableVoiceRegions.ts new file mode 100644 index 000000000..a05d2da39 --- /dev/null +++ b/helpers/guilds/voice/getAvailableVoiceRegions.ts @@ -0,0 +1,25 @@ +import type { Bot } from "../../../bot.ts"; +import { VoiceRegions } from "../../../transformers/voiceRegion.ts"; +import { DiscordVoiceRegion } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** + * Gets the list of available voice regions. + * + * @param bot - The bot instance to use to make the request. + * @returns A collection of {@link VoiceRegions | VoiceRegion} objects assorted by voice region ID. + */ +export async function getAvailableVoiceRegions(bot: Bot): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.VOICE_REGIONS(), + ); + + return new Collection( + results.map((result) => { + const region = bot.transformers.voiceRegion(bot, result); + return [region.id, region]; + }), + ); +} diff --git a/helpers/guilds/voice/getVoiceRegions.ts b/helpers/guilds/voice/getVoiceRegions.ts new file mode 100644 index 000000000..fc48e2185 --- /dev/null +++ b/helpers/guilds/voice/getVoiceRegions.ts @@ -0,0 +1,28 @@ +import type { Bot } from "../../../bot.ts"; +import { VoiceRegions } from "../../../transformers/voiceRegion.ts"; +import { DiscordVoiceRegion } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** + * Gets the list of voice regions for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the voice regions for. + * @returns A collection of {@link VoiceRegions | VoiceRegion} objects assorted by voice region ID. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-voice-regions} + */ +export async function getVoiceRegions(bot: Bot, guildId: bigint): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.GUILD_REGIONS(guildId), + ); + + return new Collection( + results.map((result) => { + const region = bot.transformers.voiceRegion(bot, result); + return [region.id, region]; + }), + ); +} diff --git a/helpers/guilds/voice/leaveVoiceChannel.ts b/helpers/guilds/voice/leaveVoiceChannel.ts new file mode 100644 index 000000000..d24eeeb7a --- /dev/null +++ b/helpers/guilds/voice/leaveVoiceChannel.ts @@ -0,0 +1,36 @@ +import type { Bot } from "../../../bot.ts"; +import { GatewayOpcodes } from "../../../types/shared.ts"; + +/** + * Leaves the voice channel the bot user is currently in. + * + * This function sends the _Update Voice State_ gateway command over the gateway behind the scenes. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the voice channel to leave is in. + * + * @remarks + * Fires a _Voice State Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state} + */ +export async function leaveVoiceChannel( + bot: Bot, + guildId: bigint, +): Promise { + 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`); + } + + return shard.send({ + op: GatewayOpcodes.VoiceStateUpdate, + d: { + guild_id: guildId.toString(), + channel_id: null, + self_mute: false, + self_deaf: false, + }, + }); +} diff --git a/helpers/guilds/voice/mod.ts b/helpers/guilds/voice/mod.ts new file mode 100644 index 000000000..80a759613 --- /dev/null +++ b/helpers/guilds/voice/mod.ts @@ -0,0 +1,5 @@ +export * from "./connectToVoiceChannel.ts"; +export * from "./editVoiceState.ts"; +export * from "./getAvailableVoiceRegions.ts"; +export * from "./getVoiceRegions.ts"; +export * from "./leaveVoiceChannel.ts"; diff --git a/helpers/guilds/widget/editWidgetSettings.ts b/helpers/guilds/widget/editWidgetSettings.ts new file mode 100644 index 000000000..fd71d7594 --- /dev/null +++ b/helpers/guilds/widget/editWidgetSettings.ts @@ -0,0 +1,38 @@ +import type { Bot } from "../../../bot.ts"; +import { GuildWidgetSettings } from "../../../transformers/widgetSettings.ts"; +import { DiscordGuildWidgetSettings } from "../../../types/discord.ts"; + +// TODO: Use `options` instead of `enabled` and `channelId`. + +/** + * Edits the settings of a guild's widget. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the settings of the widget of. + * @returns An instance of the edited {@link GuildWidgetSettings}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * Fires a _Guild Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-widget} + */ +export async function editWidgetSettings( + bot: Bot, + guildId: bigint, + enabled: boolean, + channelId?: string | null, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.GUILD_WIDGET(guildId), + { + enabled, + channel_id: channelId, + }, + ); + + return bot.transformers.widgetSettings(bot, result); +} diff --git a/helpers/guilds/widget/getWidget.ts b/helpers/guilds/widget/getWidget.ts new file mode 100644 index 000000000..404cfcac6 --- /dev/null +++ b/helpers/guilds/widget/getWidget.ts @@ -0,0 +1,22 @@ +import type { Bot } from "../../../bot.ts"; +import { GuildWidget } from "../../../transformers/widget.ts"; +import { DiscordGuildWidget } from "../../../types/discord.ts"; + +/** + * Gets the guild widget by guild ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the widget of. + * @returns An instance of {@link GuildWidget}. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-widget} + */ +export async function getWidget(bot: Bot, guildId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.GUILD_WIDGET_JSON(guildId), + ); + + return bot.transformers.widget(bot, result); +} diff --git a/helpers/guilds/getWidgetImageUrl.ts b/helpers/guilds/widget/getWidgetImageUrl.ts similarity index 75% rename from helpers/guilds/getWidgetImageUrl.ts rename to helpers/guilds/widget/getWidgetImageUrl.ts index 0f2b986d8..b0a9851a5 100644 --- a/helpers/guilds/getWidgetImageUrl.ts +++ b/helpers/guilds/widget/getWidgetImageUrl.ts @@ -1,6 +1,13 @@ -import type { Bot } from "../../bot.ts"; +import type { Bot } from "../../../bot.ts"; -/** Returns the widget image URL for the guild. */ +/** + * Builds a URL to the guild widget image stored in the Discord CDN. + * + * @param bot - The bot instance to use to build the URL. + * @param guildId - The ID of the guild to get the link to the widget image for. + * @param options - The parameters for the building of the URL. + * @returns The link to the resource. + */ export function getWidgetImageURL(bot: Bot, guildId: bigint, options?: GetGuildWidgetImageQuery): string { return bot.constants.routes.GUILD_WIDGET_IMAGE(guildId, options?.style); } diff --git a/helpers/guilds/widget/getWidgetSettings.ts b/helpers/guilds/widget/getWidgetSettings.ts new file mode 100644 index 000000000..e74e20d7f --- /dev/null +++ b/helpers/guilds/widget/getWidgetSettings.ts @@ -0,0 +1,25 @@ +import type { Bot } from "../../../bot.ts"; +import { GuildWidgetSettings } from "../../../transformers/widgetSettings.ts"; +import { DiscordGuildWidgetSettings } from "../../../types/discord.ts"; + +/** + * Gets the settings of a guild's widget. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the widget of. + * @returns An instance of {@link GuildWidgetSettings}. + * + * @remarks + * Requires the `MANAGE_GUILD` permission. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-widget-settings} + */ +export async function getWidgetSettings(bot: Bot, guildId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.GUILD_WIDGET(guildId), + ); + + return bot.transformers.widgetSettings(bot, result); +} diff --git a/helpers/guilds/widget/mod.ts b/helpers/guilds/widget/mod.ts new file mode 100644 index 000000000..061123e3d --- /dev/null +++ b/helpers/guilds/widget/mod.ts @@ -0,0 +1,4 @@ +export * from "./editWidgetSettings.ts"; +export * from "./getWidget.ts"; +export * from "./getWidgetImageUrl.ts"; +export * from "./getWidgetSettings.ts"; diff --git a/helpers/integrations/deleteIntegration.ts b/helpers/integrations/deleteIntegration.ts deleted file mode 100644 index 8fa8af047..000000000 --- a/helpers/integrations/deleteIntegration.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ -export async function deleteIntegration(bot: Bot, guildId: bigint, id: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_INTEGRATION(guildId, id)); -} diff --git a/helpers/interactions/commands/createApplicationCommand.ts b/helpers/interactions/commands/createApplicationCommand.ts deleted file mode 100644 index 603f138c5..000000000 --- a/helpers/interactions/commands/createApplicationCommand.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { ApplicationCommand, ApplicationCommandOption, ApplicationCommandTypes, Localization } from "../../../mod.ts"; -import { DiscordApplicationCommand, DiscordApplicationCommandOption } from "../../../types/discord.ts"; -import { AtLeastOne, PermissionStrings } from "../../../types/shared.ts"; - -/** - * There are two kinds of Application Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means: - * - * - Your app **cannot** have two global commands with the same name - * - Your app **cannot** have two guild commands within the same name **on the same guild** - * - Your app **can** have a global and guild command with the same name - * - Multiple apps **can** have commands with the same names - * - * Global commands are cached for **1 hour**. That means that new global commands will fan out slowly across all guilds, and will be guaranteed to be updated in an hour. - * Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. - */ -export async function createApplicationCommand( - bot: Bot, - command: CreateApplicationCommand | CreateContextApplicationCommand, - guildId?: bigint, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "POST", - guildId - ? bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId) - : bot.constants.routes.COMMANDS(bot.applicationId), - isContextApplicationCommand(command) - ? { - name: command.name, - name_localizations: command.nameLocalizations, - type: command.type, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - } - : { - name: command.name, - name_localizations: command.nameLocalizations, - description: command.description, - description_localizations: command.descriptionLocalizations, - type: command.type, - options: command.options ? makeOptionsForCommand(command.options) : undefined, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - }, - ); - - return bot.transformers.applicationCommand(bot, result); -} - -export function makeOptionsForCommand(options: ApplicationCommandOption[]): DiscordApplicationCommandOption[] { - return options.map((option) => ({ - type: option.type, - name: option.name, - name_localizations: option.nameLocalizations, - description: option.description, - description_localizations: option.descriptionLocalizations, - required: option.required, - choices: option.choices?.map((choice) => ({ - name: choice.name, - name_localizations: choice.nameLocalizations, - value: choice.value, - })), - options: option.options ? makeOptionsForCommand(option.options) : undefined, - channel_types: option.channelTypes, - autocomplete: option.autocomplete, - min_value: option.minValue, - max_value: option.maxValue, - min_length: option.minLength, - max_length: option.maxLength, - })); -} - -/** https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params */ -export interface CreateApplicationCommand { - /** - * Name of command, 1-32 characters. - * `ApplicationCommandTypes.ChatInput` command names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. - * If there is a lowercase variant of any letters used, you must use those. - * Characters with no lowercase variants and/or uncased letters are still allowed. - * ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands may be mixed case and can include spaces. - */ - name: string; - /** Localization object for the `name` field. Values follow the same restrictions as `name` */ - nameLocalizations?: Localization; - /** 1-100 character description */ - description: string; - /** Localization object for the `description` field. Values follow the same restrictions as `description` */ - descriptionLocalizations?: Localization; - /** Type of command, defaults `ApplicationCommandTypes.ChatInput` if not set */ - type?: ApplicationCommandTypes; - /** Parameters for the command */ - options?: ApplicationCommandOption[]; - /** Set of permissions represented as a bit set */ - defaultMemberPermissions?: PermissionStrings[]; - /** Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. */ - dmPermission?: boolean; -} - -/** https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params */ -export interface CreateContextApplicationCommand - extends Omit { - /** The type of the command */ - type: ApplicationCommandTypes.Message | ApplicationCommandTypes.User; -} - -export function isContextApplicationCommand( - cmd: AtLeastOne | AtLeastOne, -): cmd is AtLeastOne { - return cmd.type === ApplicationCommandTypes.Message || cmd.type === ApplicationCommandTypes.User; -} diff --git a/helpers/interactions/commands/createGlobalApplicationCommand.ts b/helpers/interactions/commands/createGlobalApplicationCommand.ts new file mode 100644 index 000000000..608ca788c --- /dev/null +++ b/helpers/interactions/commands/createGlobalApplicationCommand.ts @@ -0,0 +1,31 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand, CreateApplicationCommand } from "../../../mod.ts"; +import { DiscordApplicationCommand } from "../../../types/discord.ts"; + +/** + * Creates an application command accessible globally; across different guilds and channels. + * + * @param bot - The bot instance to use to make the request. + * @param command - The command to create. + * @returns An instance of the created {@link ApplicationCommand}. + * + * @remarks + * ⚠️ Creating a command with the same name as an existing command for your application will overwrite the old command. + * ⚠️ Global commands once created are cached for periods of __an hour__, so changes made to existing commands will take an hour to surface. + * ⚠️ You can only create up to 200 _new_ commands daily. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#create-global-application-command} + */ +export async function createGlobalApplicationCommand( + bot: Bot, + command: CreateApplicationCommand, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "POST", + bot.constants.routes.COMMANDS(bot.applicationId), + bot.transformers.reverse.createApplicationCommand(bot, command), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/createGuildApplicationCommand.ts b/helpers/interactions/commands/createGuildApplicationCommand.ts new file mode 100644 index 000000000..921ba2087 --- /dev/null +++ b/helpers/interactions/commands/createGuildApplicationCommand.ts @@ -0,0 +1,31 @@ +import { Bot } from "../../../bot.ts"; +import { ApplicationCommand, CreateApplicationCommand, DiscordApplicationCommand } from "../../../mod.ts"; + +/** + * Creates an application command only accessible in a specific guild. + * + * @param bot - The bot instance to use to make the request. + * @param command - The command to create. + * @param guildId - The ID of the guild to create the command for. + * @returns An instance of the created {@link ApplicationCommand}. + * + * @remarks + * ⚠️ Creating a command with the same name as an existing command for your application will overwrite the old command. + * ⚠️ You can only create up to 200 _new_ commands daily. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command} + */ +export async function createGuildApplicationCommand( + bot: Bot, + command: CreateApplicationCommand, + guildId: bigint, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "POST", + bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId), + bot.transformers.reverse.createApplicationCommand(bot, command), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/deleteApplicationCommand.ts b/helpers/interactions/commands/deleteApplicationCommand.ts deleted file mode 100644 index e9392197b..000000000 --- a/helpers/interactions/commands/deleteApplicationCommand.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Bot } from "../../../bot.ts"; - -/** Deletes a application command. */ -export async function deleteApplicationCommand(bot: Bot, id: bigint, guildId?: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - guildId - ? bot.constants.routes.COMMANDS_GUILD_ID(bot.applicationId, guildId, id) - : bot.constants.routes.COMMANDS_ID(bot.applicationId, id), - ); -} diff --git a/helpers/interactions/commands/deleteGlobalApplicationCommand.ts b/helpers/interactions/commands/deleteGlobalApplicationCommand.ts new file mode 100644 index 000000000..02222d4a1 --- /dev/null +++ b/helpers/interactions/commands/deleteGlobalApplicationCommand.ts @@ -0,0 +1,17 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes an application command registered globally. + * + * @param bot - The bot instance to use to make the request. + * @param commandId - The ID of the command to delete. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command} + */ +export async function deleteGlobalApplicationCommand(bot: Bot, commandId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.COMMANDS_ID(bot.applicationId, commandId), + ); +} diff --git a/helpers/interactions/commands/deleteGuildApplicationCommand.ts b/helpers/interactions/commands/deleteGuildApplicationCommand.ts new file mode 100644 index 000000000..865474fe2 --- /dev/null +++ b/helpers/interactions/commands/deleteGuildApplicationCommand.ts @@ -0,0 +1,20 @@ +import type { Bot } from "../../../bot.ts"; + +// TODO: `guildId` and `commandId` should be swapped. + +/** + * Deletes an application command registered in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete the command from. + * @param commandId - The ID of the command to delete from the guild. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command} + */ +export async function deleteGuildApplicationCommand(bot: Bot, commandId: bigint, guildId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId), + ); +} diff --git a/helpers/interactions/commands/deleteInteractionResponse.ts b/helpers/interactions/commands/deleteInteractionResponse.ts deleted file mode 100644 index 48face800..000000000 --- a/helpers/interactions/commands/deleteInteractionResponse.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Bot } from "../../../bot.ts"; - -/** To delete your response to a application command. If a message id is not provided, it will default to deleting the original response. */ -export async function deleteInteractionResponse(bot: Bot, token: string, messageId?: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - messageId - ? bot.constants.routes.INTERACTION_ID_TOKEN_MESSAGE_ID(bot.applicationId, token, messageId) - : bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), - ); -} diff --git a/helpers/interactions/commands/editApplicationCommandPermissions.ts b/helpers/interactions/commands/editApplicationCommandPermissions.ts index a2c0c6683..6a0e4e248 100644 --- a/helpers/interactions/commands/editApplicationCommandPermissions.ts +++ b/helpers/interactions/commands/editApplicationCommandPermissions.ts @@ -3,7 +3,23 @@ import { ApplicationCommandPermission } from "../../../transformers/applicationC import { DiscordGuildApplicationCommandPermissions } from "../../../types/discord.ts"; import { ApplicationCommandPermissionTypes } from "../../../types/shared.ts"; -/** Edits command permissions for a specific command for your application in a guild. */ +// TODO: Make `options` into an object with a `permissions` field. + +/** + * Edits the permissions for a guild application command. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the command is registered in. + * @param commandId - The ID of the command to edit the permissions of. + * @param bearerToken - The bearer token to use to make the request. + * @param options - The parameters for the edit of the command permissions. + * @returns An instance of the edited {@link ApplicationCommandPermission}. + * + * @remarks + * The bearer token requires the `applications.commands.permissions.update` scope to be enabled, and to have access to the guild whose ID has been provided in the parameters. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions} + */ export async function editApplicationCommandPermissions( bot: Bot, guildId: bigint, diff --git a/helpers/interactions/commands/editGlobalApplicationCommand.ts b/helpers/interactions/commands/editGlobalApplicationCommand.ts new file mode 100644 index 000000000..c446e98a4 --- /dev/null +++ b/helpers/interactions/commands/editGlobalApplicationCommand.ts @@ -0,0 +1,28 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { CreateApplicationCommand, DiscordApplicationCommand } from "../../../types/mod.ts"; + +/** + * Edits a global application command. + * + * @param bot - The bot instance to use to make the request. + * @param commandId - The ID of the command to edit. + * @param options - The parameters for the edit of the command. + * @returns An instance of the edited {@link ApplicationCommand}. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command} + */ +export async function editGlobalApplicationCommand( + bot: Bot, + commandId: bigint, + options: CreateApplicationCommand, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.COMMANDS_ID(bot.applicationId, commandId), + bot.transformers.reverse.createApplicationCommand(bot, options), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/editGuildApplicationCommand.ts b/helpers/interactions/commands/editGuildApplicationCommand.ts new file mode 100644 index 000000000..94c44ead5 --- /dev/null +++ b/helpers/interactions/commands/editGuildApplicationCommand.ts @@ -0,0 +1,32 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { CreateApplicationCommand, DiscordApplicationCommand } from "../../../types/mod.ts"; + +// TODO: Swap `commandId` and `guildId` parameters. + +/** + * Edits an application command registered in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the command is registered in. + * @param commandId - The ID of the command to edit. + * @param options - The parameters for the edit of the command. + * @returns An instance of the edited {@link ApplicationCommand}. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command} + */ +export async function editGuildApplicationCommand( + bot: Bot, + commandId: bigint, + guildId: bigint, + options: CreateApplicationCommand, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId), + bot.transformers.reverse.createApplicationCommand(bot, options), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/editInteractionResponse.ts b/helpers/interactions/commands/editInteractionResponse.ts deleted file mode 100644 index e843b716f..000000000 --- a/helpers/interactions/commands/editInteractionResponse.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { Message } from "../../../transformers/message.ts"; -import { DiscordMessage } from "../../../types/discord.ts"; -import { EditWebhookMessage } from "../../webhooks/editWebhookMessage.ts"; - -/** To edit your response to a application command. If a messageId is not provided it will default to editing the original response. */ -export async function editInteractionResponse( - bot: Bot, - token: string, - options: EditWebhookMessage & { - /** Id of the message you want to edit if undefined the initial response message will be edited */ - messageId?: bigint; - }, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "PATCH", - options.messageId - ? bot.constants.routes.WEBHOOK_MESSAGE(bot.applicationId, token, options.messageId) - : bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), - { - content: options.content, - embeds: options.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), - file: options.file, - allowed_mentions: options.allowedMentions - ? bot.transformers.reverse.allowedMentions(bot, options.allowedMentions) - : undefined, - attachments: options.attachments?.map((attachment) => bot.transformers.reverse.attachment(bot, attachment)), - components: options.components?.map((component) => bot.transformers.reverse.component(bot, component)), - message_id: options.messageId?.toString(), - }, - ); - - // If the original message was edited, this will not return a message - if (!options.messageId) return; - - return bot.transformers.message(bot, result); -} diff --git a/helpers/interactions/commands/getApplicationCommand.ts b/helpers/interactions/commands/getApplicationCommand.ts deleted file mode 100644 index 809a368cc..000000000 --- a/helpers/interactions/commands/getApplicationCommand.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; -import { DiscordApplicationCommand } from "../../../types/discord.ts"; - -/** Fetches the global command for the given Id. If a guildId is provided, the guild command will be fetched. */ -export async function getApplicationCommand( - bot: Bot, - commandId: bigint, - options?: GetApplicationCommand, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - options?.guildId - ? bot.constants.routes.COMMANDS_GUILD_ID( - bot.applicationId, - options.guildId, - commandId, - options?.withLocalizations, - ) - : bot.constants.routes.COMMANDS_ID(bot.applicationId, commandId, options?.withLocalizations), - ); - - return bot.transformers.applicationCommand(bot, result); -} - -/** https://discord.com/developers/docs/interactions/application-commands#endpoints-query-string-params */ -export interface GetApplicationCommand { - /** Guild ID of the guild in which the command is available if it is a guild-specific command */ - guildId?: bigint; - /** Whether to include full localization object (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default false */ - withLocalizations?: boolean; -} diff --git a/helpers/interactions/commands/getApplicationCommandPermission.ts b/helpers/interactions/commands/getApplicationCommandPermission.ts index 24a798c10..b542d4c66 100644 --- a/helpers/interactions/commands/getApplicationCommandPermission.ts +++ b/helpers/interactions/commands/getApplicationCommandPermission.ts @@ -2,7 +2,16 @@ import type { Bot } from "../../../bot.ts"; import { ApplicationCommandPermission } from "../../../transformers/applicationCommandPermission.ts"; import { DiscordGuildApplicationCommandPermissions } from "../../../types/discord.ts"; -/** Fetches command permissions for a specific command for your application in a guild. Returns a GuildApplicationCommandPermissions object. */ +/** + * Gets the permissions of a guild application command. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the command is registered in. + * @param commandId - The ID of the command to get the permissions of. + * @returns An instance of {@link ApplicationCommandPermission}. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions} + */ export async function getApplicationCommandPermission( bot: Bot, guildId: bigint, diff --git a/helpers/interactions/commands/getApplicationCommandPermissions.ts b/helpers/interactions/commands/getApplicationCommandPermissions.ts index 6ab44fcab..08d73e6cb 100644 --- a/helpers/interactions/commands/getApplicationCommandPermissions.ts +++ b/helpers/interactions/commands/getApplicationCommandPermissions.ts @@ -3,7 +3,15 @@ import { ApplicationCommandPermission } from "../../../transformers/applicationC import { DiscordGuildApplicationCommandPermissions } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -/** Fetches command permissions for all commands for your application in a guild. Returns an array of GuildApplicationCommandPermissions objects. */ +/** + * Gets the permissions of all application commands registered in a guild by the ID of the guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the permissions objects of. + * @returns A collection of {@link ApplicationCommandPermission} objects assorted by command ID. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions} + */ export async function getApplicationCommandPermissions( bot: Bot, guildId: bigint, diff --git a/helpers/interactions/commands/getGlobalApplicationCommand.ts b/helpers/interactions/commands/getGlobalApplicationCommand.ts new file mode 100644 index 000000000..025a32b4e --- /dev/null +++ b/helpers/interactions/commands/getGlobalApplicationCommand.ts @@ -0,0 +1,22 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { DiscordApplicationCommand } from "../../../types/discord.ts"; + +/** + * Gets a global application command by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param commandId - The ID of the command to get. + * @returns An instance of {@link ApplicationCommand}. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-global-application-command} + */ +export async function getGlobalApplicationCommand(bot: Bot, commandId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.COMMANDS_ID(bot.applicationId, commandId), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/getGlobalApplicationCommands.ts b/helpers/interactions/commands/getGlobalApplicationCommands.ts new file mode 100644 index 000000000..c3c07ddce --- /dev/null +++ b/helpers/interactions/commands/getGlobalApplicationCommands.ts @@ -0,0 +1,29 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { DiscordApplicationCommand } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; + +// TODO: Implement `with_localizations` options field. + +/** + * Gets the list of your bot's global application commands. + * + * @param bot - The bot instance to use to make the request. + * @returns A collection of {@link ApplicationCommand} objects assorted by command ID. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands} + */ +export async function getGlobalApplicationCommands(bot: Bot): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.COMMANDS(bot.applicationId), + ); + + return new Collection( + results.map((result) => { + const command = bot.transformers.applicationCommand(bot, result); + return [command.id, command]; + }), + ); +} diff --git a/helpers/interactions/commands/getGuildApplicationCommand.ts b/helpers/interactions/commands/getGuildApplicationCommand.ts new file mode 100644 index 000000000..4b789b9cb --- /dev/null +++ b/helpers/interactions/commands/getGuildApplicationCommand.ts @@ -0,0 +1,29 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { DiscordApplicationCommand } from "../../../types/discord.ts"; + +// TODO: Swap `commandId` and `guildId` parameters. + +/** + * Gets a guild application command by its ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the command is registered in. + * @param commandId - The ID of the command to get. + * @returns An instance of {@link ApplicationCommand}. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command} + */ +export async function getGuildApplicationCommand( + bot: Bot, + commandId: bigint, + guildId: bigint, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId), + ); + + return bot.transformers.applicationCommand(bot, result); +} diff --git a/helpers/interactions/commands/getApplicationCommands.ts b/helpers/interactions/commands/getGuildApplicationCommands.ts similarity index 51% rename from helpers/interactions/commands/getApplicationCommands.ts rename to helpers/interactions/commands/getGuildApplicationCommands.ts index 821e0be07..dea95bcc7 100644 --- a/helpers/interactions/commands/getApplicationCommands.ts +++ b/helpers/interactions/commands/getGuildApplicationCommands.ts @@ -3,17 +3,23 @@ import { ApplicationCommand } from "../../../transformers/applicationCommand.ts" import { DiscordApplicationCommand } from "../../../types/discord.ts"; import { Collection } from "../../../util/collection.ts"; -/** Fetch all the commands for your application. If a guild id is not provided, it will fetch global commands. */ -export async function getApplicationCommands( +/** + * Gets the list of application commands registered by your bot in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the commands are registered in. + * @returns A collection of {@link ApplicationCommand} objects assorted by command ID. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#get-global-application-commandss} + */ +export async function getGuildApplicationCommands( bot: Bot, - guildId?: bigint, + guildId: bigint, ): Promise> { const results = await bot.rest.runMethod( bot.rest, "GET", - guildId - ? bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId) - : bot.constants.routes.COMMANDS(bot.applicationId), + bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId), ); return new Collection( diff --git a/helpers/interactions/commands/mod.ts b/helpers/interactions/commands/mod.ts index 7d69c7e94..c0a9c4e68 100644 --- a/helpers/interactions/commands/mod.ts +++ b/helpers/interactions/commands/mod.ts @@ -1,11 +1,15 @@ -export * from "./createApplicationCommand.ts"; -export * from "./deleteApplicationCommand.ts"; -export * from "./deleteInteractionResponse.ts"; +export * from "./createGlobalApplicationCommand.ts"; +export * from "./createGuildApplicationCommand.ts"; +export * from "./deleteGlobalApplicationCommand.ts"; +export * from "./deleteGuildApplicationCommand.ts"; export * from "./editApplicationCommandPermissions.ts"; -export * from "./editInteractionResponse.ts"; -export * from "./getApplicationCommand.ts"; +export * from "./editGlobalApplicationCommand.ts"; +export * from "./editGuildApplicationCommand.ts"; export * from "./getApplicationCommandPermission.ts"; export * from "./getApplicationCommandPermissions.ts"; -export * from "./getApplicationCommands.ts"; -export * from "./upsertApplicationCommand.ts"; -export * from "./upsertApplicationCommands.ts"; +export * from "./getGlobalApplicationCommand.ts"; +export * from "./getGlobalApplicationCommands.ts"; +export * from "./getGuildApplicationCommand.ts"; +export * from "./getGuildApplicationCommands.ts"; +export * from "./upsertGlobalApplicationCommands.ts"; +export * from "./upsertGuildApplicationCommands.ts"; diff --git a/helpers/interactions/commands/upsertApplicationCommand.ts b/helpers/interactions/commands/upsertApplicationCommand.ts deleted file mode 100644 index 5913d437f..000000000 --- a/helpers/interactions/commands/upsertApplicationCommand.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; -import { DiscordApplicationCommand } from "../../../types/discord.ts"; -import { AtLeastOne } from "../../../types/shared.ts"; -import { - CreateApplicationCommand, - CreateContextApplicationCommand, - isContextApplicationCommand, - makeOptionsForCommand, -} from "./createApplicationCommand.ts"; - -/** - * Edit an existing application command. If this command did not exist, it will create it. - */ -export async function upsertApplicationCommand( - bot: Bot, - commandId: bigint, - command: AtLeastOne | AtLeastOne, - guildId?: bigint, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "PATCH", - guildId - ? bot.constants.routes.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId) - : bot.constants.routes.COMMANDS_ID(bot.applicationId, commandId), - isContextApplicationCommand(command) - ? { - name: command.name, - name_localizations: command.nameLocalizations, - type: command.type, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - } - : { - name: command.name, - name_localizations: command.nameLocalizations, - description: command.description, - description_localizations: command.descriptionLocalizations, - type: command.type, - options: command.options ? makeOptionsForCommand(command.options) : undefined, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - }, - ); - - return bot.transformers.applicationCommand(bot, result); -} diff --git a/helpers/interactions/commands/upsertApplicationCommands.ts b/helpers/interactions/commands/upsertApplicationCommands.ts deleted file mode 100644 index a24c76105..000000000 --- a/helpers/interactions/commands/upsertApplicationCommands.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { Bot } from "../../../bot.ts"; -import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; -import { DiscordApplicationCommand } from "../../../types/discord.ts"; -import { Collection } from "../../../util/collection.ts"; -import { - CreateApplicationCommand, - CreateContextApplicationCommand, - isContextApplicationCommand, - makeOptionsForCommand, -} from "./createApplicationCommand.ts"; - -/** - * Bulk edit existing application commands. If a command does not exist, it will create it. - * - * **NOTE:** Any application commands that are not specified in this function will be **deleted**. If you don't provide the commandId and rename your command, the command gets a new Id. - */ -export async function upsertApplicationCommands( - bot: Bot, - commands: (UpsertApplicationCommands | CreateContextApplicationCommand)[], - guildId?: bigint, -): Promise> { - const results = await bot.rest.runMethod( - bot.rest, - "PUT", - guildId - ? bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId) - : bot.constants.routes.COMMANDS(bot.applicationId), - commands.map((command) => (isContextApplicationCommand(command) - ? { - name: command.name, - name_localizations: command.nameLocalizations, - type: command.type, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - } - : { - name: command.name, - name_localizations: command.nameLocalizations, - description: command.description, - description_localizations: command.descriptionLocalizations, - type: command.type, - options: command.options ? makeOptionsForCommand(command.options) : undefined, - default_member_permissions: command.defaultMemberPermissions - ? bot.utils.calculateBits(command.defaultMemberPermissions) - : undefined, - dm_permission: command.dmPermission, - }) - ), - ); - - return new Collection( - results.map((result) => { - const command = bot.transformers.applicationCommand(bot, result); - return [command.id, command]; - }), - ); -} - -export interface UpsertApplicationCommands extends CreateApplicationCommand { - /** ID of the command, if known */ - id?: bigint; -} diff --git a/helpers/interactions/commands/upsertGlobalApplicationCommands.ts b/helpers/interactions/commands/upsertGlobalApplicationCommands.ts new file mode 100644 index 000000000..bde1c4b8e --- /dev/null +++ b/helpers/interactions/commands/upsertGlobalApplicationCommands.ts @@ -0,0 +1,37 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { CreateApplicationCommand, DiscordApplicationCommand } from "../../../types/mod.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** + * Re-registers the list of global application commands, overwriting the previous commands completely. + * + * @param bot - The bot instance to use to make the request. + * @param commands - The list of commands to use to overwrite the previous list. + * @returns A collection of {@link ApplicationCommand} objects assorted by command ID. + * + * @remarks + * ❗ Commands that are not present in the `commands` array will be __deleted__. + * + * ⚠️ Commands that do not already exist will count towards the daily limit of _200_ new commands. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands} + */ +export async function upsertGlobalApplicationCommands( + bot: Bot, + commands: CreateApplicationCommand[], +): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.COMMANDS(bot.applicationId), + commands.map((command) => bot.transformers.reverse.createApplicationCommand(bot, command)), + ); + + return new Collection( + results.map((result) => { + const command = bot.transformers.applicationCommand(bot, result); + return [command.id, command]; + }), + ); +} diff --git a/helpers/interactions/commands/upsertGuildApplicationCommands.ts b/helpers/interactions/commands/upsertGuildApplicationCommands.ts new file mode 100644 index 000000000..08a93f6fc --- /dev/null +++ b/helpers/interactions/commands/upsertGuildApplicationCommands.ts @@ -0,0 +1,39 @@ +import type { Bot } from "../../../bot.ts"; +import { ApplicationCommand } from "../../../transformers/applicationCommand.ts"; +import { CreateApplicationCommand, DiscordApplicationCommand } from "../../../types/mod.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** + * Re-registers the list of application commands registered in a guild, overwriting the previous commands completely. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild whose list of commands to overwrite. + * @param commands - The list of commands to use to overwrite the previous list. + * @returns A collection of {@link ApplicationCommand} objects assorted by command ID. + * + * @remarks + * ❗ Commands that are not present in the `commands` array will be __deleted__. + * + * ⚠️ Commands that do not already exist will count towards the daily limit of _200_ new commands. + * + * @see {@link https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands} + */ +export async function upsertGuildApplicationCommands( + bot: Bot, + guildId: bigint, + commands: CreateApplicationCommand[], +): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.COMMANDS_GUILD(bot.applicationId, guildId), + commands.map((command) => bot.transformers.reverse.createApplicationCommand(bot, command)), + ); + + return new Collection( + results.map((result) => { + const command = bot.transformers.applicationCommand(bot, result); + return [command.id, command]; + }), + ); +} diff --git a/helpers/interactions/followups/deleteFollowupMessage.ts b/helpers/interactions/followups/deleteFollowupMessage.ts deleted file mode 100644 index f4b38c9c3..000000000 --- a/helpers/interactions/followups/deleteFollowupMessage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Bot } from "../../../bot.ts"; - -/** Deletes a followup message for an Interaction. Functions the same as delete webhook message, however this uses your interaction token instead of bot token. Does not support ephemeral followups. */ -export async function deleteFollowupMessage(bot: Bot, interactionToken: string, messageId: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - bot.constants.routes.WEBHOOK_MESSAGE(bot.applicationId, interactionToken, messageId), - ); -} diff --git a/helpers/interactions/followups/editFollowupMessage.ts b/helpers/interactions/followups/editFollowupMessage.ts deleted file mode 100644 index e31927b49..000000000 --- a/helpers/interactions/followups/editFollowupMessage.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Bot } from "../../../bot.ts"; -import { Message } from "../../../transformers/message.ts"; -import { DiscordMessage } from "../../../types/discord.ts"; -import { EditWebhookMessage } from "../../webhooks/editWebhookMessage.ts"; - -/** Edits a followup message for an Interaction. Functions the same as edit webhook message, however this uses your interaction token instead of bot token. Does not support ephemeral followups. */ -export async function editFollowupMessage( - bot: Bot, - interactionToken: string, - messageId: bigint, - options: EditWebhookMessage, -): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "PATCH", - bot.constants.routes.WEBHOOK_MESSAGE(bot.applicationId, interactionToken, messageId), - { - content: options.content, - embeds: options.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), - file: options.file, - allowed_mentions: options.allowedMentions - ? bot.transformers.reverse.allowedMentions(bot, options.allowedMentions) - : undefined, - attachments: options.attachments?.map((attachment) => bot.transformers.reverse.attachment(bot, attachment)), - components: options.components?.map((component) => bot.transformers.reverse.component(bot, component)), - message_id: messageId?.toString(), - }, - ); - - return bot.transformers.message(bot, result); -} diff --git a/helpers/interactions/followups/getFollowupMessage.ts b/helpers/interactions/followups/getFollowupMessage.ts deleted file mode 100644 index 8fdc7573f..000000000 --- a/helpers/interactions/followups/getFollowupMessage.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Bot } from "../../../bot.ts"; -import { Message } from "../../../transformers/message.ts"; -import { DiscordMessage } from "../../../types/discord.ts"; - -/** Returns a followup message for an Interaction. Functions the same as get webhook message, however this uses your interaction token instead of bot token. Does not support ephemeral followups. */ -export async function getFollowupMessage(bot: Bot, interactionToken: string, messageId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.WEBHOOK_MESSAGE(bot.applicationId, interactionToken, messageId), - ); - - return bot.transformers.message(bot, result); -} diff --git a/helpers/interactions/followups/mod.ts b/helpers/interactions/followups/mod.ts deleted file mode 100644 index 800ef2984..000000000 --- a/helpers/interactions/followups/mod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./deleteFollowupMessage.ts"; -export * from "./editFollowupMessage.ts"; -export * from "./getFollowupMessage.ts"; diff --git a/helpers/interactions/getOriginalInteractionResponse.ts b/helpers/interactions/getOriginalInteractionResponse.ts deleted file mode 100644 index 290417049..000000000 --- a/helpers/interactions/getOriginalInteractionResponse.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { Message } from "../../transformers/message.ts"; -import { DiscordMessage } from "../../types/discord.ts"; - -/** Returns the initial Interaction response. Functions the same as Get Webhook Message */ -export async function getOriginalInteractionResponse(bot: Bot, token: string): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "GET", - bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), - ); - - return bot.transformers.message(bot, result); -} diff --git a/helpers/interactions/mod.ts b/helpers/interactions/mod.ts index 8a4603a32..8532dd52e 100644 --- a/helpers/interactions/mod.ts +++ b/helpers/interactions/mod.ts @@ -1,6 +1,2 @@ export * from "./commands/mod.ts"; -export * from "./followups/mod.ts"; - -export * from "./getOriginalInteractionResponse.ts"; -export * from "./sendInteractionResponse.ts"; -export * from "./verifySignature.ts"; +export * from "./responses/mod.ts"; diff --git a/helpers/interactions/responses/deleteFollowupMessage.ts b/helpers/interactions/responses/deleteFollowupMessage.ts new file mode 100644 index 000000000..0884b2803 --- /dev/null +++ b/helpers/interactions/responses/deleteFollowupMessage.ts @@ -0,0 +1,25 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes a follow-up message to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @param messageId - The ID of the message to delete. + * + * @remarks + * Unlike `deleteMessage()`, this endpoint allows the bot user to act without needing to see the channel the message is in. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * Fires a _Message Delete_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#delete-followup-message} + */ +export async function deleteFollowupMessage(bot: Bot, token: string, messageId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.INTERACTION_ID_TOKEN_MESSAGE_ID(bot.applicationId, token, messageId), + ); +} diff --git a/helpers/interactions/responses/deleteOriginalInteractionResponse.ts b/helpers/interactions/responses/deleteOriginalInteractionResponse.ts new file mode 100644 index 000000000..854f6a64f --- /dev/null +++ b/helpers/interactions/responses/deleteOriginalInteractionResponse.ts @@ -0,0 +1,24 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes the initial message response to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * + * @remarks + * Unlike `deleteMessage()`, this endpoint allows the bot user to act without needing to see the channel the message is in. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * Fires a _Message Delete_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response} + */ +export async function deleteOriginalInteractionResponse(bot: Bot, token: string): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), + ); +} diff --git a/helpers/interactions/responses/editFollowupMessage.ts b/helpers/interactions/responses/editFollowupMessage.ts new file mode 100644 index 000000000..fae74090b --- /dev/null +++ b/helpers/interactions/responses/editFollowupMessage.ts @@ -0,0 +1,45 @@ +import type { Bot } from "../../../bot.ts"; +import { InteractionCallbackData } from "../../../mod.ts"; +import { Message } from "../../../transformers/message.ts"; +import { DiscordMessage } from "../../../types/discord.ts"; +import { InteractionResponseTypes } from "../../../types/shared.ts"; + +/** + * Edits a follow-up message to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @param messageId - The ID of the message to edit. + * @param options - The parameters for the edit of the message. + * @returns An instance of the edited {@link Message}. + * + * @remarks + * Unlike `editMessage()`, this endpoint allows the bot user to act without needing to see the channel the message is in. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * Fires a _Message Update_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message} + */ +export async function editFollowupMessage( + bot: Bot, + token: string, + messageId: bigint, + options: InteractionCallbackData, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.WEBHOOK_MESSAGE(bot.applicationId, token, messageId), + { + messageId, + ...bot.transformers.reverse.interactionResponse(bot, { + type: InteractionResponseTypes.UpdateMessage, + data: options, + }).data, + }, + ); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/interactions/responses/editOriginalInteractionResponse.ts b/helpers/interactions/responses/editOriginalInteractionResponse.ts new file mode 100644 index 000000000..7c87996bd --- /dev/null +++ b/helpers/interactions/responses/editOriginalInteractionResponse.ts @@ -0,0 +1,39 @@ +import type { Bot } from "../../../bot.ts"; +import { Message } from "../../../transformers/message.ts"; +import { DiscordMessage } from "../../../types/discord.ts"; +import { InteractionCallbackData, InteractionResponseTypes } from "../../../types/mod.ts"; + +/** + * Edits the initial message response to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @param options - The parameters for the edit of the response. + * @returns An instance of the edited {@link Message}. + * + * @remarks + * Unlike `editMessage()`, this endpoint allows the bot user to act without needing to see the channel the message is in. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * Fires a _Message Update_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response} + */ +export async function editOriginalInteractionResponse( + bot: Bot, + token: string, + options: InteractionCallbackData, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), + bot.transformers.reverse.interactionResponse(bot, { + type: InteractionResponseTypes.UpdateMessage, + data: options, + }).data, + ); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/interactions/responses/getFollowupMessage.ts b/helpers/interactions/responses/getFollowupMessage.ts new file mode 100644 index 000000000..eee455c46 --- /dev/null +++ b/helpers/interactions/responses/getFollowupMessage.ts @@ -0,0 +1,30 @@ +import type { Bot } from "../../../bot.ts"; +import { Message } from "../../../transformers/message.ts"; +import { DiscordMessage } from "../../../types/discord.ts"; + +/** + * Gets a follow-up message to an interaction by the ID of the message. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @param messageId - The ID of the message to get. + * @returns An instance of {@link Message}. + * + * @remarks + * Unlike `getMessage()`, this endpoint allows the bot user to act without: + * - Needing to be able to see the contents of the channel that the message is in. (`READ_MESSAGES` permission.) + * - Requiring the `MESSAGE_CONTENT` intent. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message} + */ +export async function getFollowupMessage(bot: Bot, token: string, messageId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.INTERACTION_ID_TOKEN_MESSAGE_ID(bot.applicationId, token, messageId), + ); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/interactions/responses/getOriginalInteractionResponse.ts b/helpers/interactions/responses/getOriginalInteractionResponse.ts new file mode 100644 index 000000000..6998a8164 --- /dev/null +++ b/helpers/interactions/responses/getOriginalInteractionResponse.ts @@ -0,0 +1,29 @@ +import type { Bot } from "../../../bot.ts"; +import { Message } from "../../../transformers/message.ts"; +import { DiscordMessage } from "../../../types/discord.ts"; + +/** + * Gets the initial message response to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @returns An instance of {@link Message}. + * + * @remarks + * Unlike `getMessage()`, this endpoint allows the bot user to act without: + * - Needing to be able to see the contents of the channel that the message is in. (`READ_MESSAGES` permission.) + * - Requiring the `MESSAGE_CONTENT` intent. + * + * Does not support ephemeral follow-up messages due to these being stateless. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response} + */ +export async function getOriginalInteractionResponse(bot: Bot, token: string): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.INTERACTION_ORIGINAL_ID_TOKEN(bot.applicationId, token), + ); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/interactions/responses/mod.ts b/helpers/interactions/responses/mod.ts new file mode 100644 index 000000000..62164bb99 --- /dev/null +++ b/helpers/interactions/responses/mod.ts @@ -0,0 +1,8 @@ +export * from "./deleteFollowupMessage.ts"; +export * from "./deleteOriginalInteractionResponse.ts"; +export * from "./editFollowupMessage.ts"; +export * from "./editOriginalInteractionResponse.ts"; +export * from "./getFollowupMessage.ts"; +export * from "./getOriginalInteractionResponse.ts"; +export * from "./sendFollowupMessage.ts"; +export * from "./sendInteractionResponse.ts"; diff --git a/helpers/interactions/responses/sendFollowupMessage.ts b/helpers/interactions/responses/sendFollowupMessage.ts new file mode 100644 index 000000000..a3fa0dbfc --- /dev/null +++ b/helpers/interactions/responses/sendFollowupMessage.ts @@ -0,0 +1,46 @@ +import type { Bot } from "../../../bot.ts"; +import { InteractionResponse, Message } from "../../../mod.ts"; +import { DiscordMessage } from "../../../types/discord.ts"; + +/** + * Sends a follow-up message to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param token - The interaction token to use, provided in the original interaction. + * @param options - The parameters for the creation of the message. + * @returns An instance of the created {@link Message}. + * + * @remarks + * ⚠️ Interaction tokens are only valid for _15 minutes_. + * + * By default, mentions are suppressed. To enable mentions, pass a mention object with the callback data. + * + * Unlike `sendMessage()`, this endpoint allows the bot user to act without: + * - Needing to be able to see the contents of the channel that the message is in. (`READ_MESSAGES` permission.) + * - Requiring the `MESSAGE_CONTENT` intent. + * + * Fires a _Message Create_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#create-followup-message} + */ +export async function sendFollowupMessage( + bot: Bot, + token: string, + options: InteractionResponse, +): Promise { + const result = await bot.rest.sendRequest(bot.rest, { + url: bot.constants.routes.WEBHOOK(bot.applicationId, token), + method: "POST", + payload: bot.rest.createRequestBody(bot.rest, { + method: "POST", + body: { + ...bot.transformers.reverse.interactionResponse(bot, options).data, + file: options.data?.file, + }, + // remove authorization header + headers: { Authorization: "" }, + }), + }); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/interactions/responses/sendInteractionResponse.ts b/helpers/interactions/responses/sendInteractionResponse.ts new file mode 100644 index 000000000..227660bf9 --- /dev/null +++ b/helpers/interactions/responses/sendInteractionResponse.ts @@ -0,0 +1,45 @@ +import type { Bot } from "../../../bot.ts"; +import { InteractionResponse } from "../../../types/discordeno.ts"; + +/** + * Sends a response to an interaction. + * + * @param bot - The bot instance to use to make the request. + * @param interactionId - The ID of the interaction to respond to. + * @param token - The interaction token to use, provided in the original interaction. + * @param options - The parameters for the creation of the message. + * @returns An instance of the created {@link Message}. + * + * @remarks + * ⚠️ Interaction tokens are only valid for _15 minutes_. + * + * By default, mentions are suppressed. To enable mentions, pass a mention object with the callback data. + * + * Unlike `sendMessage()`, this endpoint allows the bot user to act without: + * - Needing to be able to see the contents of the channel that the message is in. (`READ_MESSAGES` permission.) + * - Requiring the `MESSAGE_CONTENT` intent. + * + * Fires a _Message Create_ event. + * + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response} + */ +export async function sendInteractionResponse( + bot: Bot, + interactionId: bigint, + token: string, + options: InteractionResponse, +): Promise { + return await bot.rest.sendRequest(bot.rest, { + url: bot.constants.routes.INTERACTION_ID_TOKEN(interactionId, token), + method: "POST", + payload: bot.rest.createRequestBody(bot.rest, { + method: "POST", + body: { + ...bot.transformers.reverse.interactionResponse(bot, options), + file: options.data?.file, + }, + // Remove authorization header + headers: { Authorization: "" }, + }), + }); +} diff --git a/helpers/interactions/sendInteractionResponse.ts b/helpers/interactions/sendInteractionResponse.ts deleted file mode 100644 index ffce28f0b..000000000 --- a/helpers/interactions/sendInteractionResponse.ts +++ /dev/null @@ -1,108 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { Embed, Message } from "../../mod.ts"; -import { DiscordMessage } from "../../types/discord.ts"; -import { AllowedMentions, FileContent, MessageComponents } from "../../types/discordeno.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. - * Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages. - * - * NOTE: By default we will suppress mentions. To enable mentions, just pass any mentions object. - */ -export async function sendInteractionResponse( - bot: Bot, - id: bigint, - token: string, - options: InteractionResponse, -): Promise { - // If no mentions are provided, force disable mentions - if (!options.data?.allowedMentions) { - options.data = { ...options.data, allowedMentions: { parse: [] } }; - } - - // DRY code a little bit - const data = { - tts: options.data.tts, - title: options.data.title, - flags: options.data.flags, - content: options.data.content, - choices: options.data.choices, - custom_id: options.data.customId, - embeds: options.data.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), - allowed_mentions: bot.transformers.reverse.allowedMentions(bot, options.data.allowedMentions!), - components: options.data.components?.map((component) => bot.transformers.reverse.component(bot, component)), - }; - - // A reply has never been send - if (bot.cache.unrepliedInteractions.delete(id)) { - return await bot.rest.sendRequest(bot.rest, { - url: bot.constants.routes.INTERACTION_ID_TOKEN(id, token), - method: "POST", - payload: bot.rest.createRequestBody(bot.rest, { - method: "POST", - body: { type: options.type, data, file: options.data.file }, - headers: { - // remove authorization header - Authorization: "", - }, - }), - }); - } - - // If its already been executed, we need to send a followup response - const result = await bot.rest.sendRequest(bot.rest, { - url: bot.constants.routes.WEBHOOK(bot.applicationId, token), - method: "POST", - payload: bot.rest.createRequestBody(bot.rest, { - method: "POST", - body: { ...data, file: options.data.file }, - headers: { - // remove authorization header - Authorization: "", - }, - }), - }); - - return bot.transformers.message(bot, result); -} - -/** https://discord.com/developers/docs/interactions/slash-commands#interaction-response */ -export interface InteractionResponse { - /** The type of response */ - type: InteractionResponseTypes; - /** An optional response message */ - data?: InteractionApplicationCommandCallbackData; -} - -/** https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionapplicationcommandcallbackdata */ -export interface InteractionApplicationCommandCallbackData { - /** The message contents (up to 2000 characters) */ - content?: string; - /** True if this is a TTS message */ - tts?: boolean; - /** Embedded `rich` content (up to 6000 characters) */ - embeds?: Embed[]; - /** Allowed mentions for the message */ - allowedMentions?: AllowedMentions; - /** The contents of the file being sent */ - file?: FileContent | FileContent[]; - /** The customId you want to use for this modal response. */ - customId?: string; - /** The title you want to use for this modal response. */ - title?: string; - /** The components you would like to have sent in this message */ - components?: MessageComponents; - /** Message flags combined as a bit field (only SUPPRESS_EMBEDS and EPHEMERAL can be set) */ - flags?: number; - /** Autocomplete choices (max of 25 choices) */ - choices?: ApplicationCommandOptionChoice[]; -} - -/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */ -export interface ApplicationCommandOptionChoice { - /** 1-100 character choice name */ - name: string; - /** Value of the choice, up to 100 characters if string */ - value: string | number; -} diff --git a/helpers/invites/deleteInvite.ts b/helpers/invites/deleteInvite.ts deleted file mode 100644 index b0c148870..000000000 --- a/helpers/invites/deleteInvite.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Deletes an invite for the given code. Requires `MANAGE_CHANNELS` or `MANAGE_GUILD` permission */ -export async function deleteInvite(bot: Bot, inviteCode: string): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.INVITE(inviteCode)); -} diff --git a/helpers/members/banMember.ts b/helpers/members/banMember.ts index 693f7580c..5e9cf6de6 100644 --- a/helpers/members/banMember.ts +++ b/helpers/members/banMember.ts @@ -1,11 +1,25 @@ import type { Bot } from "../../bot.ts"; -/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */ -export async function banMember(bot: Bot, guildId: bigint, id: bigint, options?: CreateGuildBan): Promise { +/** + * Bans a user from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to ban the user from. + * @param userId - The ID of the user to ban from the guild. + * @param options - The parameters for the creation of the ban. + * + * @remarks + * Requires the `BAN_MEMBERS` permission. + * + * Fires a _Guild Ban Add_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#create-guild-ban} + */ +export async function banMember(bot: Bot, guildId: bigint, userId: bigint, options?: CreateGuildBan): Promise { return await bot.rest.runMethod( bot.rest, "PUT", - bot.constants.routes.GUILD_BAN(guildId, id), + bot.constants.routes.GUILD_BAN(guildId, userId), { delete_message_seconds: options?.deleteMessageSeconds, reason: options?.reason, diff --git a/helpers/members/editBotMember.ts b/helpers/members/editBotMember.ts new file mode 100644 index 000000000..8604e2789 --- /dev/null +++ b/helpers/members/editBotMember.ts @@ -0,0 +1,30 @@ +import type { Bot } from "../../bot.ts"; +import { DiscordMember, Member } from "../../mod.ts"; + +/** + * Edits the nickname of the bot user. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the nickname of the bot user in. + * @param options - The parameters for the edit of the nickname. + * @returns An instance of the edited {@link Member} + * + * @remarks + * Fires a _Guild Member Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-current-member} + */ +export async function editBotMember( + bot: Bot, + guildId: bigint, + options: { nick: string | null; reason?: string }, +): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "PATCH", + bot.constants.routes.USER_NICK(guildId), + options, + ); + + return bot.transformers.member(bot, result, guildId, bot.id); +} diff --git a/helpers/members/editBotNickname.ts b/helpers/members/editBotNickname.ts deleted file mode 100644 index 0aad431bf..000000000 --- a/helpers/members/editBotNickname.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Edit the nickname of the bot in this guild */ -export async function editBotNickname( - bot: Bot, - guildId: bigint, - options: { nick: string | null; reason?: string }, -): Promise { - const result = await bot.rest.runMethod<{ nick?: string }>( - bot.rest, - "PATCH", - bot.constants.routes.USER_NICK(guildId), - options, - ); - - if (!result?.nick) return; - - return result.nick; -} diff --git a/helpers/members/editMember.ts b/helpers/members/editMember.ts index 0a6ad6eff..f364891f6 100644 --- a/helpers/members/editMember.ts +++ b/helpers/members/editMember.ts @@ -2,17 +2,32 @@ import type { Bot } from "../../bot.ts"; import { Member } from "../../transformers/member.ts"; import { DiscordMemberWithUser } from "../../types/discord.ts"; -/** Edit the member */ +/** + * Edits a member's properties. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the member of. + * @param userId - The user ID of the member to edit. + * @param options - The parameters for the edit of the user. + * + * @remarks + * This endpoint requires various permissions depending on what is edited about the member. + * To find out the required permission to enact a change, read the documentation of this endpoint's {@link ModifyGuildMember | parameters}. + * + * Fires a _Guild Member Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-member} + */ export async function editMember( bot: Bot, guildId: bigint, - memberId: bigint, + userId: bigint, options: ModifyGuildMember, ): Promise { const result = await bot.rest.runMethod( bot.rest, "PATCH", - bot.constants.routes.GUILD_MEMBER(guildId, memberId), + bot.constants.routes.GUILD_MEMBER(guildId, userId), { nick: options.nick, roles: options.roles?.map((id) => id.toString()), @@ -25,7 +40,7 @@ export async function editMember( }, ); - return bot.transformers.member(bot, result, guildId, memberId); + return bot.transformers.member(bot, result, guildId, userId); } /** https://discord.com/developers/docs/resources/guild#modify-guild-member */ diff --git a/helpers/members/fetchMembers.ts b/helpers/members/fetchMembers.ts index c52b95fa1..f33a7451e 100644 --- a/helpers/members/fetchMembers.ts +++ b/helpers/members/fetchMembers.ts @@ -3,9 +3,28 @@ 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. - * REST: 50/s global(across all shards) rate limit with ALL requests this included - * GW(this function): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is now 960/m. + * Fetches the list of members for a guild over the gateway. + * + * @param bot - The bot instance to use to make the requests. + * @param guildId - The ID of the guild to get the list of members for. + * @param options - The parameters for the fetching of the members. + * + * @remarks + * If requesting the entire member list: + * - Requires the `GUILD_MEMBERS` intent. + * + * If requesting presences ({@link RequestGuildMembers.presences | presences} set to `true`): + * - Requires the `GUILD_PRESENCES` intent. + * + * If requesting a prefix ({@link RequestGuildMembers.query | query} non-`undefined`): + * - Returns a maximum of 100 members. + * + * If requesting a users by ID ({@link RequestGuildMembers.userIds | userIds} non-`undefined`): + * - Returns a maximum of 100 members. + * + * Fires a _Guild Members Chunk_ gateway event for every 1000 members fetched. + * + * @see {@link https://discord.com/developers/docs/topics/gateway#request-guild-members} */ export function fetchMembers( bot: Bot, diff --git a/helpers/members/getAvatarUrl.ts b/helpers/members/getAvatarUrl.ts index f797311c4..7f003fab2 100644 --- a/helpers/members/getAvatarUrl.ts +++ b/helpers/members/getAvatarUrl.ts @@ -1,6 +1,14 @@ import type { Bot } from "../../bot.ts"; -/** The users custom avatar or the default avatar if you don't have a member object. */ +/** + * Builds a URL to a user's avatar stored in the Discord CDN. + * + * @param bot - The bot instance to use to build the URL. + * @param userId - The ID of the user to get the avatar of. + * @param discriminator - The user's discriminator. (4-digit tag after the hashtag.) + * @param options - The parameters for the building of the URL. + * @returns The link to the resource. + */ export function getAvatarURL( bot: Bot, userId: bigint, diff --git a/helpers/members/getDmChannel.ts b/helpers/members/getDmChannel.ts index 7c5deeea3..59538931d 100644 --- a/helpers/members/getDmChannel.ts +++ b/helpers/members/getDmChannel.ts @@ -2,7 +2,15 @@ import type { Bot } from "../../bot.ts"; import { Channel } from "../../transformers/channel.ts"; import { DiscordChannel } from "../../types/discord.ts"; -/** Get a user's dm channel. This is required in order to send a DM. */ +/** + * Gets or creates a DM channel with a user. + * + * @param bot - The bot instance to use to make the request. + * @param userId - The ID of the user to create the DM channel with. + * @returns An instance of {@link Channel}. + * + * @see {@link https://discord.com/developers/docs/resources/user#create-dm} + */ export async function getDmChannel(bot: Bot, userId: bigint): Promise { if (userId === bot.id) throw new Error(bot.constants.Errors.YOU_CAN_NOT_DM_THE_BOT_ITSELF); diff --git a/helpers/members/getMember.ts b/helpers/members/getMember.ts index 91420cb1a..a2ce45d83 100644 --- a/helpers/members/getMember.ts +++ b/helpers/members/getMember.ts @@ -2,13 +2,22 @@ import type { Bot } from "../../bot.ts"; import { Member } from "../../transformers/member.ts"; import { DiscordMemberWithUser } from "../../types/discord.ts"; -/** Returns a guild member object for the specified user. */ -export async function getMember(bot: Bot, guildId: bigint, id: bigint): Promise { +/** + * Gets the member object by user ID. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the member object for. + * @param userId - The ID of the user to get the member object for. + * @returns An instance of {@link Member}. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-member} + */ +export async function getMember(bot: Bot, guildId: bigint, userId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, "GET", - bot.constants.routes.GUILD_MEMBER(guildId, id), + bot.constants.routes.GUILD_MEMBER(guildId, userId), ); - return bot.transformers.member(bot, result, guildId, id); + return bot.transformers.member(bot, result, guildId, userId); } diff --git a/helpers/members/getMembers.ts b/helpers/members/getMembers.ts index 1d6d79767..c3d5511da 100644 --- a/helpers/members/getMembers.ts +++ b/helpers/members/getMembers.ts @@ -4,10 +4,25 @@ import { DiscordMemberWithUser } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; // TODO: make options optional + /** - * Highly recommended to **NOT** use this function to get members instead use fetchMembers(). - * REST(this function): 50/s global(across all shards) rate limit with ALL requests this included - * GW(fetchMembers): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is 960/m. + * Gets the list of members for a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the list of members for. + * @param options - The parameters for the fetching of the members. + * @returns A collection of {@link Member} objects assorted by user ID. + * + * @remarks + * Requires the `GUILD_MEMBERS` intent. + * + * ⚠️ It is not recommended to use this endpoint with very large bots. Instead, opt to use `fetchMembers()`: + * REST communication only permits 50 requests to be made per second, while gateways allow for up to 120 requests + * per minute per shard. For more information, read {@link https://discord.com/developers/docs/topics/rate-limits#rate-limits}. + * + * @see {@link https://discord.com/developers/docs/resources/guild#list-guild-members} + * @see {@link https://discord.com/developers/docs/topics/gateway#request-guild-members} + * @see {@link https://discord.com/developers/docs/topics/rate-limits#rate-limits} */ export async function getMembers( bot: Bot, diff --git a/helpers/members/kickMember.ts b/helpers/members/kickMember.ts index 3c9232bbd..4266037e6 100644 --- a/helpers/members/kickMember.ts +++ b/helpers/members/kickMember.ts @@ -1,11 +1,24 @@ import { Bot } from "../../bot.ts"; -/** Kick a member from the server */ -export async function kickMember(bot: Bot, guildId: bigint, memberId: bigint, reason?: string): Promise { +/** + * Kicks a member from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to kick the member from. + * @param userId - The user ID of the member to kick from the guild. + * + * @remarks + * Requires the `KICK_MEMBERS` permission. + * + * Fires a _Guild Member Remove_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#remove-guild-member} + */ +export async function kickMember(bot: Bot, guildId: bigint, userId: bigint, reason?: string): Promise { return await bot.rest.runMethod( bot.rest, "DELETE", - bot.constants.routes.GUILD_MEMBER(guildId, memberId), + bot.constants.routes.GUILD_MEMBER(guildId, userId), { reason, }, diff --git a/helpers/members/mod.ts b/helpers/members/mod.ts index 65a43adcc..8a523b2c3 100644 --- a/helpers/members/mod.ts +++ b/helpers/members/mod.ts @@ -1,5 +1,5 @@ export * from "./banMember.ts"; -export * from "./editBotNickname.ts"; +export * from "./editBotMember.ts"; export * from "./editMember.ts"; export * from "./fetchMembers.ts"; export * from "./getAvatarUrl.ts"; diff --git a/helpers/members/pruneMembers.ts b/helpers/members/pruneMembers.ts index 580ff359b..d07d5ffdb 100644 --- a/helpers/members/pruneMembers.ts +++ b/helpers/members/pruneMembers.ts @@ -1,15 +1,29 @@ import type { Bot } from "../../bot.ts"; /** - * Begin a prune operation. Requires the KICK_MEMBERS permission. Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation. For large guilds it's recommended to set the computePruneCount option to false, forcing 'pruned' to null. Fires multiple Guild Member Remove Gateway events. + * Initiates the process of pruning inactive members. * - * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the roles (resolved to include_roles internally) parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to prune the members of. + * @param options - The parameters for the pruning of members. + * @returns A number indicating how many members were pruned. + * + * @remarks + * Requires the `KICK_MEMBERS` permission. + * + * ❗ Requests to this endpoint will time out for large guilds. To prevent this from happening, set the {@link BeginGuildPrune.computePruneCount} property of the {@link options} object parameter to `false`. This will begin the process of pruning, and immediately return `undefined`, rather than wait for the process to complete before returning the actual count of members that have been kicked. + * + * ⚠️ By default, this process will not remove members with a role. To include the members who have a _particular subset of roles_, specify the role(s) in the {@link BeginGuildPrune.includeRoles | includeRoles} property of the {@link options} object parameter. + * + * Fires a _Guild Member Remove_ gateway event for every member kicked. + * + * @see {@link https://discord.com/developers/docs/resources/guild#begin-guild-prune} */ -export async function pruneMembers(bot: Bot, guildId: bigint, options: BeginGuildPrune): Promise { +export async function pruneMembers(bot: Bot, guildId: bigint, options: BeginGuildPrune): Promise { if (options.days && options.days < 1) throw new Error(bot.constants.Errors.PRUNE_MIN_DAYS); if (options.days && options.days > 30) throw new Error(bot.constants.Errors.PRUNE_MAX_DAYS); - const result = await bot.rest.runMethod<{ pruned: number }>( + const result = await bot.rest.runMethod<{ pruned: number | null }>( bot.rest, "POST", bot.constants.routes.GUILD_PRUNE(guildId), @@ -20,7 +34,7 @@ export async function pruneMembers(bot: Bot, guildId: bigint, options: BeginGuil }, ); - return result.pruned; + return result.pruned ?? undefined; } /** https://discord.com/developers/docs/resources/guild#begin-guild-prune */ diff --git a/helpers/members/searchMembers.ts b/helpers/members/searchMembers.ts index da252c83b..e355ef675 100644 --- a/helpers/members/searchMembers.ts +++ b/helpers/members/searchMembers.ts @@ -6,7 +6,15 @@ import { Member } from "../../transformers/member.ts"; import { Collection } from "../../util/collection.ts"; /** - * Query string to match username(s) and nickname(s) against + * Gets the list of members whose usernames or nicknames start with a provided string. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to search in. + * @param query - The string to match usernames or nicknames against. + * @param options - The parameters for searching through the members. + * @returns A collection of {@link Member} objects assorted by user ID. + * + * @see {@link https://discord.com/developers/docs/resources/guild#search-guild-members} */ export async function searchMembers( bot: Bot, diff --git a/helpers/members/unbanMember.ts b/helpers/members/unbanMember.ts index cb5a4ef33..5075a8fee 100644 --- a/helpers/members/unbanMember.ts +++ b/helpers/members/unbanMember.ts @@ -1,6 +1,19 @@ import type { Bot } from "../../bot.ts"; -/** Remove the ban for a user. Requires BAN_MEMBERS permission */ -export async function unbanMember(bot: Bot, guildId: bigint, id: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_BAN(guildId, id)); +/** + * Unbans a user from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to unban the user in. + * @param userId - The ID of the user to unban. + * + * @remarks + * Requires the `BAN_MEMBERS` permission. + * + * Fires a _Guild Ban Remove_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#remove-guild-ban} + */ +export async function unbanMember(bot: Bot, guildId: bigint, userId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_BAN(guildId, userId)); } diff --git a/helpers/messages/addReaction.ts b/helpers/messages/addReaction.ts deleted file mode 100644 index 0b9cd6a6b..000000000 --- a/helpers/messages/addReaction.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Create a reaction for the message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */ -export async function addReaction(bot: Bot, channelId: bigint, messageId: bigint, reaction: string): Promise { - if (reaction.startsWith("<:")) { - reaction = reaction.substring(2, reaction.length - 1); - } else if (reaction.startsWith("( - bot.rest, - "PUT", - bot.constants.routes.CHANNEL_MESSAGE_REACTION_ME(channelId, messageId, reaction), - {}, - ); -} diff --git a/helpers/messages/addReactions.ts b/helpers/messages/addReactions.ts deleted file mode 100644 index 6b23f8c27..000000000 --- a/helpers/messages/addReactions.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Adds multiple reactions to a message. If `ordered` is true(default is false), it will add the reactions one at a time in the order provided. Note: Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */ -export async function addReactions( - bot: Bot, - channelId: bigint, - messageId: bigint, - reactions: string[], - ordered = false, -): Promise { - if (!ordered) { - return void await Promise.all(reactions.map((reaction) => bot.helpers.addReaction(channelId, messageId, reaction))); - } - - for (const reaction of reactions) { - await bot.helpers.addReaction(channelId, messageId, reaction); - } -} diff --git a/helpers/messages/crosspostMessage.ts b/helpers/messages/crosspostMessage.ts new file mode 100644 index 000000000..8355e89d7 --- /dev/null +++ b/helpers/messages/crosspostMessage.ts @@ -0,0 +1,33 @@ +import type { Bot } from "../../bot.ts"; +import { Message } from "../../transformers/message.ts"; +import { DiscordMessage } from "../../types/discord.ts"; + +export const publishMessage = crosspostMessage; + +/** + * Cross-posts a message posted in an announcement channel to subscribed channels. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the announcement channel. + * @param messageId - The ID of the message to cross-post. + * @returns An instance of the cross-posted {@link Message}. + * + * @remarks + * Requires the `SEND_MESSAGES` permission. + * + * If not cross-posting own message: + * - Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Message Create_ event in the guilds the subscribed channels are in. + * + * @see {@link https://discord.com/developers/docs/resources/channel#crosspost-message} + */ +export async function crosspostMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { + const result = await bot.rest.runMethod( + bot.rest, + "POST", + bot.constants.routes.CHANNEL_MESSAGE_CROSSPOST(channelId, messageId), + ); + + return bot.transformers.message(bot, result); +} diff --git a/helpers/messages/deleteAllReactions.ts b/helpers/messages/deleteAllReactions.ts deleted file mode 100644 index 709df67af..000000000 --- a/helpers/messages/deleteAllReactions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Deletes all reactions for all emojis on this message. */ -export async function deleteAllReactions(bot: Bot, channelId: bigint, messageId: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "DELETE", - bot.constants.routes.CHANNEL_MESSAGE_REACTIONS(channelId, messageId), - ); -} diff --git a/helpers/messages/deleteMessage.ts b/helpers/messages/deleteMessage.ts index 6b58cc32f..5144d17be 100644 --- a/helpers/messages/deleteMessage.ts +++ b/helpers/messages/deleteMessage.ts @@ -1,6 +1,22 @@ import type { Bot } from "../../bot.ts"; -/** Delete a message with the channel id and message id only. */ +// TODO: Remove `delayMilliseconds` parameter. + +/** + * Deletes a message from a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to delete the message from. + * @param messageId - The ID of the message to delete from the channel. + * + * @remarks + * If not deleting own message: + * - Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Message Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-message} + */ export async function deleteMessage( bot: Bot, channelId: bigint, diff --git a/helpers/messages/deleteMessages.ts b/helpers/messages/deleteMessages.ts index 1e9ab21e7..972e58749 100644 --- a/helpers/messages/deleteMessages.ts +++ b/helpers/messages/deleteMessages.ts @@ -1,17 +1,37 @@ import type { Bot } from "../../bot.ts"; -/** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */ -export async function deleteMessages(bot: Bot, channelId: bigint, ids: bigint[], reason?: string): Promise { - if (ids.length < 2) { +/** + * Deletes multiple messages from a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to delete the messages from. + * @param messageIds - The IDs of the messages to delete from the channel. + * + * @remarks + * Requires the `MANAGE_MESSAGES` permission. + * + * ⚠️ Messages older than 2 weeks old cannot be deleted. + * + * Fires a _Message Delete Bulk_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#bulk-delete-messages} + */ +export async function deleteMessages( + bot: Bot, + channelId: bigint, + messageIds: bigint[], + reason?: string, +): Promise { + if (messageIds.length < 2) { throw new Error(bot.constants.Errors.DELETE_MESSAGES_MIN); } - if (ids.length > 100) { + if (messageIds.length > 100) { console.warn(`This endpoint only accepts a maximum of 100 messages. Using the first 100 message ids provided.`); } return await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.CHANNEL_BULK_DELETE(channelId), { - messages: ids.slice(0, 100).map((id) => id.toString()), + messages: messageIds.slice(0, 100).map((id) => id.toString()), reason, }); } diff --git a/helpers/messages/deleteReaction.ts b/helpers/messages/deleteReaction.ts deleted file mode 100644 index fc01ababa..000000000 --- a/helpers/messages/deleteReaction.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Deletes a reaction from the given user on this message, defaults to bot. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */ -export async function deleteReaction( - bot: Bot, - channelId: bigint, - messageId: bigint, - reaction: string, - options?: { userId?: bigint }, -): Promise { - if (reaction.startsWith("<:")) { - reaction = reaction.substring(2, reaction.length - 1); - } else if (reaction.startsWith("( - bot.rest, - "DELETE", - options?.userId - ? bot.constants.routes.CHANNEL_MESSAGE_REACTION_USER( - channelId, - messageId, - reaction, - options.userId, - ) - : bot.constants.routes.CHANNEL_MESSAGE_REACTION_ME(channelId, messageId, reaction), - ); -} diff --git a/helpers/messages/deleteReactionEmoji.ts b/helpers/messages/deleteReactionEmoji.ts deleted file mode 100644 index a3edd9a1b..000000000 --- a/helpers/messages/deleteReactionEmoji.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */ -import type { Bot } from "../../bot.ts"; - -export async function deleteReactionEmoji( - bot: Bot, - channelId: bigint, - messageId: bigint, - reaction: string, -): Promise { - if (reaction.startsWith("<:")) { - reaction = reaction.substring(2, reaction.length - 1); - } else if (reaction.startsWith("( - bot.rest, - "DELETE", - bot.constants.routes.CHANNEL_MESSAGE_REACTION(channelId, messageId, reaction), - ); -} diff --git a/helpers/messages/editMessage.ts b/helpers/messages/editMessage.ts index 4017313cc..76d0d09ea 100644 --- a/helpers/messages/editMessage.ts +++ b/helpers/messages/editMessage.ts @@ -5,26 +5,43 @@ import { Message } from "../../transformers/message.ts"; import { DiscordMessage } from "../../types/discord.ts"; import { AllowedMentions, FileContent, MessageComponents } from "../../types/discordeno.ts"; -/** Edit the message. */ +/** + * Edits a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to edit the message in. + * @param messageId - The IDs of the message to edit. + * @param options - The parameters for the edit of the message. + * @returns An instance of the edited {@link Message}. + * + * @remarks + * If editing another user's message: + * - Requires the `MANAGE_MESSAGES` permission. + * - Only the {@link EditMessage.flags | flags} property of the {@link options} object parameter can be edited. + * + * Fires a _Message Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#edit-message} + */ export async function editMessage( bot: Bot, channelId: bigint, messageId: bigint, - content: EditMessage, + options: EditMessage, ): Promise { const result = await bot.rest.runMethod( bot.rest, "PATCH", bot.constants.routes.CHANNEL_MESSAGE(channelId, messageId), { - content: content.content, - embeds: content.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), - allowed_mentions: content.allowedMentions - ? bot.transformers.reverse.allowedMentions(bot, content.allowedMentions) + content: options.content, + embeds: options.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), + allowed_mentions: options.allowedMentions + ? bot.transformers.reverse.allowedMentions(bot, options.allowedMentions) : undefined, - attachments: content.attachments?.map((attachment) => bot.transformers.reverse.attachment(bot, attachment)), - file: content.file, - components: content.components?.map((component) => bot.transformers.reverse.component(bot, component)), + attachments: options.attachments?.map((attachment) => bot.transformers.reverse.attachment(bot, attachment)), + file: options.file, + components: options.components?.map((component) => bot.transformers.reverse.component(bot, component)), }, ); diff --git a/helpers/messages/getMessage.ts b/helpers/messages/getMessage.ts index 68d610e1e..b10b34fea 100644 --- a/helpers/messages/getMessage.ts +++ b/helpers/messages/getMessage.ts @@ -2,12 +2,27 @@ import type { Bot } from "../../bot.ts"; import { Message } from "../../transformers/message.ts"; import { DiscordMessage } from "../../types/discord.ts"; -/** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ -export async function getMessage(bot: Bot, channelId: bigint, id: bigint): Promise { +/** + * Gets a message from a channel by the ID of the message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel from which to get the message. + * @param messageId - The ID of the message to get. + * @returns An instance of {@link Message}. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel in which the message was posted. + * + * If getting a message from a guild channel: + * - Requires the `READ_MESSAGE_HISTORY` permission. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-channel-message} + */ +export async function getMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { const result = await bot.rest.runMethod( bot.rest, "GET", - bot.constants.routes.CHANNEL_MESSAGE(channelId, id), + bot.constants.routes.CHANNEL_MESSAGE(channelId, messageId), ); return bot.transformers.message(bot, result); diff --git a/helpers/messages/getMessages.ts b/helpers/messages/getMessages.ts index 20f9a1736..758649ead 100644 --- a/helpers/messages/getMessages.ts +++ b/helpers/messages/getMessages.ts @@ -4,7 +4,22 @@ import { DiscordMessage } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; import { hasProperty } from "../../util/utils.ts"; -/** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ +/** + * Gets multiple messages from a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel from which to get the messages. + * @param options - The parameters for the fetching of the messages. + * @returns A collection of {@link Message} objects assorted by message ID. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel in which the messages were posted. + * + * If getting a messages from a guild channel: + * - Requires the `READ_MESSAGE_HISTORY` permission. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-channel-messages} + */ export async function getMessages( bot: Bot, channelId: bigint, diff --git a/helpers/messages/getPinnedMessages.ts b/helpers/messages/getPinnedMessages.ts new file mode 100644 index 000000000..ffa1f7be3 --- /dev/null +++ b/helpers/messages/getPinnedMessages.ts @@ -0,0 +1,34 @@ +import type { Bot } from "../../bot.ts"; +import { Message } from "../../transformers/message.ts"; +import { DiscordMessage } from "../../types/discord.ts"; +import { Collection } from "../../util/collection.ts"; + +/** + * Gets the pinned messages for a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to get the pinned messages for. + * @returns A collection of {@link Message} objects assorted by message ID. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel in which the messages were posted. + * + * If getting a message from a guild channel: + * - Requires the `READ_MESSAGE_HISTORY` permission. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-pinned-messages} + */ +export async function getPinnedMessages(bot: Bot, channelId: bigint): Promise> { + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.CHANNEL_PINS(channelId), + ); + + return new Collection( + results.map((result) => { + const message = bot.transformers.message(bot, result); + return [message.id, message]; + }), + ); +} diff --git a/helpers/messages/getReactions.ts b/helpers/messages/getReactions.ts deleted file mode 100644 index 05842f7ce..000000000 --- a/helpers/messages/getReactions.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { User } from "../../transformers/member.ts"; -import { DiscordUser } from "../../types/discord.ts"; -import { Collection } from "../../util/collection.ts"; - -/** Get a list of users that reacted with this emoji. */ -export async function getReactions( - bot: Bot, - channelId: bigint, - messageId: bigint, - reaction: string, - options?: GetReactions, -): Promise> { - if (reaction.startsWith("<:")) { - reaction = reaction.substring(2, reaction.length - 1); - } else if (reaction.startsWith("( - bot.rest, - "GET", - bot.constants.routes.CHANNEL_MESSAGE_REACTION(channelId, messageId, reaction, options), - ); - - return new Collection( - results.map((result) => { - const user = bot.transformers.user(bot, result); - return [user.id, user]; - }), - ); -} - -/** https://discord.com/developers/docs/resources/channel#get-reactions-query-string-params */ -export interface GetReactions { - /** Get users after this user Id */ - after?: string; - /** Max number of users to return (1-100) */ - limit?: number; -} diff --git a/helpers/messages/mod.ts b/helpers/messages/mod.ts index 7ea88126a..135e32882 100644 --- a/helpers/messages/mod.ts +++ b/helpers/messages/mod.ts @@ -1,15 +1,12 @@ -export * from "./addReaction.ts"; -export * from "./addReactions.ts"; -export * from "./deleteAllReactions.ts"; +export * from "./reactions/mod.ts"; + export * from "./deleteMessage.ts"; export * from "./deleteMessages.ts"; -export * from "./deleteReaction.ts"; -export * from "./deleteReactionEmoji.ts"; export * from "./editMessage.ts"; export * from "./getMessage.ts"; export * from "./getMessages.ts"; -export * from "./getReactions.ts"; +export * from "./getPinnedMessages.ts"; export * from "./pinMessage.ts"; -export * from "./publishMessage.ts"; +export * from "./crosspostMessage.ts"; export * from "./sendMessage.ts"; export * from "./unpinMessage.ts"; diff --git a/helpers/messages/pinMessage.ts b/helpers/messages/pinMessage.ts index dc02d0918..63b92086b 100644 --- a/helpers/messages/pinMessage.ts +++ b/helpers/messages/pinMessage.ts @@ -1,6 +1,23 @@ import type { Bot } from "../../bot.ts"; -/** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */ +/** + * Pins a message in a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel where the message is to be pinned. + * @param messageId - The ID of the message to pin. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel in which the messages were posted. + * + * Requires the `MANAGE_MESSAGES` permission. + * + * ⚠️ There can only be at max 50 messages pinned in a channel. + * + * Fires a _Channel Pins Update_ event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#pin-message} + */ export async function pinMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { return await bot.rest.runMethod(bot.rest, "PUT", bot.constants.routes.CHANNEL_PIN(channelId, messageId)); } diff --git a/helpers/messages/publishMessage.ts b/helpers/messages/publishMessage.ts deleted file mode 100644 index b987a0372..000000000 --- a/helpers/messages/publishMessage.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { Message } from "../../transformers/message.ts"; -import { DiscordMessage } from "../../types/discord.ts"; - -/** Crosspost a message in a News Channel to following channels. */ -export async function publishMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { - const result = await bot.rest.runMethod( - bot.rest, - "POST", - bot.constants.routes.CHANNEL_MESSAGE_CROSSPOST(channelId, messageId), - ); - - return bot.transformers.message(bot, result); -} diff --git a/helpers/messages/reactions/addReaction.ts b/helpers/messages/reactions/addReaction.ts new file mode 100644 index 000000000..9b261e9e4 --- /dev/null +++ b/helpers/messages/reactions/addReaction.ts @@ -0,0 +1,33 @@ +import type { Bot } from "../../../bot.ts"; +import { processReactionString } from "./getReactions.ts"; + +// TODO: Improve typing of the `reaction` parameter. + +/** + * Adds a reaction to a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to add a reaction to is in. + * @param messageId - The ID of the message to add a reaction to. + * @param reaction - The reaction to add to the message. + * @returns + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * If nobody else has reacted to the message: + * - Requires the `ADD_REACTIONS` permission. + * + * Fires a _Message Reaction Add_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#create-reaction} + */ +export async function addReaction(bot: Bot, channelId: bigint, messageId: bigint, reaction: string): Promise { + reaction = processReactionString(reaction); + + return await bot.rest.runMethod( + bot.rest, + "PUT", + bot.constants.routes.CHANNEL_MESSAGE_REACTION_ME(channelId, messageId, reaction), + ); +} diff --git a/helpers/messages/reactions/addReactions.ts b/helpers/messages/reactions/addReactions.ts new file mode 100644 index 000000000..36f43f927 --- /dev/null +++ b/helpers/messages/reactions/addReactions.ts @@ -0,0 +1,38 @@ +import type { Bot } from "../../../bot.ts"; + +// TODO: Improve typing of the `reactions` parameter. + +/** + * Adds multiple a reaction to a message. + * + * This function uses the `addReaction()` helper behind the scenes. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to add reactions to is in. + * @param messageId - The ID of the message to add the reactions to. + * @param reactions - The reactions to add to the message. + * @param ordered - Whether the reactions must be added in order or not. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * If nobody else has reacted to the message: + * - Requires the `ADD_REACTIONS` permission. + * + * Fires a _Message Reaction Add_ gateway event for every reaction added. + */ +export async function addReactions( + bot: Bot, + channelId: bigint, + messageId: bigint, + reactions: string[], + ordered = false, +): Promise { + if (!ordered) { + return void await Promise.all(reactions.map((reaction) => bot.helpers.addReaction(channelId, messageId, reaction))); + } + + for (const reaction of reactions) { + await bot.helpers.addReaction(channelId, messageId, reaction); + } +} diff --git a/helpers/messages/reactions/deleteReaction.ts b/helpers/messages/reactions/deleteReaction.ts new file mode 100644 index 000000000..1fa1f8463 --- /dev/null +++ b/helpers/messages/reactions/deleteReaction.ts @@ -0,0 +1,73 @@ +import type { Bot } from "../../../bot.ts"; +import { processReactionString } from "./getReactions.ts"; + +// TODO: Improve typing of the `reaction` parameter. + +/** + * Deletes a reaction added by the bot user from a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to delete the reaction from is in. + * @param messageId - The ID of the message to delete the reaction from. + * @param reaction - The reaction to delete from the message. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * Fires a _Message Reaction Remove_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-own-reaction} + */ +export async function deleteOwnReaction( + bot: Bot, + channelId: bigint, + messageId: bigint, + reaction: string, +): Promise { + reaction = processReactionString(reaction); + + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.CHANNEL_MESSAGE_REACTION_ME(channelId, messageId, reaction), + ); +} + +/** + * Deletes a user's reaction from a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to delete the reaction from is in. + * @param messageId - The ID of the message to delete the reaction from. + * @param userId - The ID of the user whose reaction to delete. + * @param reaction - The reaction to delete from the message. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Message Reaction Remove_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-user-reaction} + */ +export async function deleteUserReaction( + bot: Bot, + channelId: bigint, + messageId: bigint, + userId: bigint, + reaction: string, +): Promise { + reaction = processReactionString(reaction); + + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.CHANNEL_MESSAGE_REACTION_USER( + channelId, + messageId, + reaction, + userId, + ), + ); +} diff --git a/helpers/messages/reactions/deleteReactionsAll.ts b/helpers/messages/reactions/deleteReactionsAll.ts new file mode 100644 index 000000000..e184481a2 --- /dev/null +++ b/helpers/messages/reactions/deleteReactionsAll.ts @@ -0,0 +1,25 @@ +import type { Bot } from "../../../bot.ts"; + +/** + * Deletes all reactions for all emojis from a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to delete the reactions from is in. + * @param messageId - The ID of the message to delete the reactions from. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Message Reaction Remove All_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions} + */ +export async function deleteReactionsAll(bot: Bot, channelId: bigint, messageId: bigint): Promise { + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.CHANNEL_MESSAGE_REACTIONS(channelId, messageId), + ); +} diff --git a/helpers/messages/reactions/deleteReactionsEmoji.ts b/helpers/messages/reactions/deleteReactionsEmoji.ts new file mode 100644 index 000000000..4791fd2ea --- /dev/null +++ b/helpers/messages/reactions/deleteReactionsEmoji.ts @@ -0,0 +1,34 @@ +import type { Bot } from "../../../bot.ts"; +import { processReactionString } from "./getReactions.ts"; + +/** + * Deletes all reactions for an emoji from a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to delete the reactions from is in. + * @param messageId - The ID of the message to delete the reactions from. + * @param reaction - The reaction to remove from the message. + * + * @remarks + * Requires the `READ_MESSAGE_HISTORY` permission. + * + * Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Message Reaction Remove Emoji_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji} + */ +export async function deleteReactionsEmoji( + bot: Bot, + channelId: bigint, + messageId: bigint, + reaction: string, +): Promise { + reaction = processReactionString(reaction); + + return await bot.rest.runMethod( + bot.rest, + "DELETE", + bot.constants.routes.CHANNEL_MESSAGE_REACTION(channelId, messageId, reaction), + ); +} diff --git a/helpers/messages/reactions/getReactions.ts b/helpers/messages/reactions/getReactions.ts new file mode 100644 index 000000000..68ec81b9e --- /dev/null +++ b/helpers/messages/reactions/getReactions.ts @@ -0,0 +1,60 @@ +import type { Bot } from "../../../bot.ts"; +import { User } from "../../../transformers/member.ts"; +import { DiscordUser } from "../../../types/discord.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** Get a list of users that reacted with this emoji. */ +/** + * Gets the list of users that reacted with an emoji to a message. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel the message to get the users for is in. + * @param messageId - The ID of the message to get the users for. + * @param reaction - The reaction for which to get the users. + * @param options - The parameters for the fetching of the users. + * @returns A collection of {@link User} objects assorted by user ID. + * + * @see {@link https://discord.com/developers/docs/resources/channel#get-reactions} + */ +export async function getReactions( + bot: Bot, + channelId: bigint, + messageId: bigint, + reaction: string, + options?: GetReactions, +): Promise> { + reaction = processReactionString(reaction); + + const results = await bot.rest.runMethod( + bot.rest, + "GET", + bot.constants.routes.CHANNEL_MESSAGE_REACTION(channelId, messageId, encodeURIComponent(reaction), options), + ); + + return new Collection( + results.map((result) => { + const user = bot.transformers.user(bot, result); + return [user.id, user]; + }), + ); +} + +export function processReactionString(reaction: string): string { + if (reaction.startsWith("<:")) { + return reaction.substring(2, reaction.length - 1); + } + + if (reaction.startsWith(" { +/** + * Sends a message to a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel to send the message in. + * @param options - The parameters for the creation of the message. + * @returns An instance of the created {@link Message}. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel the message is to be sent in. + * + * If sending a message to a guild channel: + * - Requires the `SEND_MESSAGES` permission. + * + * If sending a TTS message: + * - Requires the `SEND_TTS_MESSAGES` permission. + * + * If sending a message as a reply to another message: + * - Requires the `READ_MESSAGE_HISTORY` permission. + * - The message being replied to cannot be a system message. + * + * ⚠️ The maximum size of a request (accounting for any attachments and message content) for bot users is _8 MiB_. + * + * Fires a _Message Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#create-message} + */ +export async function sendMessage(bot: Bot, channelId: bigint, options: CreateMessage): Promise { const result = await bot.rest.runMethod( bot.rest, "POST", bot.constants.routes.CHANNEL_MESSAGES(channelId), { - content: content.content, - tts: content.tts, - embeds: content.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), - allowed_mentions: content.allowedMentions + content: options.content, + tts: options.tts, + embeds: options.embeds?.map((embed) => bot.transformers.reverse.embed(bot, embed)), + allowed_mentions: options.allowedMentions ? { - parse: content.allowedMentions?.parse, - roles: content.allowedMentions?.roles?.map((id) => id.toString()), - users: content.allowedMentions?.users?.map((id) => id.toString()), - replied_user: content.allowedMentions?.repliedUser, + parse: options.allowedMentions?.parse, + roles: options.allowedMentions?.roles?.map((id) => id.toString()), + users: options.allowedMentions?.users?.map((id) => id.toString()), + replied_user: options.allowedMentions?.repliedUser, } : undefined, - file: content.file, - components: content.components?.map((component) => ({ + file: options.file, + components: options.components?.map((component) => ({ type: component.type, components: component.components.map((subComponent) => { if (subComponent.type === MessageComponentTypes.InputText) { @@ -80,13 +106,13 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: CreateMe }; }), })), - ...(content.messageReference?.messageId + ...(options.messageReference?.messageId ? { message_reference: { - message_id: content.messageReference.messageId.toString(), - channel_id: content.messageReference.channelId?.toString(), - guild_id: content.messageReference.guildId?.toString(), - fail_if_not_exists: content.messageReference.failIfNotExists === true, + message_id: options.messageReference.messageId.toString(), + channel_id: options.messageReference.channelId?.toString(), + guild_id: options.messageReference.guildId?.toString(), + fail_if_not_exists: options.messageReference.failIfNotExists === true, }, } : {}), diff --git a/helpers/messages/unpinMessage.ts b/helpers/messages/unpinMessage.ts index 3b556fbe4..bdf92f09a 100644 --- a/helpers/messages/unpinMessage.ts +++ b/helpers/messages/unpinMessage.ts @@ -1,6 +1,21 @@ -/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */ import type { Bot } from "../../bot.ts"; +/** + * Unpins a pinned message in a channel. + * + * @param bot - The bot instance to use to make the request. + * @param channelId - The ID of the channel where the message is pinned. + * @param messageId - The ID of the message to unpin. + * + * @remarks + * Requires that the bot user be able to see the contents of the channel in which the messages were posted. + * + * Requires the `MANAGE_MESSAGES` permission. + * + * Fires a _Channel Pins Update_ event. + * + * @see {@link https://discord.com/developers/docs/resources/channel#unpin-message} + */ export async function unpinMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { return await bot.rest.runMethod( bot.rest, diff --git a/helpers/oauth/getApplicationInfo.ts b/helpers/misc/getApplicationInfo.ts similarity index 100% rename from helpers/oauth/getApplicationInfo.ts rename to helpers/misc/getApplicationInfo.ts diff --git a/helpers/misc/mod.ts b/helpers/misc/mod.ts index 9eb9010b6..91f4f8d76 100644 --- a/helpers/misc/mod.ts +++ b/helpers/misc/mod.ts @@ -1,6 +1,7 @@ export * from "./editBotProfile.ts"; export * from "./editBotStatus.ts"; export * from "./editShardStatus.ts"; +export * from "./getApplicationInfo.ts"; export * from "./getGatewayBot.ts"; export * from "./getNitroStickerPacks.ts"; export * from "./getUser.ts"; diff --git a/helpers/mod.ts b/helpers/mod.ts index 0b76f079c..69e2dce9e 100644 --- a/helpers/mod.ts +++ b/helpers/mod.ts @@ -2,14 +2,10 @@ export * from "./channels/mod.ts"; export * from "./discovery/mod.ts"; export * from "./emojis/mod.ts"; export * from "./guilds/mod.ts"; -export * from "./integrations/mod.ts"; export * from "./interactions/mod.ts"; -export * from "./invites/mod.ts"; export * from "./members/mod.ts"; export * from "./messages/mod.ts"; export * from "./misc/mod.ts"; -export * from "./oauth/mod.ts"; export * from "./roles/mod.ts"; export * from "./templates/mod.ts"; -export * from "./voice/mod.ts"; export * from "./webhooks/mod.ts"; diff --git a/helpers/oauth/mod.ts b/helpers/oauth/mod.ts deleted file mode 100644 index 8bacb18f2..000000000 --- a/helpers/oauth/mod.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./getApplicationInfo.ts"; diff --git a/helpers/roles/addRole.ts b/helpers/roles/addRole.ts index a73596149..24ffbcf74 100644 --- a/helpers/roles/addRole.ts +++ b/helpers/roles/addRole.ts @@ -1,17 +1,31 @@ import type { Bot } from "../../bot.ts"; -/** Add a role to the member */ +/** + * Adds a role to a member. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the member to add the role to is in. + * @param userId - The user ID of the member to add the role to. + * @param roleId - The ID of the role to add to the member. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Member Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#add-guild-member-role} + */ export async function addRole( bot: Bot, guildId: bigint, - memberId: bigint, + userId: bigint, roleId: bigint, reason?: string, ): Promise { return await bot.rest.runMethod( bot.rest, "PUT", - bot.constants.routes.GUILD_MEMBER_ROLE(guildId, memberId, roleId), + bot.constants.routes.GUILD_MEMBER_ROLE(guildId, userId, roleId), { reason }, ); } diff --git a/helpers/roles/createRole.ts b/helpers/roles/createRole.ts index 42d28f9a0..712ce8eba 100644 --- a/helpers/roles/createRole.ts +++ b/helpers/roles/createRole.ts @@ -3,7 +3,21 @@ import { Role } from "../../transformers/role.ts"; import { DiscordRole } from "../../types/discord.ts"; import { PermissionStrings } from "../../types/shared.ts"; -/** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ +/** + * Creates a role in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to create the role in. + * @param options - The parameters for the creation of the role. + * @returns An instance of the created {@link Role}. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Role Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#create-guild-role} + */ export async function createRole(bot: Bot, guildId: bigint, options: CreateGuildRole, reason?: string): Promise { const result = await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.GUILD_ROLES(guildId), { name: options.name, diff --git a/helpers/roles/deleteRole.ts b/helpers/roles/deleteRole.ts index e2e38fb1c..cb41ea7ff 100644 --- a/helpers/roles/deleteRole.ts +++ b/helpers/roles/deleteRole.ts @@ -1,6 +1,19 @@ import type { Bot } from "../../bot.ts"; -/** Delete a guild role. Requires the MANAGE_ROLES permission. */ -export async function deleteRole(bot: Bot, guildId: bigint, id: bigint): Promise { - return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_ROLE(guildId, id)); +/** + * Deletes a role from a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to delete the role from. + * @param roleId - The ID of the role to delete. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Role Delete_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#delete-guild-role} + */ +export async function deleteRole(bot: Bot, guildId: bigint, roleId: bigint): Promise { + return await bot.rest.runMethod(bot.rest, "DELETE", bot.constants.routes.GUILD_ROLE(guildId, roleId)); } diff --git a/helpers/roles/editRole.ts b/helpers/roles/editRole.ts index 047864585..3f586cae5 100644 --- a/helpers/roles/editRole.ts +++ b/helpers/roles/editRole.ts @@ -3,12 +3,27 @@ import { Role } from "../../transformers/role.ts"; import { DiscordRole } from "../../types/discord.ts"; import { PermissionStrings } from "../../types/shared.ts"; -/** Edit a guild role. Requires the MANAGE_ROLES permission. */ -export async function editRole(bot: Bot, guildId: bigint, id: bigint, options: EditGuildRole): Promise { +/** + * Edits a role in a guild. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the role in. + * @param roleId - The ID of the role to edit. + * @param options - The parameters for the edit of the role. + * @returns An instance of the edited {@link Role}. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Role Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-role} + */ +export async function editRole(bot: Bot, guildId: bigint, roleId: bigint, options: EditGuildRole): Promise { const result = await bot.rest.runMethod( bot.rest, "PATCH", - bot.constants.routes.GUILD_ROLE(guildId, id), + bot.constants.routes.GUILD_ROLE(guildId, roleId), { name: options.name, color: options.color, diff --git a/helpers/roles/modifyRolePositions.ts b/helpers/roles/editRolePositions.ts similarity index 58% rename from helpers/roles/modifyRolePositions.ts rename to helpers/roles/editRolePositions.ts index 138564f03..749126719 100644 --- a/helpers/roles/modifyRolePositions.ts +++ b/helpers/roles/editRolePositions.ts @@ -3,7 +3,21 @@ import { Role } from "../../transformers/role.ts"; import { DiscordRole } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; -/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. Returns a list of all of the guild's role objects on success. Fires multiple Guild Role Update Gateway events. */ +/** + * Edits the positions of a set of roles. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to edit the role positions in. + * @param options - The parameters for the edit of the role positions. + * @returns A collection of {@link Role} objects assorted by role ID. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Role Update_ gateway event for every role impacted in this change. + * + * @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-role-positions} + */ export async function modifyRolePositions( bot: Bot, guildId: bigint, diff --git a/helpers/roles/getRoles.ts b/helpers/roles/getRoles.ts index d0ec84fc2..5b816bbf3 100644 --- a/helpers/roles/getRoles.ts +++ b/helpers/roles/getRoles.ts @@ -3,9 +3,17 @@ import { Role } from "../../transformers/role.ts"; import { DiscordRole } from "../../types/discord.ts"; import { Collection } from "../../util/collection.ts"; -/** Returns a list of role objects for the guild. +/** + * Gets the list of roles for a guild. * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your roles will be cached in your guild.** + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild to get the list of roles for. + * @returns A collection of {@link Role} objects assorted by role ID. + * + * @remarks + * ⚠️ This endpoint should be used sparingly due to {@link User} objects already being included in guild payloads. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-roles} */ export async function getRoles(bot: Bot, guildId: bigint): Promise> { const results = await bot.rest.runMethod(bot.rest, "GET", bot.constants.routes.GUILD_ROLES(guildId)); diff --git a/helpers/roles/mod.ts b/helpers/roles/mod.ts index 0541ba668..66c419060 100644 --- a/helpers/roles/mod.ts +++ b/helpers/roles/mod.ts @@ -2,6 +2,6 @@ export * from "./addRole.ts"; export * from "./createRole.ts"; export * from "./deleteRole.ts"; export * from "./editRole.ts"; +export * from "./editRolePositions.ts"; export * from "./getRoles.ts"; -export * from "./modifyRolePositions.ts"; export * from "./removeRole.ts"; diff --git a/helpers/roles/removeRole.ts b/helpers/roles/removeRole.ts index 3e9b87bdd..9ccd66b41 100644 --- a/helpers/roles/removeRole.ts +++ b/helpers/roles/removeRole.ts @@ -1,11 +1,25 @@ import type { Bot } from "../../bot.ts"; -/** Remove a role from the member */ -export async function removeRole(bot: Bot, guildId: bigint, memberId: bigint, roleId: bigint, reason?: string) { +/** + * Removes a role from a member. + * + * @param bot - The bot instance to use to make the request. + * @param guildId - The ID of the guild the member to remove the role from is in. + * @param userId - The user ID of the member to remove the role from. + * @param roleId - The ID of the role to remove from the member. + * + * @remarks + * Requires the `MANAGE_ROLES` permission. + * + * Fires a _Guild Member Update_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild#remove-guild-member-role} + */ +export async function removeRole(bot: Bot, guildId: bigint, userId: bigint, roleId: bigint, reason?: string) { return await bot.rest.runMethod( bot.rest, "DELETE", - bot.constants.routes.GUILD_MEMBER_ROLE(guildId, memberId, roleId), + bot.constants.routes.GUILD_MEMBER_ROLE(guildId, userId, roleId), { reason }, ); } diff --git a/helpers/templates/createGuildFromTemplate.ts b/helpers/templates/createGuildFromTemplate.ts index 53ba7b473..d4d1ea8a9 100644 --- a/helpers/templates/createGuildFromTemplate.ts +++ b/helpers/templates/createGuildFromTemplate.ts @@ -3,23 +3,34 @@ import { Guild } from "../../transformers/guild.ts"; import { DiscordGuild } from "../../types/discord.ts"; /** - * Create a new guild based on a template - * NOTE: This endpoint can be used only by bots in less than 10 guilds. + * Creates a guild from a template. + * + * @param bot - The bot instance to use to make the request. + * @param templateCode - The code of the template. + * @param options - The parameters for the creation of the guild. + * @returns An instance of the created {@link Guild}. + * + * @remarks + * ⚠️ This route can only be used by bots in __fewer than 10 guilds__. + * + * Fires a _Guild Create_ gateway event. + * + * @see {@link https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template} */ export async function createGuildFromTemplate( bot: Bot, templateCode: string, - data: CreateGuildFromTemplate, + options: CreateGuildFromTemplate, ): Promise { - if (data.icon) { - data.icon = await bot.utils.urlToBase64(data.icon); + if (options.icon) { + options.icon = await bot.utils.urlToBase64(options.icon); } const createdGuild = await bot.rest.runMethod( bot.rest, "POST", bot.constants.routes.TEMPLATE(templateCode), - data, + options, ); return bot.transformers.guild(bot, { diff --git a/helpers/templates/createGuildTemplate.ts b/helpers/templates/createGuildTemplate.ts index 72cc42b7e..1c33470cc 100644 --- a/helpers/templates/createGuildTemplate.ts +++ b/helpers/templates/createGuildTemplate.ts @@ -2,13 +2,27 @@ import type { Bot } from "../../bot.ts"; import { Template } from "../../transformers/template.ts"; import { DiscordTemplate } from "../../types/discord.ts"; -/** Creates a template for the guild. Requires the `MANAGE_GUILD` permission. */ -export async function createGuildTemplate(bot: Bot, guildId: bigint, data: CreateTemplate): Promise