diff --git a/packages/bot/src/bot.ts b/packages/bot/src/bot.ts index cf31e38b6..b4040ef27 100644 --- a/packages/bot/src/bot.ts +++ b/packages/bot/src/bot.ts @@ -13,6 +13,7 @@ import type { AutoModerationActionExecution } from './transformers/automodAction import type { AutoModerationRule } from './transformers/automodRule.js' import type { Channel } from './transformers/channel.js' import type { Emoji } from './transformers/emoji.js' +import { type Entitlement } from './transformers/entitlement.js' import type { Guild } from './transformers/guild.js' import type { Integration } from './transformers/integration.js' import type { Interaction } from './transformers/interaction.js' @@ -225,4 +226,7 @@ export interface EventHandlers { webhooksUpdate: (payload: { channelId: bigint; guildId: bigint }) => unknown botUpdate: (user: User) => unknown typingStart: (payload: { guildId: bigint | undefined; channelId: bigint; userId: bigint; timestamp: number; member: Member | undefined }) => unknown + entitlementCreate: (entitlement: Entitlement) => unknown + entitlementUpdate: (entitlement: Entitlement) => unknown + entitlementDelete: (entitlement: Entitlement) => unknown } diff --git a/packages/bot/src/handlers.ts b/packages/bot/src/handlers.ts index d2c0f8642..d97d51cd5 100644 --- a/packages/bot/src/handlers.ts +++ b/packages/bot/src/handlers.ts @@ -66,6 +66,9 @@ export function createBotGatewayHandlers( VOICE_SERVER_UPDATE: options.VOICE_SERVER_UPDATE ?? handlers.handleVoiceServerUpdate, VOICE_STATE_UPDATE: options.VOICE_STATE_UPDATE ?? handlers.handleVoiceStateUpdate, WEBHOOKS_UPDATE: options.WEBHOOKS_UPDATE ?? handlers.handleWebhooksUpdate, + ENTITLEMENT_CREATE: options.ENTITLEMENT_CREATE ?? handlers.handleEntitlementCreate, + ENTITLEMENT_UPDATE: options.ENTITLEMENT_UPDATE ?? handlers.handleEntitlementUpdate, + ENTITLEMENT_DELETE: options.ENTITLEMENT_DELETE ?? handlers.handleEntitlementDelete, } } diff --git a/packages/bot/src/handlers/entitlements/ENTITLEMENT_CREATE.ts b/packages/bot/src/handlers/entitlements/ENTITLEMENT_CREATE.ts new file mode 100644 index 000000000..24c701e2a --- /dev/null +++ b/packages/bot/src/handlers/entitlements/ENTITLEMENT_CREATE.ts @@ -0,0 +1,9 @@ +import type { DiscordEntitlement, DiscordGatewayPayload } from '@discordeno/types' +import type { Bot } from '../../bot.js' + +export async function handleEntitlementCreate(bot: Bot, data: DiscordGatewayPayload): Promise { + if (!bot.events.entitlementCreate) return + + const payload = data.d as DiscordEntitlement + bot.events.entitlementCreate(bot.transformers.entitlement(bot, payload)) +} diff --git a/packages/bot/src/handlers/entitlements/ENTITLEMENT_DELETE.ts b/packages/bot/src/handlers/entitlements/ENTITLEMENT_DELETE.ts new file mode 100644 index 000000000..742296dfc --- /dev/null +++ b/packages/bot/src/handlers/entitlements/ENTITLEMENT_DELETE.ts @@ -0,0 +1,9 @@ +import type { DiscordEntitlement, DiscordGatewayPayload } from '@discordeno/types' +import type { Bot } from '../../bot.js' + +export async function handleEntitlementDelete(bot: Bot, data: DiscordGatewayPayload): Promise { + if (!bot.events.entitlementDelete) return + + const payload = data.d as DiscordEntitlement + bot.events.entitlementDelete(bot.transformers.entitlement(bot, payload)) +} diff --git a/packages/bot/src/handlers/entitlements/ENTITLEMENT_UPDATE.ts b/packages/bot/src/handlers/entitlements/ENTITLEMENT_UPDATE.ts new file mode 100644 index 000000000..fe9c764f4 --- /dev/null +++ b/packages/bot/src/handlers/entitlements/ENTITLEMENT_UPDATE.ts @@ -0,0 +1,9 @@ +import type { DiscordEntitlement, DiscordGatewayPayload } from '@discordeno/types' +import type { Bot } from '../../bot.js' + +export async function handleEntitlementUpdate(bot: Bot, data: DiscordGatewayPayload): Promise { + if (!bot.events.entitlementUpdate) return + + const payload = data.d as DiscordEntitlement + bot.events.entitlementUpdate(bot.transformers.entitlement(bot, payload)) +} diff --git a/packages/bot/src/handlers/entitlements/index.ts b/packages/bot/src/handlers/entitlements/index.ts new file mode 100644 index 000000000..920971554 --- /dev/null +++ b/packages/bot/src/handlers/entitlements/index.ts @@ -0,0 +1,3 @@ +export * from './ENTITLEMENT_CREATE.js' +export * from './ENTITLEMENT_DELETE.js' +export * from './ENTITLEMENT_UPDATE.js' diff --git a/packages/bot/src/handlers/index.ts b/packages/bot/src/handlers/index.ts index 243289d18..29085efe1 100644 --- a/packages/bot/src/handlers/index.ts +++ b/packages/bot/src/handlers/index.ts @@ -1,5 +1,6 @@ export * from './channels/index.js' export * from './emojis/index.js' +export * from './entitlements/index.js' export * from './guilds/index.js' export * from './integrations/index.js' export * from './interactions/index.js' diff --git a/packages/bot/src/handlers/invites/INVITE_CREATE.ts b/packages/bot/src/handlers/invites/INVITE_CREATE.ts index 0fb03feb1..b0b85fee1 100644 --- a/packages/bot/src/handlers/invites/INVITE_CREATE.ts +++ b/packages/bot/src/handlers/invites/INVITE_CREATE.ts @@ -1,8 +1,8 @@ import type { DiscordGatewayPayload, DiscordInviteCreate } from '@discordeno/types' import type { Bot } from '../../index.js' -export async function handleInviteCreate(bot: Bot, data: DiscordGatewayPayload): Promise { +export async function handleInviteCreate(bot: Bot, data: DiscordGatewayPayload, shardId: number): Promise { if (!bot.events.inviteCreate) return - bot.events.inviteCreate(bot.transformers.invite(bot, data.d as DiscordInviteCreate)) + bot.events.inviteCreate(bot.transformers.invite(bot, { invite: data.d as DiscordInviteCreate, shardId })) } diff --git a/packages/bot/src/helpers.ts b/packages/bot/src/helpers.ts index 6219c018d..2a07c87e0 100644 --- a/packages/bot/src/helpers.ts +++ b/packages/bot/src/helpers.ts @@ -29,6 +29,7 @@ import type { CreateApplicationCommand, CreateAutoModerationRuleOptions, CreateChannelInvite, + CreateEntitlement, CreateForumPostWithMessage, CreateGlobalApplicationCommandOptions, CreateGuild, @@ -44,7 +45,9 @@ import type { CreateStageInstance, CreateTemplate, DeleteWebhookMessageOptions, + DiscordEntitlement, DiscordMessage, + EditApplication, EditAutoModerationRuleOptions, EditBotMemberOptions, EditChannelPermissionOverridesOptions, @@ -58,6 +61,7 @@ import type { ExecuteWebhook, GetApplicationCommandPermissionOptions, GetBans, + GetEntitlements, GetGroupDmOptions, GetGuildAuditLog, GetGuildPruneCountQuery, @@ -95,6 +99,7 @@ import type { ApplicationCommandPermission } from './transformers/applicationCom import type { AutoModerationRule } from './transformers/automodRule.js' import type { Channel } from './transformers/channel.js' import type { Emoji } from './transformers/emoji.js' +import { type Entitlement } from './transformers/entitlement.js' import type { Guild } from './transformers/guild.js' import type { Integration } from './transformers/integration.js' import type { Invite } from './transformers/invite.js' @@ -103,6 +108,7 @@ import type { Message } from './transformers/message.js' import type { GuildOnboarding } from './transformers/onboarding.js' import type { Role } from './transformers/role.js' import type { ScheduledEvent } from './transformers/scheduledEvent.js' +import type { Sku } from './transformers/sku.js' import type { StageInstance } from './transformers/stageInstance.js' import type { Sticker, StickerPack } from './transformers/sticker.js' import type { Template } from './transformers/template.js' @@ -252,7 +258,10 @@ export function createBotHelpers(bot: Bot): BotHelpers { } }, getApplicationInfo: async () => { - return bot.transformers.application(bot, snakelize(await bot.rest.getApplicationInfo())) + return bot.transformers.application(bot, { application: snakelize(await bot.rest.getApplicationInfo()), shardId: 0 }) + }, + editApplicationInfo: async (body) => { + return bot.transformers.application(bot, { application: snakelize(await bot.rest.editApplicationInfo(body)), shardId: 0 }) }, getCurrentAuthenticationInfo: async (bearerToken) => { return await bot.rest.getCurrentAuthenticationInfo(bearerToken) @@ -366,10 +375,10 @@ export function createBotHelpers(bot: Bot): BotHelpers { ) }, getInvite: async (inviteCode, options) => { - return bot.transformers.invite(bot, snakelize(await bot.rest.getInvite(inviteCode, options))) + return bot.transformers.invite(bot, { invite: snakelize(await bot.rest.getInvite(inviteCode, options)), shardId: 0 }) }, getInvites: async (guildId) => { - return (await bot.rest.getInvites(guildId)).map((res) => bot.transformers.invite(bot, snakelize(res))) + return (await bot.rest.getInvites(guildId)).map((res) => bot.transformers.invite(bot, { invite: snakelize(res), shardId: 0 })) }, getMessage: async (channelId, messageId) => { return bot.transformers.message(bot, snakelize(await bot.rest.getMessage(channelId, messageId))) @@ -686,6 +695,18 @@ export function createBotHelpers(bot: Bot): BotHelpers { editGuildOnboarding: async (guildId, options, reason) => { return bot.transformers.guildOnboarding(bot, snakelize(await bot.rest.editGuildOnboarding(guildId, options, reason))) }, + listEntitlements: async (applicationId, options) => { + return (await bot.rest.listEntitlements(applicationId, options)).map((entitlement) => bot.transformers.entitlement(bot, snakelize(entitlement))) + }, + createTestEntitlement: async (applicationId, body) => { + return bot.transformers.entitlement(bot, snakelize(await bot.rest.createTestEntitlement(applicationId, body)) as DiscordEntitlement) + }, + deleteTestEntitlement: async (applicationId, entitlementId) => { + await bot.rest.deleteTestEntitlement(applicationId, entitlementId) + }, + listSkus: async (applicationId) => { + return (await bot.rest.listSkus(applicationId)).map((sku) => bot.transformers.sku(bot, snakelize(sku))) + }, } } @@ -756,6 +777,7 @@ export interface BotHelpers { followAnnouncement: (sourceChannelId: BigString, targetChannelId: BigString) => Promise getActiveThreads: (guildId: BigString) => Promise<{ threads: Channel[]; members: ThreadMember[] }> getApplicationInfo: () => Promise + editApplicationInfo: (body: EditApplication) => Promise getCurrentAuthenticationInfo: (bearerToken: string) => Promise exchangeToken: (clientId: BigString, clientSecret: string, options: CamelizedDiscordTokenExchange) => Promise revokeToken: (clientId: BigString, clientSecret: string, options: CamelizedDiscordTokenRevocation) => Promise @@ -905,4 +927,8 @@ export interface BotHelpers { unpinMessage: (channelId: BigString, messageId: BigString, reason?: string) => Promise getGuildOnboarding: (guildId: BigString) => Promise editGuildOnboarding: (guildId: BigString, options: EditGuildOnboarding, reason?: string) => Promise + listEntitlements: (applicationId: BigString, options?: GetEntitlements) => Promise + createTestEntitlement: (applicationId: BigString, body: CreateEntitlement) => Promise> + deleteTestEntitlement: (applicationId: BigString, entitlementId: BigString) => Promise + listSkus: (applicationId: BigString) => Promise } diff --git a/packages/bot/src/transformers.ts b/packages/bot/src/transformers.ts index f0e9191a0..dfbcc8190 100644 --- a/packages/bot/src/transformers.ts +++ b/packages/bot/src/transformers.ts @@ -16,6 +16,7 @@ import type { DiscordCreateApplicationCommand, DiscordEmbed, DiscordEmoji, + DiscordEntitlement, DiscordGetGatewayBot, DiscordGuild, DiscordGuildApplicationCommandPermissions, @@ -33,6 +34,7 @@ import type { DiscordPresenceUpdate, DiscordRole, DiscordScheduledEvent, + DiscordSku, DiscordStageInstance, DiscordSticker, DiscordStickerPack, @@ -61,6 +63,7 @@ import { transformChannel, type Channel } from './transformers/channel.js' import { transformComponent, type Component } from './transformers/component.js' import { transformEmbed, type Embed } from './transformers/embed.js' import { transformEmoji, type Emoji } from './transformers/emoji.js' +import { transformEntitlement, type Entitlement } from './transformers/entitlement.js' import { transformGatewayBot, type GetGatewayBot } from './transformers/gatewayBot.js' import { transformGuild, type Guild } from './transformers/guild.js' import { @@ -88,6 +91,7 @@ import { transformCreateApplicationCommandToDiscordCreateApplicationCommand } fr import { transformInteractionResponseToDiscordInteractionResponse } from './transformers/reverse/interactionResponse.js' import { transformRole, type Role } from './transformers/role.js' import { transformScheduledEvent, type ScheduledEvent } from './transformers/scheduledEvent.js' +import { transformSku, type Sku } from './transformers/sku.js' import { transformStageInstance, type StageInstance } from './transformers/stageInstance.js' import { transformInviteStageInstance, type InviteStageInstance } from './transformers/stageInviteInstance.js' import { transformSticker, transformStickerPack, type Sticker, type StickerPack } from './transformers/sticker.js' @@ -159,6 +163,8 @@ export interface Transformers { ) => any template: (bot: Bot, payload: DiscordTemplate, template: Template) => any guildOnboarding: (bot: Bot, payload: DiscordGuildOnboarding, onboarding: GuildOnboarding) => any + entitlement: (bot: Bot, payload: DiscordEntitlement, entitlement: Entitlement) => any + sku: (bot: Bot, payload: DiscordSku, sku: Sku) => any } desiredProperties: { attachment: { @@ -268,6 +274,7 @@ export interface Transformers { rulesChannelId: boolean publicUpdatesChannelId: boolean premiumProgressBarEnabled: boolean + safetyAlertsChannelId: boolean } interaction: { id: boolean @@ -472,6 +479,25 @@ export interface Transformers { enabled: boolean mode: boolean } + entitlement: { + id: boolean + skuId: boolean + userId: boolean + guildId: boolean + applicationId: boolean + type: boolean + deleted: boolean + startsAt: boolean + endsAt: boolean + } + sku: { + id: boolean + type: boolean + applicationId: boolean + name: boolean + slug: boolean + flags: boolean + } } reverse: { allowedMentions: (bot: Bot, payload: AllowedMentions) => DiscordAllowedMentions @@ -504,8 +530,8 @@ export interface Transformers { interaction: (bot: Bot, payload: DiscordInteraction) => Interaction interactionDataOptions: (bot: Bot, payload: DiscordInteractionDataOption) => InteractionDataOption integration: (bot: Bot, payload: DiscordIntegrationCreateUpdate) => Integration - invite: (bot: Bot, invite: DiscordInviteCreate | DiscordInviteMetadata) => Invite - application: (bot: Bot, payload: DiscordApplication) => Application + invite: (bot: Bot, payload: { invite: DiscordInviteCreate | DiscordInviteMetadata; shardId: number }) => Invite + application: (bot: Bot, payload: { application: DiscordApplication; shardId: number }) => Application team: (bot: Bot, payload: DiscordTeam) => Team emoji: (bot: Bot, payload: DiscordEmoji) => Emoji activity: (bot: Bot, payload: DiscordActivity) => Activity @@ -533,6 +559,8 @@ export interface Transformers { applicationCommandOptionChoice: (bot: Bot, payload: DiscordApplicationCommandOptionChoice) => ApplicationCommandOptionChoice template: (bot: Bot, payload: DiscordTemplate) => Template guildOnboarding: (bot: Bot, payload: DiscordGuildOnboarding) => GuildOnboarding + entitlement: (bot: Bot, payload: DiscordEntitlement) => Entitlement + sku: (bot: Bot, payload: DiscordSku) => Sku } export interface CreateTransformerOptions { @@ -674,6 +702,12 @@ export function createTransformers(options: Partial, opts?: Create guildOnboarding(bot, payload, onboarding) { return onboarding }, + entitlement(bot, payload, entitlement) { + return entitlement + }, + sku(bot, payload, sku) { + return sku + }, }, desiredProperties: { attachment: { @@ -783,6 +817,7 @@ export function createTransformers(options: Partial, opts?: Create rulesChannelId: opts?.defaultDesiredPropertiesValue ?? false, publicUpdatesChannelId: opts?.defaultDesiredPropertiesValue ?? false, premiumProgressBarEnabled: opts?.defaultDesiredPropertiesValue ?? false, + safetyAlertsChannelId: opts?.defaultDesiredPropertiesValue ?? false, }, interaction: { id: opts?.defaultDesiredPropertiesValue ?? false, @@ -987,6 +1022,25 @@ export function createTransformers(options: Partial, opts?: Create type: opts?.defaultDesiredPropertiesValue ?? false, }, }, + entitlement: { + id: opts?.defaultDesiredPropertiesValue ?? false, + skuId: opts?.defaultDesiredPropertiesValue ?? false, + userId: opts?.defaultDesiredPropertiesValue ?? false, + guildId: opts?.defaultDesiredPropertiesValue ?? false, + applicationId: opts?.defaultDesiredPropertiesValue ?? false, + type: opts?.defaultDesiredPropertiesValue ?? false, + deleted: opts?.defaultDesiredPropertiesValue ?? false, + startsAt: opts?.defaultDesiredPropertiesValue ?? false, + endsAt: opts?.defaultDesiredPropertiesValue ?? false, + }, + sku: { + id: opts?.defaultDesiredPropertiesValue ?? false, + type: opts?.defaultDesiredPropertiesValue ?? false, + applicationId: opts?.defaultDesiredPropertiesValue ?? false, + name: opts?.defaultDesiredPropertiesValue ?? false, + slug: opts?.defaultDesiredPropertiesValue ?? false, + flags: opts?.defaultDesiredPropertiesValue ?? false, + }, }, reverse: { allowedMentions: options.reverse?.allowedMentions ?? transformAllowedMentionsToDiscordAllowedMentions, @@ -1048,5 +1102,7 @@ export function createTransformers(options: Partial, opts?: Create applicationCommandOptionChoice: options.applicationCommandOptionChoice ?? transformApplicationCommandOptionChoice, template: options.template ?? transformTemplate, guildOnboarding: options.guildOnboarding ?? transformGuildOnboarding, + entitlement: options.entitlement ?? transformEntitlement, + sku: options.sku ?? transformSku, } } diff --git a/packages/bot/src/transformers/application.ts b/packages/bot/src/transformers/application.ts index e01655131..4d8421d82 100644 --- a/packages/bot/src/transformers/application.ts +++ b/packages/bot/src/transformers/application.ts @@ -1,31 +1,46 @@ -import { iconHashToBigInt, type ApplicationFlags, type Bot, type DiscordApplication, type Team, type User } from '../index.js' +import { + iconHashToBigInt, + type ApplicationFlags, + type Bot, + type DiscordApplication, + type DiscordUser, + type Guild, + type Team, + type User, +} from '../index.js' -export function transformApplication(bot: Bot, payload: DiscordApplication): Application { +export function transformApplication(bot: Bot, payload: { application: DiscordApplication; shardId: number }): Application { const application = { - name: payload.name, - description: payload.description, - rpcOrigins: payload.rpc_origins, - botPublic: payload.bot_public, - botRequireCodeGrant: payload.bot_require_code_grant, - termsOfServiceUrl: payload.terms_of_service_url, - privacyPolicyUrl: payload.privacy_policy_url, - verifyKey: payload.verify_key, - primarySkuId: payload.primary_sku_id, - slug: payload.slug, - coverImage: payload.cover_image ? iconHashToBigInt(payload.cover_image) : undefined, - flags: payload.flags, + name: payload.application.name, + description: payload.application.description, + rpcOrigins: payload.application.rpc_origins, + botPublic: payload.application.bot_public, + botRequireCodeGrant: payload.application.bot_require_code_grant, + termsOfServiceUrl: payload.application.terms_of_service_url, + privacyPolicyUrl: payload.application.privacy_policy_url, + verifyKey: payload.application.verify_key, + primarySkuId: payload.application.primary_sku_id, + slug: payload.application.slug, + coverImage: payload.application.cover_image ? iconHashToBigInt(payload.application.cover_image) : undefined, + flags: payload.application.flags, - id: bot.transformers.snowflake(payload.id), - icon: payload.icon ? iconHashToBigInt(payload.icon) : undefined, - owner: payload.owner + id: bot.transformers.snowflake(payload.application.id), + icon: payload.application.icon ? iconHashToBigInt(payload.application.icon) : undefined, + owner: payload.application.owner ? // @ts-expect-error the partial here wont break anything - bot.transformers.user(bot, payload.owner) + bot.transformers.user(bot, payload.application.owner) : undefined, - team: payload.team ? bot.transformers.team(bot, payload.team) : undefined, - guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined, + team: payload.application.team ? bot.transformers.team(bot, payload.application.team) : undefined, + guildId: payload.application.guild_id ? bot.transformers.snowflake(payload.application.guild_id) : undefined, + // @ts-expect-error the partial here wont break anything + guild: payload.application.guild ? bot.transformers.guild(bot, { guild: payload.application.guild, shardId: payload.shardId }) : undefined, + approximateGuildCount: payload.application.approximate_guild_count, + bot: payload.application.bot ? bot.transformers.user(bot, payload.application.bot as DiscordUser) : undefined, + interactionsEndpointUrl: payload.application.interactions_endpoint_url, + redirectUris: payload.application.redirect_uris, } as Application - return bot.transformers.customizers.application(bot, payload, application) + return bot.transformers.customizers.application(bot, payload.application, application) } export interface Application { @@ -40,10 +55,15 @@ export interface Application { owner?: User team?: Team guildId?: bigint + guild?: Guild id: bigint name: string description: string botPublic: boolean botRequireCodeGrant: boolean verifyKey: string + approximateGuildCount?: number + bot?: User + redirectUris?: string[] + interactionsEndpointUrl?: string } diff --git a/packages/bot/src/transformers/entitlement.ts b/packages/bot/src/transformers/entitlement.ts new file mode 100644 index 000000000..6c0c0d2a9 --- /dev/null +++ b/packages/bot/src/transformers/entitlement.ts @@ -0,0 +1,40 @@ +import type { DiscordEntitlement, DiscordEntitlementType } from '@discordeno/types' +import type { Bot } from '../index.js' + +export function transformEntitlement(bot: Bot, payload: DiscordEntitlement): Entitlement { + const props = bot.transformers.desiredProperties.entitlement + const entitlement = {} as Entitlement + + if (props.id && payload.id) entitlement.id = bot.transformers.snowflake(payload.id) + if (props.skuId && payload.sku_id) entitlement.skuId = bot.transformers.snowflake(payload.sku_id) + if (props.userId && payload.user_id) entitlement.userId = bot.transformers.snowflake(payload.user_id) + if (props.guildId && payload.guild_id) entitlement.guildId = bot.transformers.snowflake(payload.guild_id) + if (props.applicationId && payload.application_id) entitlement.applicationId = bot.transformers.snowflake(payload.application_id) + if (props.type && payload.type) entitlement.type = payload.type + if (props.deleted && payload.deleted) entitlement.deleted = payload.deleted + if (props.startsAt && payload.starts_at) entitlement.startsAt = Date.parse(payload.starts_at) + if (props.endsAt && payload.ends_at) entitlement.endsAt = Date.parse(payload.ends_at) + + return bot.transformers.customizers.entitlement(bot, payload, entitlement) +} + +export interface Entitlement { + /** ID of the entitlement */ + id: bigint + /** ID of the SKU */ + skuId: bigint + /** ID of the user that is granted access to the entitlement's sku */ + userId?: bigint + /** ID of the guild that is granted access to the entitlement's sku */ + guildId?: bigint + /** ID of the parent application */ + applicationId: bigint + /** Type of entitlement */ + type: DiscordEntitlementType + /** Entitlement was deleted */ + deleted: boolean + /** Start date at which the entitlement is valid. Not present when using test entitlements */ + startsAt?: number + /** Date at which the entitlement is no longer valid. Not present when using test entitlements */ + endsAt?: number +} diff --git a/packages/bot/src/transformers/guild.ts b/packages/bot/src/transformers/guild.ts index ad6a12e95..9033e8fd9 100644 --- a/packages/bot/src/transformers/guild.ts +++ b/packages/bot/src/transformers/guild.ts @@ -144,6 +144,8 @@ export function transformGuild(bot: Bot, payload: { guild: DiscordGuild } & { sh if (props.iconHash && payload.guild.icon_hash) guild.iconHash = iconHashToBigInt(payload.guild.icon_hash) if (props.presences && payload.guild.presences) guild.presences = payload.guild.presences?.map((presence) => bot.transformers.presence(bot, presence as DiscordPresenceUpdate)) + if (props.safetyAlertsChannelId && payload.guild.safety_alerts_channel_id) + guild.safetyAlertsChannelId = bot.transformers.snowflake(payload.guild.safety_alerts_channel_id) return bot.transformers.customizers.guild(bot, payload.guild, guild) } @@ -251,6 +253,8 @@ export interface Guild { welcomeScreen?: WelcomeScreen /** Stage instances in the guild */ stageInstances?: StageInstance[] - /** custom guild stickers */ + /** Custom guild stickers */ stickers?: Collection + /** The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord */ + safetyAlertsChannelId?: bigint } diff --git a/packages/bot/src/transformers/invite.ts b/packages/bot/src/transformers/invite.ts index 8ddccdc7d..7143f08f9 100644 --- a/packages/bot/src/transformers/invite.ts +++ b/packages/bot/src/transformers/invite.ts @@ -2,48 +2,54 @@ import type { DiscordApplication, DiscordInviteCreate, DiscordInviteMetadata } f import { isInviteWithMetadata, type Application, type Bot, type ScheduledEvent, type User } from '../index.js' import type { InviteStageInstance } from './stageInviteInstance.js' -export function transformInvite(bot: Bot, payload: DiscordInviteCreate | DiscordInviteMetadata): Invite { +export function transformInvite(bot: Bot, payload: { invite: DiscordInviteCreate | DiscordInviteMetadata; shardId: number }): Invite { const props = bot.transformers.desiredProperties.invite const invite = {} as Invite - const hasMetadata = isInviteWithMetadata(payload) - if (props.code && payload.code) invite.code = payload.code - if (props.createdAt && payload.created_at) invite.createdAt = Date.parse(payload.created_at) - if (props.inviter && payload.inviter) invite.inviter = bot.transformers.user(bot, payload.inviter) - if (props.maxAge && payload.max_age) invite.maxAge = payload.max_age - if (props.maxUses && payload.max_uses) invite.maxUses = payload.max_uses - if (props.targetType && payload.target_type) invite.targetType = payload.target_type - if (props.targetUser && payload.target_user) invite.targetUser = bot.transformers.user(bot, payload.target_user) - if (props.targetApplication && payload.target_application) - invite.targetApplication = bot.transformers.application(bot, payload.target_application as DiscordApplication) - if (props.temporary && payload.temporary) invite.temporary = payload.temporary - if (props.uses && payload.uses) invite.uses = payload.uses + if (props.code && payload.invite.code) invite.code = payload.invite.code + if (props.createdAt && payload.invite.created_at) invite.createdAt = Date.parse(payload.invite.created_at) + if (props.inviter && payload.invite.inviter) invite.inviter = bot.transformers.user(bot, payload.invite.inviter) + if (props.maxAge && payload.invite.max_age) invite.maxAge = payload.invite.max_age + if (props.maxUses && payload.invite.max_uses) invite.maxUses = payload.invite.max_uses + if (props.targetType && payload.invite.target_type) invite.targetType = payload.invite.target_type + if (props.targetUser && payload.invite.target_user) invite.targetUser = bot.transformers.user(bot, payload.invite.target_user) + if (props.targetApplication && payload.invite.target_application) + invite.targetApplication = bot.transformers.application(bot, { + application: payload.invite.target_application as DiscordApplication, + shardId: payload.shardId, + }) + if (props.temporary && payload.invite.temporary) invite.temporary = payload.invite.temporary + if (props.uses && payload.invite.uses) invite.uses = payload.invite.uses - if (hasMetadata) { - if (props.channelId && payload.channel?.id) invite.channelId = bot.transformers.snowflake(payload.channel.id) - if (props.guildId && payload.guild?.id) invite.guildId = bot.transformers.snowflake(payload.guild.id) - if (props.approximateMemberCount && payload.approximate_member_count) invite.approximateMemberCount = payload.approximate_member_count - if (props.approximatePresenceCount && payload.approximate_presence_count) invite.approximatePresenceCount = payload.approximate_presence_count - if (props.guildScheduledEvent && payload.guild_scheduled_event) { - invite.guildScheduledEvent = payload.guild_scheduled_event ? bot.transformers.scheduledEvent(bot, payload.guild_scheduled_event) : undefined + if (isInviteWithMetadata(payload.invite)) { + if (props.channelId && payload.invite.channel?.id) invite.channelId = bot.transformers.snowflake(payload.invite.channel.id) + if (props.guildId && payload.invite.guild?.id) invite.guildId = bot.transformers.snowflake(payload.invite.guild.id) + if (props.approximateMemberCount && payload.invite.approximate_member_count) + invite.approximateMemberCount = payload.invite.approximate_member_count + if (props.approximatePresenceCount && payload.invite.approximate_presence_count) + invite.approximatePresenceCount = payload.invite.approximate_presence_count + if (props.guildScheduledEvent && payload.invite.guild_scheduled_event) { + invite.guildScheduledEvent = payload.invite.guild_scheduled_event + ? bot.transformers.scheduledEvent(bot, payload.invite.guild_scheduled_event) + : undefined } - if (props.stageInstance && invite.guildId && payload.stage_instance) { - invite.stageInstance = payload.stage_instance + if (props.stageInstance && invite.guildId && payload.invite.stage_instance) { + invite.stageInstance = payload.invite.stage_instance ? bot.transformers.inviteStageInstance(bot, { - ...payload.stage_instance, + ...payload.invite.stage_instance, guildId: invite.guildId, }) : undefined } - if (props.expiresAt && payload.expires_at) { - invite.expiresAt = Date.parse(payload.expires_at) + if (props.expiresAt && payload.invite.expires_at) { + invite.expiresAt = Date.parse(payload.invite.expires_at) } } else { - if (props.channelId && payload.channel_id) invite.channelId = bot.transformers.snowflake(payload.channel_id) - if (props.guildId && payload.guild_id) invite.guildId = bot.transformers.snowflake(payload.guild_id) + if (props.channelId && payload.invite.channel_id) invite.channelId = bot.transformers.snowflake(payload.invite.channel_id) + if (props.guildId && payload.invite.guild_id) invite.guildId = bot.transformers.snowflake(payload.invite.guild_id) } - return bot.transformers.customizers.invite(bot, payload, invite) + return bot.transformers.customizers.invite(bot, payload.invite, invite) } export interface Invite { diff --git a/packages/bot/src/transformers/reverse/team.ts b/packages/bot/src/transformers/reverse/team.ts index 17e6cca6f..a165bc1fe 100644 --- a/packages/bot/src/transformers/reverse/team.ts +++ b/packages/bot/src/transformers/reverse/team.ts @@ -13,9 +13,9 @@ export function transformTeamToDiscordTeam(bot: Bot, payload: Team): DiscordTeam owner_user_id: payload.ownerUserId.toString(), members: payload.members.map((member) => ({ membership_state: member.membershipState, - permissions: member.permissions, team_id: id, user: bot.transformers.reverse.user(bot, member.user), + role: member.role, })), } } diff --git a/packages/bot/src/transformers/sku.ts b/packages/bot/src/transformers/sku.ts new file mode 100644 index 000000000..55cdca74b --- /dev/null +++ b/packages/bot/src/transformers/sku.ts @@ -0,0 +1,31 @@ +import type { DiscordSku, DiscordSkuFlag, DiscordSkuType } from '@discordeno/types' +import type { Bot } from '../index.js' + +export function transformSku(bot: Bot, payload: DiscordSku): Sku { + const props = bot.transformers.desiredProperties.sku + const sku = {} as Sku + + if (props.id && payload.id) sku.id = bot.transformers.snowflake(payload.id) + if (props.type && payload.type) sku.type = payload.type + if (props.applicationId && payload.application_id) sku.applicationId = bot.transformers.snowflake(payload.application_id) + if (props.name && payload.name) sku.name = payload.name + if (props.slug && payload.slug) sku.slug = payload.slug + if (props.flags && payload.flags) sku.flags = payload.flags + + return bot.transformers.customizers.sku(bot, payload, sku) +} + +export interface Sku { + /** ID of SKU */ + id: bigint + /** Type of SKU */ + type: DiscordSkuType + /** ID of the parent application */ + applicationId: bigint + /** Customer-facing name of your premium offering */ + name: string + /** System-generated URL slug based on the SKU's name */ + slug: string + /** SKU flags combined as a bitfield */ + flags: DiscordSkuFlag +} diff --git a/packages/bot/src/transformers/team.ts b/packages/bot/src/transformers/team.ts index 90e350449..4d128d00b 100644 --- a/packages/bot/src/transformers/team.ts +++ b/packages/bot/src/transformers/team.ts @@ -1,4 +1,4 @@ -import type { DiscordTeam, TeamMembershipStates } from '@discordeno/types' +import type { DiscordTeam, DiscordTeamMemberRole, TeamMembershipStates } from '@discordeno/types' import { iconHashToBigInt, type Bot, type User } from '../index.js' export function transformTeam(bot: Bot, payload: DiscordTeam): Team { @@ -11,9 +11,9 @@ export function transformTeam(bot: Bot, payload: DiscordTeam): Team { ownerUserId: bot.transformers.snowflake(payload.owner_user_id), members: payload.members.map((member) => ({ membershipState: member.membership_state, - permissions: member.permissions, teamId: id, user: bot.transformers.user(bot, member.user), + role: member.role, })), } as Team @@ -27,8 +27,8 @@ export interface Team { ownerUserId: bigint members: Array<{ membershipState: TeamMembershipStates - permissions: Array<'*'> teamId: bigint user: User + role: DiscordTeamMemberRole }> } diff --git a/packages/bot/src/typings.ts b/packages/bot/src/typings.ts index 547a47ae1..5bc030d62 100644 --- a/packages/bot/src/typings.ts +++ b/packages/bot/src/typings.ts @@ -204,6 +204,9 @@ export interface BotGatewayHandlerOptions { INTEGRATION_CREATE: typeof handlers.handleIntegrationCreate INTEGRATION_UPDATE: typeof handlers.handleIntegrationUpdate INTEGRATION_DELETE: typeof handlers.handleIntegrationDelete + ENTITLEMENT_CREATE: typeof handlers.handleEntitlementCreate + ENTITLEMENT_UPDATE: typeof handlers.handleEntitlementUpdate + ENTITLEMENT_DELETE: typeof handlers.handleEntitlementDelete } export enum MessageFlags { diff --git a/packages/rest/src/manager.ts b/packages/rest/src/manager.ts index a1d033a1a..8bb3dc9a3 100644 --- a/packages/rest/src/manager.ts +++ b/packages/rest/src/manager.ts @@ -22,6 +22,7 @@ import { type DiscordConnection, type DiscordCurrentAuthorization, type DiscordEmoji, + type DiscordEntitlement, type DiscordFollowedChannel, type DiscordGetGatewayBot, type DiscordGuild, @@ -42,6 +43,7 @@ import { type DiscordPrunedCount, type DiscordRole, type DiscordScheduledEvent, + type DiscordSku, type DiscordStageInstance, type DiscordSticker, type DiscordStickerPack, @@ -967,6 +969,12 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage return await rest.get(rest.routes.oauth2.application()) }, + async editApplicationInfo(body) { + return await rest.patch(rest.routes.oauth2.application(), { + body, + }) + }, + async getCurrentAuthenticationInfo(token) { return await rest.get(rest.routes.oauth2.currentAuthorization(), { headers: { @@ -1453,6 +1461,24 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage }) }, + async createTestEntitlement(applicationId, body) { + return await rest.post(rest.routes.monetization.entitlements(applicationId), { + body, + }) + }, + + async listEntitlements(applicationId, options) { + return await rest.get(rest.routes.monetization.entitlements(applicationId, options)) + }, + + async deleteTestEntitlement(applicationId, entitlementId) { + await rest.delete(rest.routes.monetization.entitlement(applicationId, entitlementId)) + }, + + async listSkus(applicationId) { + return await rest.get(rest.routes.monetization.skus(applicationId)) + }, + preferSnakeCase(enabled: boolean) { const camelizer = enabled ? (x: any) => x : camelize diff --git a/packages/rest/src/routes.ts b/packages/rest/src/routes.ts index 061df1267..cd343beba 100644 --- a/packages/rest/src/routes.ts +++ b/packages/rest/src/routes.ts @@ -579,6 +579,31 @@ export function createRoutes(): RestRoutes { }, }, + monetization: { + entitlements: (applicationId, options) => { + let url = `/applications/${applicationId}/entitlements?` + + if (options) { + if (options.after) url += `after=${options.after}` + if (options.before) url += `&before=${options.before}` + if (options.excludeEnded) url += `&exclude_ended=${options.excludeEnded}` + if (options.guildId) url += `&guild_id=${options.guildId}` + if (options.limit) url += `&limit=${options.limit}` + if (options.skuIds) url += `&sku_ids=${options.skuIds.join(',')}` + if (options.userId) url += `&user_id=${options.userId}` + } + + return url + }, + entitlement: (applicationId, entitlementId) => { + return `/applications/${applicationId}/entitlements/${entitlementId}` + }, + + skus: (applicationId) => { + return `/applications/${applicationId}/skus` + }, + }, + // User endpoints user(userId) { return `/users/${userId}` diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index 1940d1ec7..f33f6b342 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -19,6 +19,7 @@ import type { CamelizedDiscordConnection, CamelizedDiscordCurrentAuthorization, CamelizedDiscordEmoji, + CamelizedDiscordEntitlement, CamelizedDiscordFollowedChannel, CamelizedDiscordGetGatewayBot, CamelizedDiscordGuild, @@ -38,6 +39,7 @@ import type { CamelizedDiscordPrunedCount, CamelizedDiscordRole, CamelizedDiscordScheduledEvent, + CamelizedDiscordSku, CamelizedDiscordStageInstance, CamelizedDiscordSticker, CamelizedDiscordStickerPack, @@ -53,6 +55,7 @@ import type { CreateApplicationCommand, CreateAutoModerationRuleOptions, CreateChannelInvite, + CreateEntitlement, CreateForumPostWithMessage, CreateGlobalApplicationCommandOptions, CreateGuild, @@ -68,6 +71,7 @@ import type { CreateStageInstance, CreateTemplate, DeleteWebhookMessageOptions, + EditApplication, EditAutoModerationRuleOptions, EditBotMemberOptions, EditChannelPermissionOverridesOptions, @@ -82,14 +86,15 @@ import type { FileContent, GetApplicationCommandPermissionOptions, GetBans, + GetEntitlements, GetGroupDmOptions, GetGuildAuditLog, GetGuildPruneCountQuery, GetInvite, GetMessagesOptions, GetReactions, - GetScheduledEvents, GetScheduledEventUsers, + GetScheduledEvents, GetUserGuilds, GetWebhookMessageOptions, InteractionCallbackData, @@ -370,7 +375,7 @@ export interface RestManager { * @returns An instance of the created {@link CamelizedDiscordEmoji}. * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * Requires the `CREATE_GUILD_EXPRESSIONS` 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. * @@ -380,7 +385,7 @@ export interface RestManager { */ createEmoji: (guildId: BigString, options: CreateGuildEmoji, reason?: string) => Promise /** - * Creates a new thread in a forum channel, and sends a message within the created thread. + * Creates a new thread in a forum channel or media channel, and sends a message within the created thread. * * @param channelId - The ID of the forum channel to create the thread within. * @param options - The parameters for the creation of the thread. @@ -393,9 +398,7 @@ export interface RestManager { * 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 + * @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-or-media-channel} */ createForumThread: (channelId: BigString, options: CreateForumPostWithMessage, reason?: string) => Promise /** @@ -478,7 +481,7 @@ export interface RestManager { * @return A {@link CamelizedDiscordSticker} * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * Requires the `CREATE_GUILD_EXPRESSIONS` permission. * Fires a Guild Stickers Update Gateway event. * Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots. * Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` guild feature. @@ -651,7 +654,8 @@ export interface RestManager { * @param {string} [reason] - An optional reason for the action, to be included in the audit log. * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + * For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. * * Fires a _Guild Emojis Update_ gateway event. * @@ -710,7 +714,8 @@ export interface RestManager { * @return A {@link CamelizedDiscordSticker} * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + * For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. * Fires a Guild Stickers Update Gateway event. * Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots. * Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` guild feature. @@ -1077,7 +1082,8 @@ export interface RestManager { * @returns An instance of the updated {@link CamelizedDiscordEmoji}. * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + * For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. * * Fires a `Guild Emojis Update` gateway event. * @@ -1159,7 +1165,8 @@ export interface RestManager { * @return A {@link CamelizedDiscordSticker} * * @remarks - * Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. + * For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + * For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. * Fires a Guild Stickers Update Gateway event. * * @see {@link https://discord.com/developers/docs/resources/sticker#modify-guild-sticker} @@ -1497,6 +1504,13 @@ export interface RestManager { getActiveThreads: (guildId: BigString) => Promise /** Get the applications info */ getApplicationInfo: () => Promise + /** + * Edit properties of the app associated with the requesting bot user. + * + * @remarks + * Only properties that are passed will be updated. + */ + editApplicationInfo: (body: EditApplication) => Promise /** * Get the current authentication info for the authenticated user * @@ -1712,6 +1726,10 @@ export interface RestManager { * @param emojiId - The ID of the emoji to get. * @returns An instance of {@link CamelizedDiscordEmoji}. * + * @remarks + * Includes the `user` field if the bot has the `MANAGE_GUILD_EXPRESSIONS` permission, + * or if the bot created the emoji and has the the `CREATE_GUILD_EXPRESSIONS` permission. + * * @see {@link https://discord.com/developers/docs/resources/emoji#get-guild-emoji} */ getEmoji: (guildId: BigString, emojiId: BigString) => Promise @@ -1721,6 +1739,9 @@ export interface RestManager { * @param guildId - The ID of the guild which to get the emojis of. * @returns A collection of {@link CamelizedDiscordEmoji} objects assorted by emoji ID. * + * @remarks + * Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + * * @see {@link https://discord.com/developers/docs/resources/emoji#list-guild-emojis} */ getEmojis: (guildId: BigString) => Promise @@ -1821,7 +1842,7 @@ export interface RestManager { * @param stickerId The ID of the sticker to get * @return A {@link CamelizedDiscordSticker} * - * @remarks Includes the user field if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission. + * @remarks Includes the user field if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. * * @see {@link https://discord.com/developers/docs/resources/sticker#get-guild-sticker} */ @@ -1832,7 +1853,7 @@ export interface RestManager { * @param guildId The ID of the guild to get * @returns A collection of {@link CamelizedDiscordSticker} objects assorted by sticker ID. * - * @remarks Includes user fields if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission. + * @remarks Includes user fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. * * @see {@link https://discord.com/developers/docs/resources/sticker#list-guild-stickers} */ @@ -2274,6 +2295,9 @@ export interface RestManager { * @param guildId - The ID of the guild to get the widget of. * @returns An instance of {@link GuildWidget}. * + * @remarks + * Fires an `INVITE_CREATED` Gateway event when an invite channel is defined and a new `Invite` is generated. + * * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-widget} */ getWidget: (guildId: BigString) => Promise @@ -2513,12 +2537,14 @@ export interface RestManager { */ syncGuildTemplate: (guildId: BigString) => Promise /** - * Triggers a typing indicator for the bot user. + * Triggers a typing indicator for the specified channel, which expires after 10 seconds. * * @param channelId - The ID of the channel in which to trigger the typing indicator. * * @remarks - * Generally, bots should _not_ use this route. + * Generally bots should **not** use 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. * * Fires a _Typing Start_ gateway event. * @@ -2783,6 +2809,37 @@ export interface RestManager { * The `mode` field modifies what is considered when enforcing these constraints. */ editGuildOnboarding: (guildId: BigString, options: EditGuildOnboarding, reason?: string) => Promise + /** + * Returns all entitlements for a given app, active and expired. + * + * @param applicationId - The id of the application to get the entitlements + * @param {GetEntitlements} [options] - The optional query params for the endpoint + */ + listEntitlements: (applicationId: BigString, options?: GetEntitlements) => Promise + /** + * Creates a test entitlement to a given SKU for a given guild or user. Discord will act as though that user or guild has entitlement to your premium offering. + * + * @param applicationId - The id of the application to create the entitlement + * @param body - The options for new entitlement + * + * @remarks + * This endpoint returns a partial entitlement object. + * It will not contain subscription_id, starts_at, or ends_at, as it's valid in perpetuity. + */ + createTestEntitlement: (applicationId: BigString, body: CreateEntitlement) => Promise> + /** + * Deletes a currently-active test entitlement. Discord will act as though that user or guild no longer has entitlement to your premium offering. + * + * @param applicationId - The id of the application from where delete the entitlement + * @param entitlementId - The id of the entitlement to delete + */ + deleteTestEntitlement: (applicationId: BigString, entitlementId: BigString) => Promise + /** + * Returns all SKUs for a given application + * + * @param applicationId - The id of the application to get the SKUs + */ + listSkus: (applicationId: BigString) => Promise } export type RequestMethods = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'PUT' diff --git a/packages/rest/src/typings/routes.ts b/packages/rest/src/typings/routes.ts index ea07caa4f..7b8dce13c 100644 --- a/packages/rest/src/typings/routes.ts +++ b/packages/rest/src/typings/routes.ts @@ -1,6 +1,7 @@ import type { BigString, GetBans, + GetEntitlements, GetGuildAuditLog, GetGuildPruneCountQuery, GetInvite, @@ -254,6 +255,15 @@ export interface RestRoutes { /** Route to handling role-connection for an application */ roleConnections: (applicationId: BigString) => string } + /** Routes related to monetization (entitlements and SKU) */ + monetization: { + /** Route to list / create entitlements */ + entitlements: (applicationId: BigString, options?: GetEntitlements) => string + /** Route to delete an entitlement */ + entitlement: (applicationId: BigString, entitlementId: BigString) => string + /** Route to list the SKUs */ + skus: (applicationId: BigString) => string + } /** Get information about the current OAuth2 user / bot user. If used with a OAuth2 token requires the `identify` OAuth2 scope */ currentUser: () => string /** Route for handling a sticker. */ diff --git a/packages/types/src/camel.ts b/packages/types/src/camel.ts index 2c1e596aa..57c74bee3 100644 --- a/packages/types/src/camel.ts +++ b/packages/types/src/camel.ts @@ -52,6 +52,7 @@ import type { DiscordEmbedThumbnail, DiscordEmbedVideo, DiscordEmoji, + DiscordEntitlement, DiscordFollowAnnouncementChannel, DiscordFollowedChannel, DiscordForumTag, @@ -130,6 +131,7 @@ import type { DiscordSelectMenuDefaultValue, DiscordSelectOption, DiscordSessionStartLimit, + DiscordSku, DiscordStageInstance, DiscordSticker, DiscordStickerItem, @@ -320,3 +322,5 @@ export interface CamelizedDiscordPrunedCount extends Camelize {} export interface CamelizedDiscordGuildOnboardingPrompt extends Camelize {} export interface CamelizedDiscordGuildOnboardingOption extends Camelize {} +export interface CamelizedDiscordEntitlement extends Camelize {} +export interface CamelizedDiscordSku extends Camelize {} diff --git a/packages/types/src/discord.ts b/packages/types/src/discord.ts index 8c6870f6d..186229bd8 100644 --- a/packages/types/src/discord.ts +++ b/packages/types/src/discord.ts @@ -107,7 +107,7 @@ export enum OAuth2Scope { * This scope requires Discord approval to be used */ ApplicationsBuildsUpload = 'applications.builds.upload', - /** Allows your app to use Application Commands in a guild */ + /** Allows your app to add commands to a guild - included by default with the `bot` scope */ ApplicationsCommands = 'applications.commands', /** * Allows your app to update its Application Commands via this bearer token @@ -363,8 +363,10 @@ export interface DiscordApplication { owner?: Partial /** If the application belongs to a team, this will be a list of the members of that team */ team: DiscordTeam | null - /** If this application is a game sold on Discord, this field will be the guild to which it has been linked */ + /** Guild associated with the app. For example, a developer support server. */ guild_id?: string + /** A partial object of the associated guild */ + guild?: Partial /** If this application is a game sold on Discord, this field will be the hash of the image on store embeds */ cover_image?: string /** up to 5 tags describing the content and functionality of the application */ @@ -375,6 +377,14 @@ export interface DiscordApplication { custom_install_url?: string /** the application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration */ role_connections_verification_url?: string + /** An approximate count of the app's guild membership. */ + approximate_guild_count?: number + /** Partial user object for the bot user associated with the app */ + bot?: Partial + /** Array of redirect URIs for the app */ + redirect_uris?: string[] + /** Interactions endpoint URL for the app */ + interactions_endpoint_url?: string } export type DiscordTokenExchange = DiscordTokenExchangeAuthorizationCode | DiscordTokenExchangeRefreshToken | DiscordTokenExchangeClientCredentials @@ -508,15 +518,15 @@ export interface DiscordApplicationRoleConnection { /** https://discord.com/developers/docs/topics/teams#data-models-team-object */ export interface DiscordTeam { - /** A hash of the image of the team's icon */ + /** Hash of the image of the team's icon */ icon: string | null - /** The unique id of the team */ + /** Unique ID of the team */ id: string - /** The members of the team */ + /** Members of the team */ members: DiscordTeamMember[] - /** The user id of the current team owner */ + /** User ID of the current team owner */ owner_user_id: string - /** The name of the team */ + /** Name of the team */ name: string } @@ -524,12 +534,12 @@ export interface DiscordTeam { export interface DiscordTeamMember { /** The user's membership state on the team */ membership_state: TeamMembershipStates - /** Will always be `["*"]` */ - permissions: Array<'*'> /** The id of the parent team of which they are a member */ team_id: string /** The avatar, discriminator, id, username, and global_name of the user */ user: Partial & Pick + /** Role of the team member */ + role: DiscordTeamMemberRole } /** https://discord.com/developers/docs/topics/gateway#webhooks-update-webhook-update-event-fields */ @@ -848,8 +858,10 @@ export interface DiscordGuild { welcome_screen?: DiscordWelcomeScreen /** Stage instances in the guild */ stage_instances?: DiscordStageInstance[] - /** custom guild stickers */ + /** Custom guild stickers */ stickers?: DiscordSticker[] + /** The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord */ + safety_alerts_channel_id: string | null } export interface DiscordPartialGuild { @@ -1297,7 +1309,7 @@ export interface DiscordMessage { /** Data showing the source of a crossposted channel follow add, pin or reply message */ message_reference?: Omit /** Message flags combined as a bitfield */ - flags?: number + flags?: DiscordMessageFlag /** * The stickers sent with the message (bots currently can only receive messages with stickers, not send) * @deprecated @@ -1585,6 +1597,8 @@ export interface DiscordInteraction { guild_locale?: string /** The computed permissions for a bot or app in the context of a specific interaction (including channel overwrites) */ app_permissions: string + /** For monetized apps, any entitlements for the invoking user, representing access to premium SKUs */ + entitlements: DiscordEntitlement[] } /** https://discord.com/developers/docs/resources/guild#guild-member-object */ @@ -1738,8 +1752,10 @@ export interface DiscordAutoModerationRuleTriggerMetadata { presets?: DiscordAutoModerationRuleTriggerMetadataPresets[] /** The substrings which will exempt from triggering the preset trigger type. Only present when TriggerType.KeywordPreset */ allow_list?: string[] - /** Total number of mentions (role & user) allowed per message (Maximum of 50) */ + /** Total number of mentions (role & user) allowed per message (Maximum of 50). Only present when TriggerType.MentionSpam */ mention_total_limit?: number + /** Whether to automatically detect mention raids. Only present when TriggerType.MentionSpam */ + mention_raid_protection_enabled?: boolean } export enum DiscordAutoModerationRuleTriggerMetadataPresets { @@ -2644,9 +2660,9 @@ export interface DiscordGuildWidgetSettings { } export interface DiscordInstallParams { - /** the scopes to add the application to the server with */ + /** Scopes to add the application to the server with */ scopes: OAuth2Scope[] - /** the permissions to request for the bot role */ + /** Permissions to request for the bot role */ permissions: string } @@ -2896,8 +2912,8 @@ export interface DiscordCreateForumPostWithMessage { payload_json?: string /** Attachment objects with filename and description. See {@link https://discord.com/developers/docs/reference#uploading-files Uploading Files} */ attachments?: DiscordAttachment[] - /** Message flags combined as a bitfield (only SUPPRESS_EMBEDS can be set) */ - flags?: number + /** Message flags combined as a bitfield, only SUPPRESS_EMBEDS can be set */ + flags?: DiscordMessageFlag } /** the IDs of the set of tags that have been applied to a thread in a GUILD_FORUM channel */ applied_tags?: string[] @@ -3008,3 +3024,107 @@ export enum DiscordGuildOnboardingMode { /** Counts Default Channels and Questions towards constraints */ OnboardingAdvanced, } + +/** https://discord.com/developers/docs/topics/teams#team-member-roles-team-member-role-types */ +export enum DiscordTeamMemberRole { + /** Owners are the most permissiable role, and can take destructive, irreversible actions like deleting the team itself. Teams are limited to 1 owner. */ + Owner = 'owner', + /** Admins have similar access as owners, except they cannot take destructive actions on the team or team-owned apps. */ + Admin = 'admin', + /** + * Developers can access information about team-owned apps, like the client secret or public key. + * They can also take limited actions on team-owned apps, like configuring interaction endpoints or resetting the bot token. + * Members with the Developer role *cannot* manage the team or its members, or take destructive actions on team-owned apps. + */ + Developer = 'developer', + /** Read-only members can access information about a team and any team-owned apps. Some examples include getting the IDs of applications and exporting payout records. */ + ReadOnly = 'read_only', +} + +/** https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-structure */ +export interface DiscordEntitlement { + /** ID of the entitlement */ + id: string + /** ID of the SKU */ + sku_id: string + /** ID of the user that is granted access to the entitlement's sku */ + user_id?: string + /** ID of the guild that is granted access to the entitlement's sku */ + guild_id?: string + /** ID of the parent application */ + application_id: string + /** Type of entitlement */ + type: DiscordEntitlementType + /** Entitlement was deleted */ + deleted: boolean + /** Start date at which the entitlement is valid. Not present when using test entitlements */ + starts_at?: string + /** Date at which the entitlement is no longer valid. Not present when using test entitlements */ + ends_at?: string +} + +/** https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types */ +export enum DiscordEntitlementType { + /** Entitlement was purchased as an app subscription */ + ApplicationSubscription = 8, +} + +/** https://discord.com/developers/docs/monetization/skus#sku-object-sku-structure */ +export interface DiscordSku { + /** ID of SKU */ + id: string + /** Type of SKU */ + type: DiscordSkuType + /** ID of the parent application */ + application_id: string + /** Customer-facing name of your premium offering */ + name: string + /** System-generated URL slug based on the SKU's name */ + slug: string + /** SKU flags combined as a bitfield */ + flags: DiscordSkuFlag +} + +/** https://discord.com/developers/docs/monetization/skus#sku-object-sku-types */ +export enum DiscordSkuType { + /** Represents a recurring subscription */ + Subscription = 5, + /** System-generated group for each SUBSCRIPTION SKU created */ + SubscriptionGroup = 6, +} + +/** https://discord.com/developers/docs/monetization/skus#sku-object-sku-flags */ +export enum DiscordSkuFlag { + /** SKU is available for purchase */ + Available = 1 << 2, + /** Recurring SKU that can be purchased by a user and applied to a single server. Grants access to every user in that server. */ + GuildSubscription = 1 << 7, + /** Recurring SKU purchased by a user for themselves. Grants access to the purchasing user in every server. */ + UserSubscription = 1 << 8, +} + +/** https://discord.com/developers/docs/resources/channel#message-object-message-flags */ +export enum DiscordMessageFlag { + /** This message has been published to subscribed channels (via Channel Following) */ + Crossposted = 1 << 0, + /** This message originated from a message in another channel (via Channel Following) */ + IsCrosspost = 1 << 1, + /** Do not include any embeds when serializing this message */ + SuppressEmbeds = 1 << 2, + /** The source message for this crosspost has been deleted (via Channel Following) */ + SourceMessageDeleted = 1 << 3, + /** This message came from the urgent message system */ + Urgent = 1 << 4, + /** This message has an associated thread, with the same id as the message */ + HasThread = 1 << 5, + /** This message is only visible to the user who invoked the Interaction */ + Ephemeral = 1 << 6, + /** This message is an Interaction Response and the bot is "thinking" */ + Loading = 1 << 7, + /** This message failed to mention some roles and add their members to the thread */ + FailedToMentionSomeRolesInThread = 1 << 8, + /** This message will not trigger push and desktop notifications */ + SuppressNotifications = 1 << 12, + /** This message is a voice message */ + IsVoiceMessage = 1 << 13, +} diff --git a/packages/types/src/discordeno.ts b/packages/types/src/discordeno.ts index d16d07433..87e06111e 100644 --- a/packages/types/src/discordeno.ts +++ b/packages/types/src/discordeno.ts @@ -10,12 +10,15 @@ import type { DiscordEmbed, DiscordGuildOnboardingMode, DiscordGuildOnboardingPrompt, + DiscordInstallParams, + DiscordMessageFlag, DiscordRole, } from './discord.js' import type { AllowedMentionsTypes, ApplicationCommandPermissionTypes, ApplicationCommandTypes, + ApplicationFlags, AuditLogEvents, BigString, ButtonStyles, @@ -40,6 +43,7 @@ import type { VideoQualityModes, } from './shared.js' +/** https://discord.com/developers/docs/resources/channel#create-message-jsonform-params */ export interface CreateMessageOptions { /** The message contents (up to 2000 characters) */ content?: string @@ -71,6 +75,8 @@ export interface CreateMessageOptions { components?: MessageComponents /** IDs of up to 3 stickers in the server to send in the message */ stickerIds?: [BigString] | [BigString, BigString] | [BigString, BigString, BigString] + /** Message flags combined as a bitfield, only SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS can be set */ + flags?: DiscordMessageFlag } export type MessageComponents = ActionRow[] @@ -569,8 +575,10 @@ export interface CreateGuildChannel { /** The unicode character of the emoji */ emojiName?: string }> - /** the default sort order type used to order posts in forum channels */ + /** The default sort order type used to order posts in forum channels */ defaultSortOrder?: SortOrderTypes | null + /** The initial ratelimit to set on newly created threads in a channel. */ + defaultThreadRateLimitPerUser?: number } export interface CreateGlobalApplicationCommandOptions { @@ -716,6 +724,8 @@ export interface ExecuteWebhook { threadId?: BigString /** Name of the thread to create (target channel has to be type of forum channel) */ threadName?: string + /** Array of tag ids to apply to the thread (requires the webhook channel to be a forum or media channel) */ + appliedTags?: BigString[] /** The message contents (up to 2000 characters) */ content?: string /** Override the default username of the webhook */ @@ -751,23 +761,36 @@ export interface CreateForumPostWithMessage { autoArchiveDuration: 60 | 1440 | 4320 | 10080 /** Amount of seconds a user has to wait before sending another message (0-21600) */ rateLimitPerUser?: number | null - /** The message contents (up to 2000 characters) */ - content?: string - /** Embedded `rich` content (up to 6000 characters) */ - embeds?: Array> - /** Allowed mentions for the message */ - allowedMentions?: AllowedMentions + /** contents of the first message in the forum/media thread */ + message: { + /** The message contents (up to 2000 characters) */ + content?: string + /** Embedded `rich` content (up to 6000 characters) */ + embeds?: Array> + /** Allowed mentions for the message */ + allowedMentions?: AllowedMentions + /** The components you would like to have sent in this message */ + components?: MessageComponents + /** IDs of up to 3 stickers in the server to send in the message */ + stickerIds?: BigString[] + /** Message flags combined as a bitfield, only SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS can be set */ + flags?: DiscordMessageFlag + } + /** The IDs of the set of tags that have been applied to a thread in a GUILD_FORUM or a GUILD_MEDIA channel */ + appliedTags?: BigString[] /** The contents of the files being sent */ files?: FileContent[] - /** The components you would like to have sent in this message */ - components?: MessageComponents } export interface CreateStageInstance { + /** The id of the Stage channel */ channelId: BigString + /** The topic of the Stage instance (1-120 characters) */ topic: string /** Notify @everyone that the stage instance has started. Requires the MENTION_EVERYONE permission. */ sendStartNotification?: boolean + /** The guild scheduled event associated with this Stage instance */ + guildScheduledEventId?: BigString } export interface EditStageInstanceOptions { @@ -942,7 +965,7 @@ export interface EditMessage { /** Embedded `rich` content (up to 6000 characters) */ embeds?: Array> | null /** Edit the flags of the message (only `SUPPRESS_EMBEDS` can currently be set/unset) */ - flags?: 4 | null + flags?: DiscordMessageFlag | null /** The contents of the files being sent/edited */ files?: FileContent[] /** Allowed mentions for the message */ @@ -1202,3 +1225,75 @@ export interface EditGuildOnboarding { /** Current mode of onboarding */ mode: DiscordGuildOnboardingMode } + +/** https://discord.com/developers/docs/monetization/entitlements#list-entitlements-query-params */ +export interface GetEntitlements { + /** User ID to look up entitlements for */ + userId?: BigString + /** Optional list of SKU IDs to check entitlements for */ + skuIds?: BigString[] + /** Retrieve entitlements before this entitlement ID */ + before?: BigString + /** Retrieve entitlements after this entitlement ID */ + after?: BigString + /** Number of entitlements to return, 1-100, default 100 */ + limit?: number + /** Guild ID to look up entitlements for */ + guildId?: BigString + /** Whether or not ended entitlements should be omitted */ + excludeEnded?: boolean +} + +/** https://discord.com/developers/docs/monetization/entitlements#create-test-entitlement-json-params */ +export interface CreateEntitlement { + /** ID of the SKU to grant the entitlement to */ + skuId: BigString + /** ID of the guild or user to grant the entitlement to */ + ownerId: BigString + /** The type of entitlement, guild subscription or user subscription */ + ownerType: CreateEntitlementOwnerType +} + +/** From the description of CreateEntitlement#ownerType on discord docs */ +export enum CreateEntitlementOwnerType { + /** Guild subscription */ + GuildSubscription = 1, + /** User subscription */ + UserSubscription = 2, +} + +export interface EditApplication { + /** Default custom authorization URL for the app, if enabled */ + customInstallUrl?: string + /** Description of the app */ + description?: string + /** Role connection verification URL for the app */ + roleConnectionsVerificationUrl?: string + /** Settings for the app's default in-app authorization link, if enabled */ + installParams?: DiscordInstallParams + /** + * App's public flags + * + * @remarks + * Only limited intent flags (`GATEWAY_PRESENCE_LIMITED`, `GATEWAY_GUILD_MEMBERS_LIMITED`, and `GATEWAY_MESSAGE_CONTENT_LIMITED`) can be updated via the API. + */ + flags?: ApplicationFlags + /** Icon for the app */ + icon?: string | null + /** Default rich presence invite cover image for the app */ + coverImage?: string | null + /** + * Interactions endpoint URL for the app + * + * @remarks + * To update an Interactions endpoint URL via the API, the URL must be valid + */ + interactionEndpointUrl?: string + /** + * List of tags describing the content and functionality of the app (max of 20 characters per tag) + * + * @remarks + * There can only be a max of 5 tags + */ + tags?: string[] +} diff --git a/packages/types/src/shared.ts b/packages/types/src/shared.ts index 788dc77fa..d721529cf 100644 --- a/packages/types/src/shared.ts +++ b/packages/types/src/shared.ts @@ -242,6 +242,8 @@ export enum GuildFeatures { InvitesDisabled = 'INVITES_DISABLED', /** Guild has access to set an animated guild banner image */ AnimatedBanner = 'ANIMATED_BANNER', + /** Guild has disabled alerts for join raids in the configured safety alerts channel */ + RaidAlertsDisabled = 'RAID_ALERTS_DISABLED', } /** https://discord.com/developers/docs/resources/guild#guild-object-mfa-level */ @@ -635,13 +637,13 @@ export enum BitwisePermissionFlags { MANAGE_ROLES = 0x0000000010000000, /** Allows management and editing of webhooks */ MANAGE_WEBHOOKS = 0x0000000020000000, - /** Allows management and editing of emojis, stickers, and soundboard sounds */ + /** Allows for editing and deleting emojis, stickers, and soundboard sounds created by all users */ MANAGE_GUILD_EXPRESSIONS = 0x0000000040000000, /** Allows members to use application commands in text channels */ USE_SLASH_COMMANDS = 0x0000000080000000, /** Allows for requesting to speak in stage channels. */ REQUEST_TO_SPEAK = 0x0000000100000000, - /** Allows for creating, editing, and deleting scheduled events */ + /** Allows for editing and deleting scheduled events created by all users */ MANAGE_EVENTS = 0x0000000200000000, /** Allows for deleting and archiving threads, and viewing all private threads */ MANAGE_THREADS = 0x0000000400000000, @@ -661,6 +663,10 @@ export enum BitwisePermissionFlags { VIEW_CREATOR_MONETIZATION_ANALYTICS = 0x0000020000000000, /** Allows for using soundboard in a voice channel. */ USE_SOUNDBOARD = 0x0000040000000000, + /** Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user */ + CREATE_GUILD_EXPRESSIONS = 0x0000080000000000, + /** Allows for creating scheduled events, and editing and deleting those created by the current user */ + CREATE_EVENTS = 0x0000100000000000, /** Allows the usage of custom soundboards sounds from other servers */ USE_EXTERNAL_SOUNDS = 0x0000200000000000, /** Allows sending voice messages */ @@ -790,6 +796,9 @@ export type GatewayDispatchEventNames = | 'VOICE_STATE_UPDATE' | 'VOICE_SERVER_UPDATE' | 'WEBHOOKS_UPDATE' + | 'ENTITLEMENT_CREATE' + | 'ENTITLEMENT_UPDATE' + | 'ENTITLEMENT_DELETE' export type GatewayEventNames = GatewayDispatchEventNames | 'READY' | 'RESUMED' @@ -947,6 +956,8 @@ export enum InteractionResponseTypes { ApplicationCommandAutocompleteResult = 8, /** For Command or Component interactions, send a Modal response */ Modal = 9, + /** Respond to an interaction with an upgrade button, only available for apps with monetization enabled */ + PremiumRequired = 10, } export enum SortOrderTypes {