From 275d17c6fe75fbd89c7330a035f82a685dfae101 Mon Sep 17 00:00:00 2001 From: rigormorrtiss Date: Wed, 12 May 2021 17:57:45 +0400 Subject: [PATCH] add: stage instances (#924) * feat: implement stage instances Reference: https://github.com/discord/discord-api-docs/pull/2898 * feat: implement stage instances * Update src/types/misc/stage_instance.ts Co-authored-by: ITOH * Update src/helpers/misc/delete_stage_instance.ts Co-authored-by: ITOH * Update src/types/misc/stage_instance.ts Co-authored-by: ITOH * Move stage instances related modules to channels module * Move to channels * Move to channels * Add permission checks * Add permissions checl * Ad inhibitors * style: fix lint warnings * Do not throw & add import type Co-authored-by: ITOH --- src/helpers/channels/create_stage_instance.ts | 40 ++++++++++++++++++ src/helpers/channels/delete_stage_instance.ts | 28 +++++++++++++ src/helpers/channels/get_stage_instance.ts | 22 ++++++++++ src/helpers/channels/update_stage_instance.ts | 42 +++++++++++++++++++ src/helpers/mod.ts | 12 ++++++ src/types/channels/stage_instance.ts | 11 +++++ src/types/discordeno/errors.ts | 2 + src/types/mod.ts | 1 + src/util/constants.ts | 5 +++ 9 files changed, 163 insertions(+) create mode 100644 src/helpers/channels/create_stage_instance.ts create mode 100644 src/helpers/channels/delete_stage_instance.ts create mode 100644 src/helpers/channels/get_stage_instance.ts create mode 100644 src/helpers/channels/update_stage_instance.ts create mode 100644 src/types/channels/stage_instance.ts diff --git a/src/helpers/channels/create_stage_instance.ts b/src/helpers/channels/create_stage_instance.ts new file mode 100644 index 000000000..25ecf2071 --- /dev/null +++ b/src/helpers/channels/create_stage_instance.ts @@ -0,0 +1,40 @@ +import { validateLength } from "../../util/validate_length.ts"; +import { Errors } from "../../types/discordeno/errors.ts"; +import { rest } from "../../rest/rest.ts"; +import { endpoints } from "../../util/constants.ts"; +import type { StageInstance } from "../../types/channels/stage_instance.ts"; +import { cacheHandlers } from "../../cache.ts"; +import { ChannelTypes } from "../../types/channels/channel_types.ts"; +import { requireBotChannelPermissions } from "../../util/permissions.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(channelId: bigint, topic: string) { + const channel = await cacheHandlers.get("channels", channelId); + + if (channel) { + if (channel.type !== ChannelTypes.GuildStageVoice) { + throw new Error(Errors.CHANNEL_NOT_STAGE_VOICE); + } + + await requireBotChannelPermissions(channel, [ + "MANAGE_CHANNELS", + "MUTE_MEMBERS", + "MOVE_MEMBERS", + ]); + } + + if ( + !validateLength(topic, { max: 120, min: 1 }) + ) { + throw new Error(Errors.INVALID_TOPIC_LENGTH); + } + + return await rest.runMethod( + "post", + endpoints.STAGE_INSTANCES, + { + "channel_id": channelId, + topic, + }, + ); +} diff --git a/src/helpers/channels/delete_stage_instance.ts b/src/helpers/channels/delete_stage_instance.ts new file mode 100644 index 000000000..e38206a8a --- /dev/null +++ b/src/helpers/channels/delete_stage_instance.ts @@ -0,0 +1,28 @@ +import { cacheHandlers } from "../../cache.ts"; +import { rest } from "../../rest/rest.ts"; +import { ChannelTypes } from "../../types/channels/channel_types.ts"; +import { Errors } from "../../types/discordeno/errors.ts"; +import { endpoints } from "../../util/constants.ts"; +import { requireBotChannelPermissions } from "../../util/permissions.ts"; + +/** Deletes the Stage instance. Requires the user to be a moderator of the Stage channel. */ +export async function deleteStageInstance(channelId: bigint) { + const channel = await cacheHandlers.get("channels", channelId); + + if (channel) { + if (channel.type !== ChannelTypes.GuildStageVoice) { + throw new Error(Errors.CHANNEL_NOT_STAGE_VOICE); + } + + await requireBotChannelPermissions(channel, [ + "MUTE_MEMBERS", + "MANAGE_CHANNELS", + "MOVE_MEMBERS", + ]); + } + + return await rest.runMethod( + "delete", + endpoints.STAGE_INSTANCE(channelId), + ); +} diff --git a/src/helpers/channels/get_stage_instance.ts b/src/helpers/channels/get_stage_instance.ts new file mode 100644 index 000000000..a712f2c2b --- /dev/null +++ b/src/helpers/channels/get_stage_instance.ts @@ -0,0 +1,22 @@ +import { cacheHandlers } from "../../cache.ts"; +import { rest } from "../../rest/rest.ts"; +import { ChannelTypes } from "../../types/channels/channel_types.ts"; +import type { StageInstance } from "../../types/channels/stage_instance.ts"; +import { Errors } from "../../types/discordeno/errors.ts"; +import { endpoints } from "../../util/constants.ts"; + +/** Gets the stage instance associated with the Stage channel, if it exists. */ +export async function getStageInstance(channelId: bigint) { + const channel = await cacheHandlers.get("channels", channelId); + + if (channel) { + if (channel.type !== ChannelTypes.GuildStageVoice) { + throw new Error(Errors.CHANNEL_NOT_STAGE_VOICE); + } + } + + return await rest.runMethod( + "get", + endpoints.STAGE_INSTANCE(channelId), + ); +} diff --git a/src/helpers/channels/update_stage_instance.ts b/src/helpers/channels/update_stage_instance.ts new file mode 100644 index 000000000..b5f5d3f2b --- /dev/null +++ b/src/helpers/channels/update_stage_instance.ts @@ -0,0 +1,42 @@ +import { rest } from "../../rest/rest.ts"; +import { Errors } from "../../types/discordeno/errors.ts"; +import type { StageInstance } from "../../types/channels/stage_instance.ts"; +import { endpoints } from "../../util/constants.ts"; +import { validateLength } from "../../util/validate_length.ts"; +import { cacheHandlers } from "../../cache.ts"; +import { requireBotChannelPermissions } from "../../util/permissions.ts"; +import { ChannelTypes } from "../../types/channels/channel_types.ts"; + +/** Updates fields of an existing Stage instance. Requires the user to be a moderator of the Stage channel. */ +export async function updateStageInstance(channelId: bigint, topic: string) { + const channel = await cacheHandlers.get("channels", channelId); + + if (channel) { + if (channel.type !== ChannelTypes.GuildStageVoice) { + throw new Error(Errors.CHANNEL_NOT_STAGE_VOICE); + } + + await requireBotChannelPermissions(channel, [ + "MOVE_MEMBERS", + "MUTE_MEMBERS", + "MANAGE_CHANNELS", + ]); + } + + if ( + !validateLength(topic, { + min: 1, + max: 120, + }) + ) { + throw new Error(Errors.INVALID_TOPIC_LENGTH); + } + + return await rest.runMethod( + "patch", + endpoints.STAGE_INSTANCE(channelId), + { + topic, + }, + ); +} diff --git a/src/helpers/mod.ts b/src/helpers/mod.ts index 356938908..5d928852c 100644 --- a/src/helpers/mod.ts +++ b/src/helpers/mod.ts @@ -128,6 +128,10 @@ import { getWebhooks } from "./webhooks/get_webhooks.ts"; import { getWebhookMessage } from "./webhooks/get_webhook_message.ts"; import { getWebhookWithToken } from "./webhooks/get_webhook_with_token.ts"; import { sendWebhook } from "./webhooks/send_webhook.ts"; +import { createStageInstance } from "./channels/create_stage_instance.ts"; +import { updateStageInstance } from "./channels/update_stage_instance.ts"; +import { getStageInstance } from "./channels/get_stage_instance.ts"; +import { deleteStageInstance } from "./channels/delete_stage_instance.ts"; export { addDiscoverySubcategory, @@ -148,6 +152,7 @@ export { createInvite, createRole, createSlashCommand, + createStageInstance, createWebhook, deleteChannel, deleteChannelOverwrite, @@ -161,6 +166,7 @@ export { deleteRole, deleteSlashCommand, deleteSlashResponse, + deleteStageInstance, deleteWebhook, deleteWebhookMessage, deleteWebhookWithToken, @@ -217,6 +223,7 @@ export { getSlashCommandPermission, getSlashCommandPermissions, getSlashCommands, + getStageInstance, getTemplate, getUser, getVanityURL, @@ -260,6 +267,7 @@ export { unpin, unpinMessage, updateBotVoiceState, + updateStageInstance, upsertSlashCommand, upsertSlashCommands, validDiscoveryTerm, @@ -282,6 +290,10 @@ export let helpers = { startTyping, swapChannels, updateBotVoiceState, + createStageInstance, + getStageInstance, + updateStageInstance, + deleteStageInstance, // commands createSlashCommand, deleteSlashCommand, diff --git a/src/types/channels/stage_instance.ts b/src/types/channels/stage_instance.ts new file mode 100644 index 000000000..3862ff9cc --- /dev/null +++ b/src/types/channels/stage_instance.ts @@ -0,0 +1,11 @@ +// TODO: add resource link +export interface StageInstance { + /** The id of this Stage instance */ + id: string; + /** The guild id of the associated Stage channel */ + guildId: string; + /** The id of the associated Stage channel */ + channelId: string; + /** The topic of the Stage instance (1-120 characters) */ + topic: string; +} diff --git a/src/types/discordeno/errors.ts b/src/types/discordeno/errors.ts index 5fc9c0ef7..3714758a2 100644 --- a/src/types/discordeno/errors.ts +++ b/src/types/discordeno/errors.ts @@ -5,9 +5,11 @@ export enum Errors { CHANNEL_NOT_FOUND = "CHANNEL_NOT_FOUND", CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD", CHANNEL_NOT_TEXT_BASED = "CHANNEL_NOT_TEXT_BASED", + CHANNEL_NOT_STAGE_VOICE = "CHANNEL_NOT_STAGE_VOICE", MESSAGE_MAX_LENGTH = "MESSAGE_MAX_LENGTH", RULES_CHANNEL_CANNOT_BE_DELETED = "RULES_CHANNEL_CANNOT_BE_DELETED", UPDATES_CHANNEL_CANNOT_BE_DELETED = "UPDATES_CHANNEL_CANNOT_BE_DELETED", + INVALID_TOPIC_LENGTH = "INVALID_TOPIC_LENGTH", // Guild Errors GUILD_NOT_DISCOVERABLE = "GUILD_NOT_DISCOVERABLE", GUILD_WIDGET_NOT_ENABLED = "GUILD_WIDGET_NOT_ENABLED", diff --git a/src/types/mod.ts b/src/types/mod.ts index 2bd4c0e92..3ca331274 100644 --- a/src/types/mod.ts +++ b/src/types/mod.ts @@ -231,3 +231,4 @@ export * from "./webhooks/execute_webhook.ts"; export * from "./webhooks/modify_webhook.ts"; export * from "./webhooks/webhook.ts"; export * from "./webhooks/webhooks_update.ts"; +export * from "./channels/stage_instance.ts"; diff --git a/src/util/constants.ts b/src/util/constants.ts index 89b987d3a..23e92df32 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -226,6 +226,11 @@ export const endpoints = { // OAuth2 OAUTH2_APPLICATION: `${baseEndpoints.BASE_URL}/oauth2/applications/@me`, + + // Stage instances + STAGE_INSTANCES: `${baseEndpoints.BASE_URL}/stage-instances`, + STAGE_INSTANCE: (channelId: bigint) => + `${baseEndpoints.BASE_URL}/stage-instances/${channelId}`, }; export const SLASH_COMMANDS_NAME_REGEX = /^[\w-]{1,32}$/;