From 45bfa059b57e95635d171960c1b316a3c3763d24 Mon Sep 17 00:00:00 2001 From: Fleny Date: Sat, 7 Sep 2024 08:21:14 +0200 Subject: [PATCH] feat(bot,rest,types): Add get activity instance endpoint (#3891) * Add get activity instance endpoint * Add ActivityInstance & ActivityLocation transformers --- packages/bot/src/helpers.ts | 7 ++++ packages/bot/src/transformers.ts | 42 +++++++++++++++++++++++ packages/bot/src/transformers/activity.ts | 35 ++++++++++++++++++- packages/bot/src/transformers/types.ts | 25 ++++++++++++++ packages/rest/src/manager.ts | 5 +++ packages/rest/src/routes.ts | 4 +++ packages/rest/src/types.ts | 9 ++++- packages/rest/src/typings/routes.ts | 2 ++ packages/types/src/discord.ts | 34 ++++++++++++++++++ 9 files changed, 161 insertions(+), 2 deletions(-) diff --git a/packages/bot/src/helpers.ts b/packages/bot/src/helpers.ts index 43d19efac..810c1a8da 100644 --- a/packages/bot/src/helpers.ts +++ b/packages/bot/src/helpers.ts @@ -5,6 +5,7 @@ import type { AtLeastOne, BeginGuildPrune, BigString, + Camelize, CamelizedDiscordAccessTokenResponse, CamelizedDiscordApplicationCommandPermissions, CamelizedDiscordApplicationRoleConnection, @@ -46,6 +47,7 @@ import type { CreateStageInstance, CreateTemplate, DeleteWebhookMessageOptions, + DiscordActivityInstance, DiscordEntitlement, DiscordMessage, EditApplication, @@ -578,6 +580,9 @@ export function createBotHelpers(bot: Bot): BotHelpers { failedUsers: res.failedUsers.map((x) => bot.transformers.snowflake(x)), } }, + getApplicationActivityInstance: async (applicationId, instanceId) => { + return await bot.rest.getApplicationActivityInstance(applicationId, instanceId) + }, // All useless void return functions here addReaction: async (channelId, messageId, reaction) => { return await bot.rest.addReaction(channelId, messageId, reaction) @@ -720,6 +725,7 @@ export function createBotHelpers(bot: Bot): BotHelpers { banMember: async (guildId, userId, options, reason) => { return await bot.rest.banMember(guildId, userId, options, reason) }, + kickMember: async (guildId, userId, reason) => { return await bot.rest.kickMember(guildId, userId, reason) }, @@ -930,6 +936,7 @@ export interface BotHelpers { pruneMembers: (guildId: BigString, options: BeginGuildPrune, reason?: string) => Promise<{ pruned: number | null }> searchMembers: (guildId: BigString, query: string, options?: Omit) => Promise bulkBanMembers: (guildId: BigString, options: CreateGuildBulkBan, reason?: string) => Promise<{ bannedUsers: bigint[]; failedUsers: bigint[] }> + getApplicationActivityInstance: (applicationId: BigString, instanceId: string) => Promise> // functions return Void so dont need any special handling addReaction: (channelId: BigString, messageId: BigString, reaction: string) => Promise addReactions: (channelId: BigString, messageId: BigString, reactions: string[], ordered?: boolean) => Promise diff --git a/packages/bot/src/transformers.ts b/packages/bot/src/transformers.ts index 25576422c..d344eb2c6 100644 --- a/packages/bot/src/transformers.ts +++ b/packages/bot/src/transformers.ts @@ -3,6 +3,8 @@ import type { BigString, CreateApplicationCommand, DiscordActivity, + DiscordActivityInstance, + DiscordActivityLocation, DiscordAllowedMentions, DiscordApplication, DiscordApplicationCommand, @@ -63,6 +65,8 @@ import { logger } from '@discordeno/utils' import type { Bot } from './bot.js' import { type Activity, + type ActivityInstance, + type ActivityLocation, type Application, type ApplicationCommand, type ApplicationCommandOption, @@ -118,6 +122,8 @@ import { type Webhook, type WelcomeScreen, transformActivity, + transformActivityInstance, + transformActivityLocation, transformActivityToDiscordActivity, transformApplication, transformApplicationCommand, @@ -201,6 +207,8 @@ import { bigintToSnowflake, snowflakeToBigint } from './utils.js' export interface Transformers { customizers: { activity: (bot: Bot, payload: DiscordActivity, activity: Activity) => any + activityInstance: (bot: Bot, payload: DiscordActivityInstance, activityInstance: ActivityInstance) => any + activityLocation: (bot: Bot, payload: DiscordActivityLocation, activityLocation: ActivityLocation) => any application: (bot: Bot, payload: DiscordApplication, application: Application) => any applicationCommand: (bot: Bot, payload: DiscordApplicationCommand, applicationCommand: ApplicationCommand) => any applicationCommandOption: (bot: Bot, payload: DiscordApplicationCommandOption, applicationCommandOption: ApplicationCommandOption) => any @@ -287,6 +295,8 @@ export interface Transformers { user: (bot: Bot, payload: User) => DiscordUser } activity: (bot: Bot, payload: DiscordActivity) => Activity + activityInstance: (bot: Bot, payload: DiscordActivityInstance) => ActivityInstance + activityLocation: (bot: Bot, payload: DiscordActivityLocation) => ActivityLocation application: (bot: Bot, payload: { application: DiscordApplication; shardId: number }) => Application applicationCommand: (bot: Bot, payload: DiscordApplicationCommand) => ApplicationCommand applicationCommandOption: (bot: Bot, payload: DiscordApplicationCommandOption) => ApplicationCommandOption @@ -345,6 +355,19 @@ export interface Transformers { } export interface TransformersDesiredProperties { + activityInstance: { + applicationId: boolean + instanceId: boolean + launchId: boolean + location: boolean + users: boolean + } + activityLocation: { + id: boolean + kind: boolean + channelId: boolean + guildId: boolean + } attachment: { id: boolean filename: boolean @@ -795,6 +818,8 @@ export function createTransformers(options: RecursivePartial, opts return { customizers: { activity: options.customizers?.activity ?? defaultCustomizer, + activityInstance: options.customizers?.activityInstance ?? defaultCustomizer, + activityLocation: options.customizers?.activityLocation ?? defaultCustomizer, application: options.customizers?.application ?? defaultCustomizer, applicationCommand: options.customizers?.applicationCommand ?? defaultCustomizer, applicationCommandOption: options.customizers?.applicationCommandOption ?? defaultCustomizer, @@ -870,6 +895,8 @@ export function createTransformers(options: RecursivePartial, opts user: options.reverse?.user ?? transformUserToDiscordUser, }, activity: options.activity ?? transformActivity, + activityInstance: options.activityInstance ?? transformActivityInstance, + activityLocation: options.activityLocation ?? transformActivityLocation, application: options.application ?? transformApplication, applicationCommand: options.applicationCommand ?? transformApplicationCommand, applicationCommandOption: options.applicationCommandOption ?? transformApplicationCommandOption, @@ -933,6 +960,21 @@ export function createDesiredPropertiesObject( defaultValue = false, ): TransformersDesiredProperties { return { + activityInstance: { + applicationId: defaultValue, + instanceId: defaultValue, + launchId: defaultValue, + location: defaultValue, + users: defaultValue, + ...desiredProperties.activityInstance, + }, + activityLocation: { + channelId: defaultValue, + guildId: defaultValue, + id: defaultValue, + kind: defaultValue, + ...desiredProperties.activityLocation, + }, attachment: { id: defaultValue, filename: defaultValue, diff --git a/packages/bot/src/transformers/activity.ts b/packages/bot/src/transformers/activity.ts index 0e70d0f36..ae6df689b 100644 --- a/packages/bot/src/transformers/activity.ts +++ b/packages/bot/src/transformers/activity.ts @@ -1,4 +1,12 @@ -import type { Activity, Bot, DiscordActivity } from '../index.js' +import type { + Activity, + ActivityInstance, + ActivityLocation, + Bot, + DiscordActivity, + DiscordActivityInstance, + DiscordActivityLocation, +} from '../index.js' export function transformActivity(bot: Bot, payload: DiscordActivity): Activity { const activity = { @@ -35,3 +43,28 @@ export function transformActivity(bot: Bot, payload: DiscordActivity): Activity return bot.transformers.customizers.activity(bot, payload, activity) } + +export function transformActivityInstance(bot: Bot, payload: DiscordActivityInstance): ActivityInstance { + const props = bot.transformers.desiredProperties.activityInstance + const activityInstance = {} as ActivityInstance + + if (props.applicationId && payload.application_id) activityInstance.applicationId = bot.transformers.snowflake(payload.application_id) + if (props.instanceId && payload.instance_id) activityInstance.instanceId = payload.instance_id + if (props.launchId && payload.launch_id) activityInstance.launchId = bot.transformers.snowflake(payload.launch_id) + if (props.location && payload.location) activityInstance.location = bot.transformers.activityLocation(bot, payload.location) + if (props.users && payload.users) activityInstance.users = payload.users.map((x) => bot.transformers.snowflake(x)) + + return bot.transformers.customizers.activityInstance(bot, payload, activityInstance) +} + +export function transformActivityLocation(bot: Bot, payload: DiscordActivityLocation): ActivityLocation { + const props = bot.transformers.desiredProperties.activityLocation + const activityLocation = {} as ActivityLocation + + if (props.id && payload.id) activityLocation.id = payload.id + if (props.kind && payload.kind) activityLocation.kind = payload.kind + if (props.channelId && payload.channel_id) activityLocation.channelId = bot.transformers.snowflake(payload.channel_id) + if (props.guildId && payload.guild_id) activityLocation.guildId = bot.transformers.snowflake(payload.guild_id) + + return bot.transformers.customizers.activityLocation(bot, payload, activityLocation) +} diff --git a/packages/bot/src/transformers/types.ts b/packages/bot/src/transformers/types.ts index 8329e571a..00ab4fcd9 100644 --- a/packages/bot/src/transformers/types.ts +++ b/packages/bot/src/transformers/types.ts @@ -13,6 +13,7 @@ import type { ButtonStyles, ChannelTypes, DefaultMessageNotificationLevels, + DiscordActivityLocationKind, DiscordApplicationIntegrationType, DiscordAutoModerationRuleTriggerMetadataPresets, DiscordEntitlementType, @@ -117,6 +118,30 @@ export interface ActivityButton { label: string } +export interface ActivityInstance { + /** Application ID */ + applicationId: bigint + /** Activity Instance ID */ + instanceId: string + /** Unique identifier for the launch */ + launchId: bigint + /** The Location the instance is runnning in */ + location: ActivityLocation + /** The IDs of the Users currently connected to the instance */ + users: bigint[] +} + +export interface ActivityLocation { + /** The unique identifier for the location */ + id: string + /** Enum describing kind of location */ + kind: DiscordActivityLocationKind + /** The id of the Channel */ + channelId: bigint + /** The id of the Guild */ + guildId?: bigint +} + export interface Application { flags?: ApplicationFlags icon?: bigint diff --git a/packages/rest/src/manager.ts b/packages/rest/src/manager.ts index 5dd8b57a3..44f8f2344 100644 --- a/packages/rest/src/manager.ts +++ b/packages/rest/src/manager.ts @@ -3,6 +3,7 @@ import { type BigString, type Camelize, type DiscordAccessTokenResponse, + type DiscordActivityInstance, type DiscordApplication, type DiscordApplicationCommand, type DiscordApplicationRoleConnection, @@ -1518,6 +1519,10 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage return await rest.get(rest.routes.guilds.members.members(guildId, options)) }, + async getApplicationActivityInstance(applicationId, instanceId) { + return await rest.get(rest.routes.applicationActivityInstance(applicationId, instanceId)) + }, + async kickMember(guildId, userId, reason) { await rest.delete(rest.routes.guilds.members.member(guildId, userId), { reason, diff --git a/packages/rest/src/routes.ts b/packages/rest/src/routes.ts index ca8cf1290..5f48d916a 100644 --- a/packages/rest/src/routes.ts +++ b/packages/rest/src/routes.ts @@ -612,6 +612,10 @@ export function createRoutes(): RestRoutes { return '/applications/@me' }, + applicationActivityInstance(applicationId, instanceId) { + return `/applications/${applicationId}/activity-instances/${instanceId}` + }, + currentUser() { return '/users/@me' }, diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index dba310a26..0f16690f8 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -77,6 +77,7 @@ import type { CreateStageInstance, CreateTemplate, DeleteWebhookMessageOptions, + DiscordActivityInstance, // Type required for typedoc // eslint-disable-next-line @typescript-eslint/no-unused-vars DiscordApplicationIntegrationType, @@ -2855,7 +2856,13 @@ export interface RestManager { * @see {@link https://discord.com/developers/docs/topics/rate-limits#rate-limits} */ getMembers: (guildId: BigString, options: ListGuildMembers) => Promise - + /** + * Returns a serialized activity instance, if it exists. Useful for preventing unwanted activity sessions. + * + * @param applicationId - The ID of the application + * @param instanceId - The ID of the activity instance + */ + getApplicationActivityInstance: (applicationId: BigString, instanceId: string) => Promise> /** * Kicks a member from a guild. * diff --git a/packages/rest/src/typings/routes.ts b/packages/rest/src/typings/routes.ts index 9d6a80c86..04ee75688 100644 --- a/packages/rest/src/typings/routes.ts +++ b/packages/rest/src/typings/routes.ts @@ -282,6 +282,8 @@ export interface RestRoutes { currentUser: () => string /** Route to get and edit information about the current application. */ application: () => string + /** Route for get an activity instance */ + applicationActivityInstance: (applicationId: BigString, instanceId: string) => string /** Route for handling a sticker. */ sticker: (stickerId: BigString) => string /** Route for handling all voice regions. */ diff --git a/packages/types/src/discord.ts b/packages/types/src/discord.ts index 46057db39..79b2d7ea8 100644 --- a/packages/types/src/discord.ts +++ b/packages/types/src/discord.ts @@ -1182,6 +1182,40 @@ export interface DiscordActivity { buttons?: DiscordActivityButton[] } +/** https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-instance-object */ +export interface DiscordActivityInstance { + /** Application ID */ + application_id: string + /** Activity Instance ID */ + instance_id: string + /** Unique identifier for the launch */ + launch_id: string + /** The Location the instance is runnning in */ + location: DiscordActivityLocation + /** The IDs of the Users currently connected to the instance */ + users: string[] +} + +/** https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-object */ +export interface DiscordActivityLocation { + /** The unique identifier for the location */ + id: string + /** Enum describing kind of location */ + kind: DiscordActivityLocationKind + /** The id of the Channel */ + channel_id: string + /** The id of the Guild */ + guild_id?: string | null +} + +/** https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-kind-enum */ +export enum DiscordActivityLocationKind { + /** The Location is a Guild Channel */ + GuildChannel = 'gc', + /** The Location is a Private Channel, such as a DM or GDM */ + PrivateChannel = 'pc', +} + /** https://discord.com/developers/docs/topics/gateway#client-status-object */ export interface DiscordClientStatus { /** The user's status set for an active desktop (Windows, Linux, Mac) application session */