mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
feat(bot,rest,types): Add subscriptions (#3892)
* Add subscriptions * Apply suggestions from code review Co-authored-by: Awesome Stickz <awesome@stickz.dev> --------- Co-authored-by: Awesome Stickz <awesome@stickz.dev>
This commit is contained in:
@@ -32,6 +32,7 @@ import type {
|
||||
Role,
|
||||
ScheduledEvent,
|
||||
Sticker,
|
||||
Subscription,
|
||||
ThreadMember,
|
||||
User,
|
||||
VoiceState,
|
||||
@@ -282,6 +283,9 @@ export interface EventHandlers {
|
||||
entitlementCreate: (entitlement: Entitlement) => unknown
|
||||
entitlementUpdate: (entitlement: Entitlement) => unknown
|
||||
entitlementDelete: (entitlement: Entitlement) => unknown
|
||||
subscriptionCreate: (subscription: Subscription) => unknown
|
||||
subscriptionUpdate: (subscription: Subscription) => unknown
|
||||
subscriptionDelete: (subscription: Subscription) => unknown
|
||||
messagePollVoteAdd: (payload: { userId: bigint; channelId: bigint; messageId: bigint; guildId?: bigint; answerId: number }) => unknown
|
||||
messagePollVoteRemove: (payload: { userId: bigint; channelId: bigint; messageId: bigint; guildId?: bigint; answerId: number }) => unknown
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ export function createBotGatewayHandlers(
|
||||
ENTITLEMENT_DELETE: options.ENTITLEMENT_DELETE ?? handlers.handleEntitlementDelete,
|
||||
MESSAGE_POLL_VOTE_ADD: options.MESSAGE_POLL_VOTE_ADD ?? handlers.handleMessagePollVoteAdd,
|
||||
MESSAGE_POLL_VOTE_REMOVE: options.MESSAGE_POLL_VOTE_REMOVE ?? handlers.handleMessagePollVoteRemove,
|
||||
SUBSCRIPTION_CREATE: options.SUBSCRIPTION_CREATE ?? handlers.handleSubscriptionCreate,
|
||||
SUBSCRIPTION_UPDATE: options.SUBSCRIPTION_UPDATE ?? handlers.handleSubscriptionUpdate,
|
||||
SUBSCRIPTION_DELETE: options.SUBSCRIPTION_DELETE ?? handlers.handleSubscriptionDelete,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,3 +12,4 @@ export * from './poll/index.js'
|
||||
export * from './roles/index.js'
|
||||
export * from './voice/index.js'
|
||||
export * from './webhooks/index.js'
|
||||
export * from './subscriptions/index.js'
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { DiscordGatewayPayload, DiscordSubscription } from '@discordeno/types'
|
||||
import type { Bot } from '../../bot.js'
|
||||
|
||||
export async function handleSubscriptionCreate(bot: Bot, data: DiscordGatewayPayload): Promise<void> {
|
||||
if (!bot.events.subscriptionCreate) return
|
||||
|
||||
const payload = data.d as DiscordSubscription
|
||||
bot.events.subscriptionCreate(bot.transformers.subscription(bot, payload))
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { DiscordGatewayPayload, DiscordSubscription } from '@discordeno/types'
|
||||
import type { Bot } from '../../bot.js'
|
||||
|
||||
export async function handleSubscriptionDelete(bot: Bot, data: DiscordGatewayPayload): Promise<void> {
|
||||
if (!bot.events.subscriptionDelete) return
|
||||
|
||||
const payload = data.d as DiscordSubscription
|
||||
bot.events.subscriptionDelete(bot.transformers.subscription(bot, payload))
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { DiscordGatewayPayload, DiscordSubscription } from '@discordeno/types'
|
||||
import type { Bot } from '../../bot.js'
|
||||
|
||||
export async function handleSubscriptionUpdate(bot: Bot, data: DiscordGatewayPayload): Promise<void> {
|
||||
if (!bot.events.subscriptionUpdate) return
|
||||
|
||||
const payload = data.d as DiscordSubscription
|
||||
bot.events.subscriptionUpdate(bot.transformers.subscription(bot, payload))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './SUBSCRIPTION_CREATE.js'
|
||||
export * from './SUBSCRIPTION_DELETE.js'
|
||||
export * from './SUBSCRIPTION_UPDATE.js'
|
||||
@@ -50,6 +50,7 @@ import type {
|
||||
DiscordActivityInstance,
|
||||
DiscordEntitlement,
|
||||
DiscordMessage,
|
||||
DiscordSubscription,
|
||||
EditApplication,
|
||||
EditAutoModerationRuleOptions,
|
||||
EditBotMemberOptions,
|
||||
@@ -80,6 +81,7 @@ import type {
|
||||
InteractionResponse,
|
||||
ListArchivedThreads,
|
||||
ListGuildMembers,
|
||||
ListSkuSubscriptionsOptions,
|
||||
MfaLevels,
|
||||
ModifyApplicationEmoji,
|
||||
ModifyChannel,
|
||||
@@ -762,6 +764,12 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
listSkus: async (applicationId) => {
|
||||
return (await bot.rest.listSkus(applicationId)).map((sku) => bot.transformers.sku(bot, snakelize(sku)))
|
||||
},
|
||||
getSubscription: async (skuId, subscriptionId) => {
|
||||
return await bot.rest.getSubscription(skuId, subscriptionId)
|
||||
},
|
||||
listSubscriptions: async (skuId, options) => {
|
||||
return await bot.rest.listSubscriptions(skuId, options)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,4 +1013,6 @@ export interface BotHelpers {
|
||||
createTestEntitlement: (applicationId: BigString, body: CreateEntitlement) => Promise<Partial<Entitlement>>
|
||||
deleteTestEntitlement: (applicationId: BigString, entitlementId: BigString) => Promise<void>
|
||||
listSkus: (applicationId: BigString) => Promise<Sku[]>
|
||||
listSubscriptions: (skuId: BigString, options?: ListSkuSubscriptionsOptions) => Promise<Camelize<DiscordSubscription[]>>
|
||||
getSubscription: (skuId: BigString, subscriptionId: BigString) => Promise<Camelize<DiscordSubscription>>
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import type {
|
||||
DiscordStageInstance,
|
||||
DiscordSticker,
|
||||
DiscordStickerPack,
|
||||
DiscordSubscription,
|
||||
DiscordTeam,
|
||||
DiscordTemplate,
|
||||
DiscordThreadMember,
|
||||
@@ -118,6 +119,7 @@ import {
|
||||
type StageInstance,
|
||||
type Sticker,
|
||||
type StickerPack,
|
||||
type Subscription,
|
||||
type Team,
|
||||
type Template,
|
||||
type ThreadMember,
|
||||
@@ -204,6 +206,7 @@ import {
|
||||
transformCreateApplicationCommandToDiscordCreateApplicationCommand,
|
||||
transformInteractionResponseToDiscordInteractionResponse,
|
||||
} from './transformers/reverse/index.js'
|
||||
import { transformSubscription } from './transformers/subscription.js'
|
||||
import type {
|
||||
BotInteractionResponse,
|
||||
DiscordComponent,
|
||||
@@ -280,6 +283,7 @@ export interface Transformers {
|
||||
stageInstance: (bot: Bot, payload: DiscordStageInstance, stageInstance: StageInstance) => any
|
||||
sticker: (bot: Bot, payload: DiscordSticker, sticker: Sticker) => any
|
||||
stickerPack: (bot: Bot, payload: DiscordStickerPack, stickerPack: StickerPack) => any
|
||||
subscription: (bot: Bot, payload: DiscordSubscription, subscription: Subscription) => any
|
||||
team: (bot: Bot, payload: DiscordTeam, team: Team) => any
|
||||
template: (bot: Bot, payload: DiscordTemplate, template: Template) => any
|
||||
threadMember: (bot: Bot, payload: DiscordThreadMember, threadMember: ThreadMember) => any
|
||||
@@ -360,6 +364,7 @@ export interface Transformers {
|
||||
stageInstance: (bot: Bot, payload: DiscordStageInstance) => StageInstance
|
||||
sticker: (bot: Bot, payload: DiscordSticker) => Sticker
|
||||
stickerPack: (bot: Bot, payload: DiscordStickerPack) => StickerPack
|
||||
subscription: (bot: Bot, payload: DiscordSubscription) => Subscription
|
||||
team: (bot: Bot, payload: DiscordTeam) => Team
|
||||
template: (bot: Bot, payload: DiscordTemplate) => Template
|
||||
threadMember: (bot: Bot, payload: DiscordThreadMember) => ThreadMember
|
||||
@@ -722,6 +727,17 @@ export interface TransformersDesiredProperties {
|
||||
user: boolean
|
||||
sortValue: boolean
|
||||
}
|
||||
subscription: {
|
||||
id: boolean
|
||||
userId: boolean
|
||||
skuIds: boolean
|
||||
entitlementIds: boolean
|
||||
currentPeriodStart: boolean
|
||||
currentPeriodEnd: boolean
|
||||
status: boolean
|
||||
canceledAt: boolean
|
||||
country: boolean
|
||||
}
|
||||
user: {
|
||||
username: boolean
|
||||
globalName: boolean
|
||||
@@ -905,6 +921,7 @@ export function createTransformers(options: RecursivePartial<Transformers>, opts
|
||||
stageInstance: options.customizers?.stageInstance ?? defaultCustomizer,
|
||||
sticker: options.customizers?.sticker ?? defaultCustomizer,
|
||||
stickerPack: options.customizers?.stickerPack ?? defaultCustomizer,
|
||||
subscription: options.customizers?.subscription ?? defaultCustomizer,
|
||||
team: options.customizers?.team ?? defaultCustomizer,
|
||||
template: options.customizers?.template ?? defaultCustomizer,
|
||||
threadMember: options.customizers?.threadMember ?? defaultCustomizer,
|
||||
@@ -986,6 +1003,7 @@ export function createTransformers(options: RecursivePartial<Transformers>, opts
|
||||
stageInstance: options.stageInstance ?? transformStageInstance,
|
||||
sticker: options.sticker ?? transformSticker,
|
||||
stickerPack: options.stickerPack ?? transformStickerPack,
|
||||
subscription: options.subscription ?? transformSubscription,
|
||||
team: options.team ?? transformTeam,
|
||||
template: options.template ?? transformTemplate,
|
||||
threadMember: options.threadMember ?? transformThreadMember,
|
||||
@@ -1379,6 +1397,18 @@ export function createDesiredPropertiesObject(
|
||||
sortValue: defaultValue,
|
||||
...desiredProperties.sticker,
|
||||
},
|
||||
subscription: {
|
||||
id: defaultValue,
|
||||
userId: defaultValue,
|
||||
skuIds: defaultValue,
|
||||
entitlementIds: defaultValue,
|
||||
currentPeriodStart: defaultValue,
|
||||
currentPeriodEnd: defaultValue,
|
||||
status: defaultValue,
|
||||
canceledAt: defaultValue,
|
||||
country: defaultValue,
|
||||
...desiredProperties.subscription,
|
||||
},
|
||||
user: {
|
||||
username: defaultValue,
|
||||
globalName: defaultValue,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { DiscordSubscription } from '@discordeno/types'
|
||||
import type { Bot, Subscription } from '../index.js'
|
||||
|
||||
export function transformSubscription(bot: Bot, payload: DiscordSubscription): Subscription {
|
||||
const props = bot.transformers.desiredProperties.subscription
|
||||
const subscription = {} as Subscription
|
||||
|
||||
if (props.id && payload.id) subscription.id = bot.transformers.snowflake(payload.id)
|
||||
if (props.userId && payload.user_id) subscription.userId = bot.transformers.snowflake(payload.user_id)
|
||||
if (props.skuIds && payload.sku_ids) subscription.skuIds = payload.sku_ids.map((skuId) => bot.transformers.snowflake(skuId))
|
||||
if (props.entitlementIds && payload.entitlement_ids) subscription.entitlementIds = payload.entitlement_ids.map((entitlementId) => bot.transformers.snowflake(entitlementId))
|
||||
if (props.currentPeriodStart && payload.current_period_start) subscription.currentPeriodStart = Date.parse(payload.current_period_start)
|
||||
if (props.currentPeriodEnd && payload.current_period_end) subscription.currentPeriodEnd = Date.parse(payload.current_period_end)
|
||||
if (props.status && payload.status) subscription.status = payload.status
|
||||
if (props.canceledAt && payload.canceled_at) subscription.canceledAt = Date.parse(payload.canceled_at)
|
||||
if (props.country && payload.country) subscription.country = payload.country
|
||||
|
||||
return bot.transformers.customizers.subscription(bot, payload, subscription)
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import type {
|
||||
DiscordScheduledEventRecurrenceRuleNWeekday,
|
||||
DiscordScheduledEventRecurrenceRuleWeekday,
|
||||
DiscordSkuType,
|
||||
DiscordSubscriptionStatus,
|
||||
DiscordTeamMemberRole,
|
||||
DiscordTemplateSerializedSourceGuild,
|
||||
DiscordWebhookEventType,
|
||||
@@ -1741,3 +1742,24 @@ export interface GuildWidgetSettings {
|
||||
channelId?: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface Subscription {
|
||||
/** ID of the subscription */
|
||||
id: bigint
|
||||
/** ID of the user who is subscribed */
|
||||
userId: bigint
|
||||
/** List of SKUs subscribed to */
|
||||
skuIds: bigint[]
|
||||
/** List of entitlements granted for this subscription */
|
||||
entitlementIds: bigint[]
|
||||
/** Start of the current subscription period */
|
||||
currentPeriodStart: number
|
||||
/** End of the current subscription period */
|
||||
currentPeriodEnd: number
|
||||
/** Current status of the subscription */
|
||||
status: DiscordSubscriptionStatus
|
||||
/** When the subscription was canceled */
|
||||
canceledAt: number
|
||||
/** ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. */
|
||||
country?: string
|
||||
}
|
||||
|
||||
@@ -207,6 +207,9 @@ export interface BotGatewayHandlerOptions {
|
||||
ENTITLEMENT_CREATE: typeof handlers.handleEntitlementCreate
|
||||
ENTITLEMENT_UPDATE: typeof handlers.handleEntitlementUpdate
|
||||
ENTITLEMENT_DELETE: typeof handlers.handleEntitlementDelete
|
||||
SUBSCRIPTION_CREATE: typeof handlers.handleSubscriptionCreate
|
||||
SUBSCRIPTION_UPDATE: typeof handlers.handleSubscriptionUpdate
|
||||
SUBSCRIPTION_DELETE: typeof handlers.handleSubscriptionDelete
|
||||
MESSAGE_POLL_VOTE_ADD: typeof handlers.handleMessagePollVoteAdd
|
||||
MESSAGE_POLL_VOTE_REMOVE: typeof handlers.handleMessagePollVoteRemove
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
type DiscordStageInstance,
|
||||
type DiscordSticker,
|
||||
type DiscordStickerPack,
|
||||
type DiscordSubscription,
|
||||
type DiscordTemplate,
|
||||
type DiscordThreadMember,
|
||||
type DiscordUser,
|
||||
@@ -1629,6 +1630,14 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.get<DiscordSku[]>(rest.routes.monetization.skus(applicationId))
|
||||
},
|
||||
|
||||
async listSubscriptions(skuId, options) {
|
||||
return await rest.get<DiscordSubscription[]>(rest.routes.monetization.subscriptions(skuId, options))
|
||||
},
|
||||
|
||||
async getSubscription(skuId, subscriptionId) {
|
||||
return await rest.get<DiscordSubscription>(rest.routes.monetization.subscription(skuId, subscriptionId))
|
||||
},
|
||||
|
||||
preferSnakeCase(enabled: boolean) {
|
||||
const camelizer = enabled ? (x: any) => x : camelize
|
||||
|
||||
|
||||
@@ -593,6 +593,23 @@ export function createRoutes(): RestRoutes {
|
||||
skus: (applicationId) => {
|
||||
return `/applications/${applicationId}/skus`
|
||||
},
|
||||
|
||||
subscription: (skuId, subscriptionId) => {
|
||||
return `/skus/${skuId}/subscriptions/${subscriptionId}`
|
||||
},
|
||||
|
||||
subscriptions: (skuId, options) => {
|
||||
let url = `/skus/${skuId}/subscriptions?`
|
||||
|
||||
if (options) {
|
||||
if (options.after) url += `after=${options.after}`
|
||||
if (options.before) url += `&before=${options.before}`
|
||||
if (options.userId) url += `&user_id=${options.userId}`
|
||||
if (options.limit) url += `&limit=${options.limit}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
},
|
||||
|
||||
applicationEmoji(applicationId, emojiId) {
|
||||
|
||||
@@ -86,6 +86,7 @@ import type {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
DiscordInteraction,
|
||||
DiscordInteractionCallbackResponse,
|
||||
DiscordSubscription,
|
||||
EditApplication,
|
||||
EditAutoModerationRuleOptions,
|
||||
EditBotMemberOptions,
|
||||
@@ -119,6 +120,7 @@ import type {
|
||||
InteractionResponse,
|
||||
ListArchivedThreads,
|
||||
ListGuildMembers,
|
||||
ListSkuSubscriptionsOptions,
|
||||
MfaLevels,
|
||||
ModifyApplicationEmoji,
|
||||
ModifyChannel,
|
||||
@@ -3028,6 +3030,18 @@ export interface RestManager {
|
||||
* @param applicationId - The id of the application to get the SKUs
|
||||
*/
|
||||
listSkus: (applicationId: BigString) => Promise<CamelizedDiscordSku[]>
|
||||
/**
|
||||
* Returns all subscriptions containing the SKU, filtered by user.
|
||||
*
|
||||
* @param skuId - The id of the sku of get the subscriptions for
|
||||
*/
|
||||
listSubscriptions: (skuId: BigString, options?: ListSkuSubscriptionsOptions) => Promise<Camelize<DiscordSubscription[]>>
|
||||
/**
|
||||
* Get a subscription by its ID.
|
||||
*
|
||||
* @param skuId - The id of the sku of get the subscriptions for
|
||||
*/
|
||||
getSubscription: (skuId: BigString, subscriptionId: BigString) => Promise<Camelize<DiscordSubscription>>
|
||||
}
|
||||
|
||||
export type RequestMethods = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'PUT'
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
InteractionCallbackOptions,
|
||||
ListArchivedThreads,
|
||||
ListGuildMembers,
|
||||
ListSkuSubscriptionsOptions,
|
||||
} from '@discordeno/types'
|
||||
|
||||
export interface RestRoutes {
|
||||
@@ -274,6 +275,10 @@ export interface RestRoutes {
|
||||
consumeEntitlement: (applicationId: BigString, entitlementId: BigString) => string
|
||||
/** Route to list the SKUs */
|
||||
skus: (applicationId: BigString) => string
|
||||
/** Route to list the SKU subscriptions */
|
||||
subscriptions: (skuId: BigString, options?: ListSkuSubscriptionsOptions) => string
|
||||
/** Route to get a SKU subscription */
|
||||
subscription: (skuId: BigString, subscriptionId: BigString) => string
|
||||
}
|
||||
/** Route to list / create an application emoji */
|
||||
applicationEmojis: (applicationId: BigString) => string
|
||||
|
||||
@@ -3778,6 +3778,38 @@ export enum DiscordSkuType {
|
||||
SubscriptionGroup = 6,
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/subscription#subscription-object */
|
||||
export interface DiscordSubscription {
|
||||
/** ID of the subscription */
|
||||
id: string
|
||||
/** ID of the user who is subscribed */
|
||||
user_id: string
|
||||
/** List of SKUs subscribed to */
|
||||
sku_ids: string[]
|
||||
/** List of entitlements granted for this subscription */
|
||||
entitlement_ids: string[]
|
||||
/** Start of the current subscription period */
|
||||
current_period_start: string
|
||||
/** End of the current subscription period */
|
||||
current_period_end: string
|
||||
/** Current status of the subscription */
|
||||
status: DiscordSubscriptionStatus
|
||||
/** When the subscription was canceled */
|
||||
canceled_at: string | null
|
||||
/** ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. */
|
||||
country?: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/subscription#subscription-statuses */
|
||||
export enum DiscordSubscriptionStatus {
|
||||
/** Subscription is active and scheduled to renew. */
|
||||
Active,
|
||||
/** Subscription is active but will not renew. */
|
||||
Ending,
|
||||
/** Subscription is inactive and not being charged. */
|
||||
Inactive,
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types */
|
||||
export enum DiscordInteractionContextType {
|
||||
/** Interaction can be used within servers */
|
||||
|
||||
@@ -1586,3 +1586,15 @@ export interface GetPollAnswerVotes {
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/subscription#query-string-params */
|
||||
export interface ListSkuSubscriptionsOptions {
|
||||
/** List subscriptions before this ID */
|
||||
before?: BigString
|
||||
/** List subscriptions after this ID */
|
||||
after?: BigString
|
||||
/** Number of results to return (1-100) */
|
||||
limit?: number
|
||||
/** User ID for which to return subscriptions. Required except for OAuth queries. */
|
||||
userId?: BigString
|
||||
}
|
||||
|
||||
@@ -941,6 +941,9 @@ export type GatewayDispatchEventNames =
|
||||
| 'ENTITLEMENT_CREATE'
|
||||
| 'ENTITLEMENT_UPDATE'
|
||||
| 'ENTITLEMENT_DELETE'
|
||||
| 'SUBSCRIPTION_CREATE'
|
||||
| 'SUBSCRIPTION_UPDATE'
|
||||
| 'SUBSCRIPTION_DELETE'
|
||||
| 'MESSAGE_POLL_VOTE_ADD'
|
||||
| 'MESSAGE_POLL_VOTE_REMOVE'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user