mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-03 17:30:07 +00:00
feat: Implement OAuth2 endpoints (#3114)
* add OAuth2 routes * Add oauth2 methods to rest * Add rest manager methods, Add token params * Add authorization headers * Add auth to editUserApplicationRoleConnection * fix logging header always displaying bot as auth * Add OAuth2Scope enum * Start testing ratelimit handling * Fix now scopes are separated by a space * move webhook object to DiscordAccessTokenResponse * convert payload to snake_case * fix some typings * more types fixes * add support for upserting commands with tokens * handle correctly ratelimit and concurrently * add guild to DiscordAccessTokenResponse * Add oauth2 create link function * Fix removeTokenPrefix to support Bearer tokens * update jsdoc comment for removeTokenPrefix * fix removeTokenPrefix unit tests * fix see link on getMember and getCurrentMember * add bot helpers and fix some types * Use objects to pass the bearer tokens * fix Deno issue with Buffer.from * Merge 'upstream/main' into feat/oauth2 to fix merge conflict * Fix debug queue logging * keep only 1 route for current user * add Bearer prefixed url to the rest of the logs --------- Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import type { CreateWebhook } from '@discordeno/rest'
|
||||
import type {
|
||||
AddDmRecipientOptions,
|
||||
AddGuildMemberOptions,
|
||||
ApplicationCommandPermissions,
|
||||
AtLeastOne,
|
||||
BeginGuildPrune,
|
||||
BigString,
|
||||
CamelizedDiscordAccessTokenResponse,
|
||||
CamelizedDiscordApplicationRoleConnection,
|
||||
CamelizedDiscordArchivedThreads,
|
||||
CamelizedDiscordAuditLog,
|
||||
CamelizedDiscordBan,
|
||||
CamelizedDiscordConnection,
|
||||
CamelizedDiscordCurrentAuthorization,
|
||||
CamelizedDiscordFollowedChannel,
|
||||
CamelizedDiscordGetGatewayBot,
|
||||
CamelizedDiscordGuildPreview,
|
||||
@@ -14,14 +20,19 @@ import type {
|
||||
CamelizedDiscordInvite,
|
||||
CamelizedDiscordInviteMetadata,
|
||||
CamelizedDiscordModifyGuildWelcomeScreen,
|
||||
CamelizedDiscordPartialGuild,
|
||||
CamelizedDiscordPrunedCount,
|
||||
CamelizedDiscordTokenExchange,
|
||||
CamelizedDiscordTokenRevocation,
|
||||
CamelizedDiscordVanityUrl,
|
||||
CamelizedDiscordVoiceRegion,
|
||||
CreateApplicationCommand,
|
||||
CreateAutoModerationRuleOptions,
|
||||
CreateChannelInvite,
|
||||
CreateForumPostWithMessage,
|
||||
CreateGlobalApplicationCommandOptions,
|
||||
CreateGuild,
|
||||
CreateGuildApplicationCommandOptions,
|
||||
CreateGuildBan,
|
||||
CreateGuildChannel,
|
||||
CreateGuildEmoji,
|
||||
@@ -44,7 +55,9 @@ import type {
|
||||
EditScheduledEvent,
|
||||
EditUserVoiceState,
|
||||
ExecuteWebhook,
|
||||
GetApplicationCommandPermissionOptions,
|
||||
GetBans,
|
||||
GetGroupDmOptions,
|
||||
GetGuildAuditLog,
|
||||
GetGuildPruneCountQuery,
|
||||
GetInvite,
|
||||
@@ -52,6 +65,7 @@ import type {
|
||||
GetReactions,
|
||||
GetScheduledEventUsers,
|
||||
GetScheduledEvents,
|
||||
GetUserGuilds,
|
||||
GetWebhookMessageOptions,
|
||||
InteractionCallbackData,
|
||||
InteractionResponse,
|
||||
@@ -69,6 +83,8 @@ import type {
|
||||
SearchMembers,
|
||||
StartThreadWithMessage,
|
||||
StartThreadWithoutMessage,
|
||||
UpsertGlobalApplicationCommandOptions,
|
||||
UpsertGuildApplicationCommandOptions,
|
||||
} from '@discordeno/types'
|
||||
import { snakelize } from '@discordeno/utils'
|
||||
import type { Bot } from './bot.js'
|
||||
@@ -108,14 +124,14 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
createForumThread: async (channelId, options, reason) => {
|
||||
return bot.transformers.channel(bot, { channel: snakelize(await bot.rest.createForumThread(channelId, options, reason)) })
|
||||
},
|
||||
createGlobalApplicationCommand: async (command) => {
|
||||
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.createGlobalApplicationCommand(command)))
|
||||
createGlobalApplicationCommand: async (command, options) => {
|
||||
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.createGlobalApplicationCommand(command, options)))
|
||||
},
|
||||
createGuild: async (options) => {
|
||||
return bot.transformers.guild(bot, { guild: snakelize(await bot.rest.createGuild(options)), shardId: 0 })
|
||||
},
|
||||
createGuildApplicationCommand: async (command, guildId) => {
|
||||
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.createGuildApplicationCommand(command, guildId)))
|
||||
createGuildApplicationCommand: async (command, guildId, options) => {
|
||||
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.createGuildApplicationCommand(command, guildId, options)))
|
||||
},
|
||||
createGuildFromTemplate: async (templateCode, options) => {
|
||||
return bot.transformers.guild(bot, { guild: snakelize(await bot.rest.createGuildFromTemplate(templateCode, options)), shardId: 0 })
|
||||
@@ -235,11 +251,23 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
getApplicationInfo: async () => {
|
||||
return bot.transformers.application(bot, snakelize(await bot.rest.getApplicationInfo()))
|
||||
},
|
||||
getApplicationCommandPermission: async (guildId, commandId) => {
|
||||
return bot.transformers.applicationCommandPermission(bot, snakelize(await bot.rest.getApplicationCommandPermission(guildId, commandId)))
|
||||
getCurrentAuthenticationInfo: async (bearerToken) => {
|
||||
return await bot.rest.getCurrentAuthenticationInfo(bearerToken)
|
||||
},
|
||||
getApplicationCommandPermissions: async (guildId) => {
|
||||
return (await bot.rest.getApplicationCommandPermissions(guildId)).map((res) =>
|
||||
exchangeToken: async (options) => {
|
||||
return await bot.rest.exchangeToken(options)
|
||||
},
|
||||
revokeToken: async (options) => {
|
||||
return await bot.rest.revokeToken(options)
|
||||
},
|
||||
getApplicationCommandPermission: async (guildId, commandId, options) => {
|
||||
const res = await bot.rest.getApplicationCommandPermission(guildId, commandId, options)
|
||||
const snakedRes = snakelize(res)
|
||||
|
||||
return bot.transformers.applicationCommandPermission(bot, snakedRes)
|
||||
},
|
||||
getApplicationCommandPermissions: async (guildId, options) => {
|
||||
return (await bot.rest.getApplicationCommandPermissions(guildId, options)).map((res) =>
|
||||
bot.transformers.applicationCommandPermission(bot, snakelize(res)),
|
||||
)
|
||||
},
|
||||
@@ -277,6 +305,9 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
getDmChannel: async (userId) => {
|
||||
return bot.transformers.channel(bot, { channel: snakelize(await bot.rest.getDmChannel(userId)) })
|
||||
},
|
||||
getGroupDmChannel: async (options) => {
|
||||
return bot.transformers.channel(bot, { channel: snakelize(await bot.rest.getGroupDmChannel(options)) })
|
||||
},
|
||||
getEmoji: async (guildId, emojiId) => {
|
||||
return bot.transformers.emoji(bot, snakelize(await bot.rest.getEmoji(guildId, emojiId)))
|
||||
},
|
||||
@@ -298,6 +329,9 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
getGuild: async (guildId, options) => {
|
||||
return bot.transformers.guild(bot, { guild: snakelize(await bot.rest.getGuild(guildId, options)), shardId: 0 })
|
||||
},
|
||||
getGuilds: async (bearerToken, options) => {
|
||||
return await bot.rest.getGuilds(bearerToken, options)
|
||||
},
|
||||
getGuildApplicationCommand: async (commandId, guildId) => {
|
||||
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.getGuildApplicationCommand(commandId, guildId)))
|
||||
},
|
||||
@@ -405,6 +439,15 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
getUser: async (id) => {
|
||||
return bot.transformers.user(bot, snakelize(await bot.rest.getUser(id)))
|
||||
},
|
||||
getCurrentUser: async (bearerToken) => {
|
||||
return bot.transformers.user(bot, snakelize(await bot.rest.getCurrentUser(bearerToken)))
|
||||
},
|
||||
getUserConnections: async (bearerToken) => {
|
||||
return await bot.rest.getUserConnections(bearerToken)
|
||||
},
|
||||
getUserApplicationRoleConnection: async (bearerToken, applicationId) => {
|
||||
return await bot.rest.getUserApplicationRoleConnection(bearerToken, applicationId)
|
||||
},
|
||||
getVanityUrl: async (guildId) => {
|
||||
return await bot.rest.getVanityUrl(guildId)
|
||||
},
|
||||
@@ -449,11 +492,15 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
syncGuildTemplate: async (guildId) => {
|
||||
return bot.transformers.template(bot, snakelize(await bot.rest.syncGuildTemplate(guildId)))
|
||||
},
|
||||
upsertGlobalApplicationCommands: async (commands) => {
|
||||
return (await bot.rest.upsertGlobalApplicationCommands(commands)).map((res) => bot.transformers.applicationCommand(bot, snakelize(res)))
|
||||
upsertGlobalApplicationCommands: async (commands, options) => {
|
||||
return (await bot.rest.upsertGlobalApplicationCommands(commands, options)).map((res) =>
|
||||
bot.transformers.applicationCommand(bot, snakelize(res)),
|
||||
)
|
||||
},
|
||||
upsertGuildApplicationCommands: async (guildId, commands) => {
|
||||
return (await bot.rest.upsertGuildApplicationCommands(guildId, commands)).map((res) => bot.transformers.applicationCommand(bot, snakelize(res)))
|
||||
upsertGuildApplicationCommands: async (guildId, commands, options) => {
|
||||
return (await bot.rest.upsertGuildApplicationCommands(guildId, commands, options)).map((res) =>
|
||||
bot.transformers.applicationCommand(bot, snakelize(res)),
|
||||
)
|
||||
},
|
||||
editBotMember: async (guildId, options, reason) => {
|
||||
return bot.transformers.member(bot, snakelize(await bot.rest.editBotMember(guildId, options, reason)), guildId, bot.id)
|
||||
@@ -464,6 +511,10 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
getMember: async (guildId, userId) => {
|
||||
return bot.transformers.member(bot, snakelize(await bot.rest.getMember(guildId, userId)), guildId, userId)
|
||||
},
|
||||
getCurrentMember: async (guildId, bearerToken) => {
|
||||
const res = await bot.rest.getCurrentMember(guildId, bearerToken)
|
||||
return bot.transformers.member(bot, snakelize(res), guildId, bot.transformers.snowflake(res.user.id))
|
||||
},
|
||||
getMembers: async (guildId, options) => {
|
||||
return (await bot.rest.getMembers(guildId, options)).map((res) =>
|
||||
bot.transformers.member(bot, snakelize(res), guildId, bot.transformers.snowflake(res.user.id)),
|
||||
@@ -490,6 +541,12 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
addThreadMember: async (channelId, userId) => {
|
||||
return await bot.rest.addThreadMember(channelId, userId)
|
||||
},
|
||||
addDmRecipient: async (channelId, userId, options) => {
|
||||
return await bot.rest.addDmRecipient(channelId, userId, options)
|
||||
},
|
||||
addGuildMember: async (guildId, userId, options) => {
|
||||
return await bot.rest.addGuildMember(guildId, userId, options)
|
||||
},
|
||||
deleteAutomodRule: async (guildId, ruleId, reason) => {
|
||||
return await bot.rest.deleteAutomodRule(guildId, ruleId, reason)
|
||||
},
|
||||
@@ -580,6 +637,9 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
editUserVoiceState: async (guildId, options) => {
|
||||
return await bot.rest.editUserVoiceState(guildId, options)
|
||||
},
|
||||
editUserApplicationRoleConnection: async (bearerToken, applicationId, options) => {
|
||||
return await bot.rest.editUserApplicationRoleConnection(bearerToken, applicationId, options)
|
||||
},
|
||||
joinThread: async (channelId) => {
|
||||
return await bot.rest.joinThread(channelId)
|
||||
},
|
||||
@@ -595,6 +655,9 @@ export function createBotHelpers(bot: Bot): BotHelpers {
|
||||
removeThreadMember: async (channelId, userId) => {
|
||||
return await bot.rest.removeThreadMember(channelId, userId)
|
||||
},
|
||||
removeDmRecipient: async (channelId, userId) => {
|
||||
return await bot.rest.removeDmRecipient(channelId, userId)
|
||||
},
|
||||
sendInteractionResponse: async (interactionId, token, options) => {
|
||||
return await bot.rest.sendInteractionResponse(interactionId, token, options)
|
||||
},
|
||||
@@ -624,9 +687,13 @@ export interface BotHelpers {
|
||||
createChannel: (guildId: BigString, options: CreateGuildChannel, reason?: string) => Promise<Channel>
|
||||
createEmoji: (guildId: BigString, options: CreateGuildEmoji, reason?: string) => Promise<Emoji>
|
||||
createForumThread: (channelId: BigString, options: CreateForumPostWithMessage, reason?: string) => Promise<Channel>
|
||||
createGlobalApplicationCommand: (command: CreateApplicationCommand) => Promise<ApplicationCommand>
|
||||
createGlobalApplicationCommand: (command: CreateApplicationCommand, options?: CreateGlobalApplicationCommandOptions) => Promise<ApplicationCommand>
|
||||
createGuild: (options: CreateGuild) => Promise<Guild>
|
||||
createGuildApplicationCommand: (command: CreateApplicationCommand, guildId: BigString) => Promise<ApplicationCommand>
|
||||
createGuildApplicationCommand: (
|
||||
command: CreateApplicationCommand,
|
||||
guildId: BigString,
|
||||
options?: CreateGuildApplicationCommandOptions,
|
||||
) => Promise<ApplicationCommand>
|
||||
createGuildFromTemplate: (templateCode: string, options: CreateGuildFromTemplate) => Promise<Guild>
|
||||
createGuildSticker: (guildId: BigString, options: CreateGuildStickerOptions, reason?: string) => Promise<Sticker>
|
||||
createGuildTemplate: (guildId: BigString, options: CreateTemplate) => Promise<Template>
|
||||
@@ -673,12 +740,24 @@ export interface BotHelpers {
|
||||
editWebhookWithToken: (webhookId: BigString, token: string, options: Omit<ModifyWebhook, 'channelId'>) => Promise<Webhook>
|
||||
editWelcomeScreen: (guildId: BigString, options: CamelizedDiscordModifyGuildWelcomeScreen, reason?: string) => Promise<WelcomeScreen>
|
||||
editWidgetSettings: (guildId: BigString, options: CamelizedDiscordGuildWidgetSettings, reason?: string) => Promise<GuildWidgetSettings>
|
||||
editUserApplicationRoleConnection: (
|
||||
bearerToken: string,
|
||||
applicationId: BigString,
|
||||
options: CamelizedDiscordApplicationRoleConnection,
|
||||
) => Promise<CamelizedDiscordApplicationRoleConnection>
|
||||
executeWebhook: (webhookId: BigString, token: string, options: ExecuteWebhook) => Promise<Message | undefined>
|
||||
followAnnouncement: (sourceChannelId: BigString, targetChannelId: BigString) => Promise<CamelizedDiscordFollowedChannel>
|
||||
getActiveThreads: (guildId: BigString) => Promise<{ threads: Channel[]; members: ThreadMember[] }>
|
||||
getApplicationInfo: () => Promise<Application>
|
||||
getApplicationCommandPermission: (guildId: BigString, commandId: BigString) => Promise<ApplicationCommandPermission>
|
||||
getApplicationCommandPermissions: (guildId: BigString) => Promise<ApplicationCommandPermission[]>
|
||||
getCurrentAuthenticationInfo: (bearerToken: string) => Promise<CamelizedDiscordCurrentAuthorization>
|
||||
exchangeToken: (options: CamelizedDiscordTokenExchange) => Promise<CamelizedDiscordAccessTokenResponse>
|
||||
revokeToken: (options: CamelizedDiscordTokenRevocation) => Promise<void>
|
||||
getApplicationCommandPermission: (
|
||||
guildId: BigString,
|
||||
commandId: BigString,
|
||||
options?: GetApplicationCommandPermissionOptions,
|
||||
) => Promise<ApplicationCommandPermission>
|
||||
getApplicationCommandPermissions: (guildId: BigString, options?: GetApplicationCommandPermissionOptions) => Promise<ApplicationCommandPermission[]>
|
||||
getAuditLog: (guildId: BigString, options?: GetGuildAuditLog) => Promise<CamelizedDiscordAuditLog>
|
||||
getAutomodRule: (guildId: BigString, ruleId: BigString) => Promise<AutoModerationRule>
|
||||
getAutomodRules: (guildId: BigString) => Promise<AutoModerationRule[]>
|
||||
@@ -690,6 +769,7 @@ export interface BotHelpers {
|
||||
getChannels: (guildId: BigString) => Promise<Channel[]>
|
||||
getChannelWebhooks: (channelId: BigString) => Promise<Webhook[]>
|
||||
getDmChannel: (userId: BigString) => Promise<Channel>
|
||||
getGroupDmChannel: (options: GetGroupDmOptions) => Promise<Channel>
|
||||
getEmoji: (guildId: BigString, emojiId: BigString) => Promise<Emoji>
|
||||
getEmojis: (guildId: BigString) => Promise<Emoji[]>
|
||||
getFollowupMessage: (token: string, messageId: BigString) => Promise<Message>
|
||||
@@ -697,6 +777,7 @@ export interface BotHelpers {
|
||||
getGlobalApplicationCommand: (commandId: BigString) => Promise<ApplicationCommand>
|
||||
getGlobalApplicationCommands: () => Promise<ApplicationCommand[]>
|
||||
getGuild: (guildId: BigString, options?: { counts?: boolean }) => Promise<Guild>
|
||||
getGuilds: (bearerToken: string, options?: GetUserGuilds) => Promise<CamelizedDiscordPartialGuild[]>
|
||||
getGuildApplicationCommand: (commandId: BigString, guildId: BigString) => Promise<ApplicationCommand>
|
||||
getGuildApplicationCommands: (guildId: BigString) => Promise<ApplicationCommand[]>
|
||||
getGuildPreview: (guildId: BigString) => Promise<CamelizedDiscordGuildPreview>
|
||||
@@ -732,6 +813,9 @@ export interface BotHelpers {
|
||||
getThreadMembers: (channelId: BigString) => Promise<ThreadMember[]>
|
||||
getReactions: (channelId: BigString, messageId: BigString, reaction: string, options?: GetReactions) => Promise<User[]>
|
||||
getUser: (id: BigString) => Promise<User>
|
||||
getCurrentUser: (bearerToken: string) => Promise<User>
|
||||
getUserConnections: (bearerToken: string) => Promise<CamelizedDiscordConnection[]>
|
||||
getUserApplicationRoleConnection: (bearerToken: string, applicationId: BigString) => Promise<CamelizedDiscordApplicationRoleConnection>
|
||||
getVanityUrl: (guildId: BigString) => Promise<CamelizedDiscordVanityUrl>
|
||||
getVoiceRegions: (guildId: BigString) => Promise<CamelizedDiscordVoiceRegion[]>
|
||||
getWebhook: (webhookId: BigString) => Promise<Webhook>
|
||||
@@ -746,11 +830,19 @@ export interface BotHelpers {
|
||||
startThreadWithMessage: (channelId: BigString, messageId: BigString, options: StartThreadWithMessage, reason?: string) => Promise<Channel>
|
||||
startThreadWithoutMessage: (channelId: BigString, options: StartThreadWithoutMessage, reason?: string) => Promise<Channel>
|
||||
syncGuildTemplate: (guildId: BigString) => Promise<Template>
|
||||
upsertGlobalApplicationCommands: (commands: CreateApplicationCommand[]) => Promise<ApplicationCommand[]>
|
||||
upsertGuildApplicationCommands: (guildId: BigString, commands: CreateApplicationCommand[]) => Promise<ApplicationCommand[]>
|
||||
upsertGlobalApplicationCommands: (
|
||||
commands: CreateApplicationCommand[],
|
||||
options?: UpsertGlobalApplicationCommandOptions,
|
||||
) => Promise<ApplicationCommand[]>
|
||||
upsertGuildApplicationCommands: (
|
||||
guildId: BigString,
|
||||
commands: CreateApplicationCommand[],
|
||||
options?: UpsertGuildApplicationCommandOptions,
|
||||
) => Promise<ApplicationCommand[]>
|
||||
editBotMember: (guildId: BigString, options: EditBotMemberOptions, reason?: string) => Promise<Member>
|
||||
editMember: (guildId: BigString, userId: BigString, options: ModifyGuildMember, reason?: string) => Promise<Member>
|
||||
getMember: (guildId: BigString, userId: BigString) => Promise<Member>
|
||||
getCurrentMember: (guildId: BigString, bearerToken: string) => Promise<Member>
|
||||
getMembers: (guildId: BigString, options: ListGuildMembers) => Promise<Member[]>
|
||||
pruneMembers: (guildId: BigString, options: BeginGuildPrune, reason?: string) => Promise<{ pruned: number | null }>
|
||||
searchMembers: (guildId: BigString, query: string, options?: Omit<SearchMembers, 'query'>) => Promise<Member[]>
|
||||
@@ -759,6 +851,8 @@ export interface BotHelpers {
|
||||
addReactions: (channelId: BigString, messageId: BigString, reactions: string[], ordered?: boolean) => Promise<void>
|
||||
addRole: (guildId: BigString, userId: BigString, roleId: BigString, reason?: string) => Promise<void>
|
||||
addThreadMember: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
addDmRecipient: (channelId: BigString, userId: BigString, options: AddDmRecipientOptions) => Promise<void>
|
||||
addGuildMember: (guildId: BigString, userId: BigString, options: AddGuildMemberOptions) => Promise<void>
|
||||
deleteAutomodRule: (guildId: BigString, ruleId: BigString, reason?: string) => Promise<void>
|
||||
deleteChannel: (channelId: BigString, reason?: string) => Promise<void>
|
||||
deleteChannelPermissionOverride: (channelId: BigString, overwriteId: BigString, reason?: string) => Promise<void>
|
||||
@@ -794,6 +888,7 @@ export interface BotHelpers {
|
||||
leaveThread: (channelId: BigString) => Promise<void>
|
||||
removeRole: (guildId: BigString, userId: BigString, roleId: BigString, reason?: string) => Promise<void>
|
||||
removeThreadMember: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
removeDmRecipient: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
sendInteractionResponse: (interactionId: BigString, token: string, options: InteractionResponse) => Promise<void>
|
||||
triggerTypingIndicator: (channelId: BigString) => Promise<void>
|
||||
banMember: (guildId: BigString, userId: BigString, options?: CreateGuildBan, reason?: string) => Promise<void>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable no-const-assign */
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
import { calculateBits, camelToSnakeCase, camelize, delay, getBotIdFromToken, logger, processReactionString, urlToBase64 } from '@discordeno/utils'
|
||||
|
||||
import { createInvalidRequestBucket } from './invalidBucket.js'
|
||||
@@ -9,12 +11,16 @@ import {
|
||||
InteractionResponseTypes,
|
||||
type BigString,
|
||||
type Camelize,
|
||||
type DiscordAccessTokenResponse,
|
||||
type DiscordApplication,
|
||||
type DiscordApplicationCommand,
|
||||
type DiscordApplicationRoleConnection,
|
||||
type DiscordAuditLog,
|
||||
type DiscordAutoModerationRule,
|
||||
type DiscordBan,
|
||||
type DiscordChannel,
|
||||
type DiscordConnection,
|
||||
type DiscordCurrentAuthorization,
|
||||
type DiscordEmoji,
|
||||
type DiscordFollowedChannel,
|
||||
type DiscordGetGatewayBot,
|
||||
@@ -31,6 +37,7 @@ import {
|
||||
type DiscordMember,
|
||||
type DiscordMemberWithUser,
|
||||
type DiscordMessage,
|
||||
type DiscordPartialGuild,
|
||||
type DiscordPrunedCount,
|
||||
type DiscordRole,
|
||||
type DiscordScheduledEvent,
|
||||
@@ -48,7 +55,7 @@ import {
|
||||
type ModifyGuildTemplate,
|
||||
} from '@discordeno/types'
|
||||
import { createRoutes } from './routes.js'
|
||||
import type { CreateRequestBodyOptions, CreateRestManagerOptions, RestManager, SendRequestOptions } from './types.js'
|
||||
import type { CreateRequestBodyOptions, CreateRestManagerOptions, MakeRequestOptions, RestManager, SendRequestOptions } from './types.js'
|
||||
|
||||
// TODO: make dynamic based on package.json file
|
||||
const version = '19.0.0-alpha.1'
|
||||
@@ -91,8 +98,10 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
|
||||
routes: createRoutes(),
|
||||
|
||||
checkRateLimits(url) {
|
||||
const ratelimited = rest.rateLimitedPaths.get(url)
|
||||
checkRateLimits(url, headers) {
|
||||
const authHeader = headers?.authorization ?? ''
|
||||
|
||||
const ratelimited = rest.rateLimitedPaths.get(`${authHeader}${url}`)
|
||||
const global = rest.rateLimitedPaths.get('global')
|
||||
const now = Date.now()
|
||||
|
||||
@@ -178,9 +187,19 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
// Have to use changeToDiscordFormat or else JSON.stringify may throw an error for the presence of BigInt(s) in the json
|
||||
form.append('payload_json', JSON.stringify(rest.changeToDiscordFormat({ ...options.body, files: undefined })))
|
||||
|
||||
body = form
|
||||
|
||||
// No need to set the `content-type` header since `fetch` does that automatically for us when we use a `FormData` object.
|
||||
body = form
|
||||
} else if (options?.body && options.headers && options.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||
// OAuth2 body handling
|
||||
const formBody: string[] = []
|
||||
|
||||
const discordBody = rest.changeToDiscordFormat(options.body)
|
||||
|
||||
for (const prop in discordBody) {
|
||||
formBody.push(`${encodeURIComponent(prop)}=${encodeURIComponent(discordBody[prop])}`)
|
||||
}
|
||||
|
||||
body = formBody.join('&')
|
||||
} else if (options?.body !== undefined) {
|
||||
if (options.body instanceof FormData) {
|
||||
body = options.body
|
||||
@@ -235,7 +254,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
},
|
||||
|
||||
/** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */
|
||||
processHeaders(url, headers) {
|
||||
processHeaders(url, headers, requestAuthorization) {
|
||||
let rateLimited = false
|
||||
|
||||
// GET ALL NECESSARY HEADERS
|
||||
@@ -247,7 +266,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
const bucketId = headers.get(RATE_LIMIT_BUCKET_HEADER) ?? undefined
|
||||
const limit = headers.get(RATE_LIMIT_LIMIT_HEADER)
|
||||
|
||||
rest.queues.get(url)?.handleCompletedRequest({
|
||||
rest.queues.get(`${requestAuthorization}${url}`)?.handleCompletedRequest({
|
||||
remaining: remaining ? Number(remaining) : undefined,
|
||||
interval: retryAfter ? Number(retryAfter) * 1000 : undefined,
|
||||
max: limit ? Number(limit) : undefined,
|
||||
@@ -258,7 +277,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
rateLimited = true
|
||||
|
||||
// SAVE THE URL AS LIMITED, IMPORTANT FOR NEW REQUESTS BY USER WITHOUT BUCKET
|
||||
rest.rateLimitedPaths.set(url, {
|
||||
rest.rateLimitedPaths.set(`${requestAuthorization}${url}`, {
|
||||
url,
|
||||
resetTimestamp: reset,
|
||||
bucketId,
|
||||
@@ -266,7 +285,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
|
||||
// SAVE THE BUCKET AS LIMITED SINCE DIFFERENT URLS MAY SHARE A BUCKET
|
||||
if (bucketId) {
|
||||
rest.rateLimitedPaths.set(bucketId, {
|
||||
rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
|
||||
url,
|
||||
resetTimestamp: reset,
|
||||
bucketId,
|
||||
@@ -295,7 +314,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
})
|
||||
|
||||
if (bucketId) {
|
||||
rest.rateLimitedPaths.set(bucketId, {
|
||||
rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
|
||||
url: 'global',
|
||||
resetTimestamp: globalReset,
|
||||
bucketId,
|
||||
@@ -313,7 +332,15 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
const url = `${rest.baseUrl}/v${rest.version}${options.route}`
|
||||
const payload = rest.createRequestBody(options.method, options.requestBodyOptions)
|
||||
|
||||
logger.debug(`sending request to ${url}`, 'with payload:', { ...payload, headers: { ...payload.headers, authorization: 'Bot tokenhere' } })
|
||||
const loggingHeaders = { ...payload.headers }
|
||||
|
||||
const authenticationScheme = payload.headers.authorization?.split(' ')[0]
|
||||
|
||||
if (payload.headers.authorization) {
|
||||
loggingHeaders.authorization = `${authenticationScheme} tokenhere`
|
||||
}
|
||||
|
||||
logger.debug(`sending request to ${url}`, 'with payload:', { ...payload, headers: loggingHeaders })
|
||||
const response = await fetch(url, payload).catch(async (error) => {
|
||||
logger.error(error)
|
||||
// Mark request and completed
|
||||
@@ -331,7 +358,11 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get(RATE_LIMIT_SCOPE_HEADER) === 'shared')
|
||||
|
||||
// Set the bucket id if it was available on the headers
|
||||
const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers)
|
||||
const bucketId = rest.processHeaders(
|
||||
rest.simplifyUrl(options.route, options.method),
|
||||
response.headers,
|
||||
authenticationScheme === 'Bearer' ? payload.headers.authorization : '',
|
||||
)
|
||||
if (bucketId) options.bucketId = bucketId
|
||||
|
||||
if (response.status < HttpResponseCode.Success || response.status >= HttpResponseCode.Error) {
|
||||
@@ -406,18 +437,20 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return
|
||||
}
|
||||
|
||||
const queue = rest.queues.get(url)
|
||||
const authHeader = request.requestBodyOptions?.headers?.authorization ?? ''
|
||||
|
||||
const queue = rest.queues.get(`${authHeader}${url}`)
|
||||
|
||||
if (queue !== undefined) {
|
||||
queue.makeRequest(request)
|
||||
} else {
|
||||
// CREATES A NEW QUEUE
|
||||
const bucketQueue = new Queue(rest, { url, deleteQueueDelay: rest.deleteQueueDelay })
|
||||
const bucketQueue = new Queue(rest, { url, deleteQueueDelay: rest.deleteQueueDelay, authentication: authHeader })
|
||||
|
||||
// Add request to queue
|
||||
bucketQueue.makeRequest(request)
|
||||
// Save queue
|
||||
rest.queues.set(url, bucketQueue)
|
||||
rest.queues.set(`${authHeader}${url}`, bucketQueue)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -511,6 +544,10 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
await rest.put(rest.routes.channels.threads.user(channelId, userId))
|
||||
},
|
||||
|
||||
async addDmRecipient(channelId, userId, body) {
|
||||
await rest.put(rest.routes.channels.dmRecipient(channelId, userId), { body })
|
||||
},
|
||||
|
||||
async createAutomodRule(guildId, body, reason) {
|
||||
return await rest.post<DiscordAutoModerationRule>(rest.routes.guilds.automod.rules(guildId), { body, reason })
|
||||
},
|
||||
@@ -523,16 +560,34 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.post<DiscordEmoji>(rest.routes.guilds.emojis(guildId), { body, reason })
|
||||
},
|
||||
|
||||
async createGlobalApplicationCommand(body) {
|
||||
return await rest.post<DiscordApplicationCommand>(rest.routes.interactions.commands.commands(rest.applicationId), { body })
|
||||
async createGlobalApplicationCommand(body, options) {
|
||||
const restOptions: MakeRequestOptions = { body }
|
||||
|
||||
if (options?.bearerToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.bearerToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.post<DiscordApplicationCommand>(rest.routes.interactions.commands.commands(rest.applicationId), restOptions)
|
||||
},
|
||||
|
||||
async createGuild(body) {
|
||||
return await rest.post<DiscordGuild>(rest.routes.guilds.all(), { body })
|
||||
},
|
||||
|
||||
async createGuildApplicationCommand(body, guildId) {
|
||||
return await rest.post<DiscordApplicationCommand>(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), { body })
|
||||
async createGuildApplicationCommand(body, guildId, options) {
|
||||
const restOptions: MakeRequestOptions = { body }
|
||||
|
||||
if (options?.bearerToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.bearerToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.post<DiscordApplicationCommand>(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions)
|
||||
},
|
||||
|
||||
async createGuildFromTemplate(templateCode, body) {
|
||||
@@ -719,7 +774,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
async editBotProfile(options) {
|
||||
const avatar = options?.botAvatarURL ? await urlToBase64(options?.botAvatarURL) : options?.botAvatarURL
|
||||
|
||||
return await rest.patch<DiscordUser>(rest.routes.userBot(), {
|
||||
return await rest.patch<DiscordUser>(rest.routes.currentUser(), {
|
||||
body: {
|
||||
username: options.username?.trim(),
|
||||
avatar,
|
||||
@@ -869,18 +924,78 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.get<DiscordListActiveThreads>(rest.routes.channels.threads.active(guildId))
|
||||
},
|
||||
|
||||
async getApplicationCommandPermission(guildId, commandId) {
|
||||
async getApplicationCommandPermission(guildId, commandId, options) {
|
||||
const restOptions: Omit<MakeRequestOptions, 'body'> = {}
|
||||
|
||||
if (options?.accessToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.accessToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.get<DiscordGuildApplicationCommandPermissions>(
|
||||
rest.routes.interactions.commands.permission(rest.applicationId, guildId, commandId),
|
||||
rest.routes.interactions.commands.permission(options?.applicationId ?? rest.applicationId, guildId, commandId),
|
||||
restOptions,
|
||||
)
|
||||
},
|
||||
|
||||
async getApplicationCommandPermissions(guildId) {
|
||||
return await rest.get<DiscordGuildApplicationCommandPermissions[]>(rest.routes.interactions.commands.permissions(rest.applicationId, guildId))
|
||||
async getApplicationCommandPermissions(guildId, options) {
|
||||
const restOptions: Omit<MakeRequestOptions, 'body'> = {}
|
||||
|
||||
if (options?.accessToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.accessToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.get<DiscordGuildApplicationCommandPermissions[]>(
|
||||
rest.routes.interactions.commands.permissions(options?.applicationId ?? rest.applicationId, guildId),
|
||||
restOptions,
|
||||
)
|
||||
},
|
||||
|
||||
async getApplicationInfo() {
|
||||
return await rest.get<DiscordApplication>(rest.routes.oauth2Application())
|
||||
return await rest.get<DiscordApplication>(rest.routes.oauth2.application())
|
||||
},
|
||||
|
||||
async getCurrentAuthenticationInfo(token) {
|
||||
return await rest.get<DiscordCurrentAuthorization>(rest.routes.oauth2.currentAuthorization(), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async exchangeToken(body) {
|
||||
const restOptions: MakeRequestOptions = {
|
||||
body,
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
unauthorized: true,
|
||||
}
|
||||
|
||||
if (body.grantType === 'client_credentials') {
|
||||
const basicCredentials = Buffer.from(`${body.clientId}:${body.clientSecret}`)
|
||||
restOptions.headers!.authorization = `Basic ${basicCredentials.toString('base64')}`
|
||||
|
||||
restOptions.body.scope = body.scope.join(' ')
|
||||
}
|
||||
|
||||
return await rest.post<DiscordAccessTokenResponse>(rest.routes.oauth2.tokenExchange(), restOptions)
|
||||
},
|
||||
|
||||
async revokeToken(body) {
|
||||
await rest.post(rest.routes.oauth2.tokenRevoke(), {
|
||||
body,
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getAuditLog(guildId, options) {
|
||||
@@ -929,6 +1044,12 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
})
|
||||
},
|
||||
|
||||
async getGroupDmChannel(body) {
|
||||
return await rest.post<DiscordChannel>(rest.routes.channels.dm(), {
|
||||
body,
|
||||
})
|
||||
},
|
||||
|
||||
async getEmoji(guildId, emojiId) {
|
||||
return await rest.get<DiscordEmoji>(rest.routes.guilds.emoji(guildId, emojiId))
|
||||
},
|
||||
@@ -957,6 +1078,15 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.get<DiscordGuild>(rest.routes.guilds.guild(guildId, options.counts))
|
||||
},
|
||||
|
||||
async getGuilds(token, options) {
|
||||
return await rest.get<DiscordPartialGuild[]>(rest.routes.guilds.userGuilds(options), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getGuildApplicationCommand(commandId, guildId) {
|
||||
return await rest.get<DiscordApplicationCommand>(rest.routes.interactions.commands.guilds.one(rest.applicationId, guildId, commandId))
|
||||
},
|
||||
@@ -1081,6 +1211,33 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.get<DiscordUser>(rest.routes.user(id))
|
||||
},
|
||||
|
||||
async getCurrentUser(token) {
|
||||
return await rest.get<DiscordUser>(rest.routes.currentUser(), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getUserConnections(token) {
|
||||
return await rest.get<DiscordConnection[]>(rest.routes.oauth2.connections(), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getUserApplicationRoleConnection(token, applicationId) {
|
||||
return await rest.get<DiscordApplicationRoleConnection>(rest.routes.oauth2.roleConnections(applicationId), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getVanityUrl(guildId) {
|
||||
return await rest.get<DiscordVanityUrl>(rest.routes.guilds.vanity(guildId))
|
||||
},
|
||||
@@ -1137,6 +1294,10 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
await rest.delete(rest.routes.channels.threads.user(channelId, userId))
|
||||
},
|
||||
|
||||
async removeDmRecipient(channelId, userId) {
|
||||
await rest.delete(rest.routes.channels.dmRecipient(channelId, userId))
|
||||
},
|
||||
|
||||
async sendFollowupMessage(token, options) {
|
||||
return await rest.post(rest.routes.webhooks.webhook(rest.applicationId, token), {
|
||||
body: options,
|
||||
@@ -1186,6 +1347,15 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return await rest.get<DiscordMemberWithUser>(rest.routes.guilds.members.member(guildId, userId))
|
||||
},
|
||||
|
||||
async getCurrentMember(guildId, token) {
|
||||
return await rest.get<DiscordMemberWithUser>(rest.routes.guilds.members.currentMember(guildId), {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async getMembers(guildId, options) {
|
||||
return await rest.get<DiscordMemberWithUser[]>(rest.routes.guilds.members.members(guildId, options))
|
||||
},
|
||||
@@ -1220,12 +1390,46 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
await rest.post(rest.routes.channels.typing(channelId))
|
||||
},
|
||||
|
||||
async upsertGlobalApplicationCommands(body) {
|
||||
return await rest.put<DiscordApplicationCommand[]>(rest.routes.interactions.commands.commands(rest.applicationId), { body })
|
||||
async upsertGlobalApplicationCommands(body, options) {
|
||||
const restOptions: MakeRequestOptions = { body }
|
||||
|
||||
if (options?.bearerToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.bearerToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.put<DiscordApplicationCommand[]>(rest.routes.interactions.commands.commands(rest.applicationId), restOptions)
|
||||
},
|
||||
|
||||
async upsertGuildApplicationCommands(guildId, body) {
|
||||
return await rest.put<DiscordApplicationCommand[]>(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), { body })
|
||||
async upsertGuildApplicationCommands(guildId, body, options) {
|
||||
const restOptions: MakeRequestOptions = { body }
|
||||
|
||||
if (options?.bearerToken) {
|
||||
restOptions.unauthorized = true
|
||||
restOptions.headers = {
|
||||
authorization: `Bearer ${options.bearerToken}`,
|
||||
}
|
||||
}
|
||||
|
||||
return await rest.put<DiscordApplicationCommand[]>(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions)
|
||||
},
|
||||
|
||||
async editUserApplicationRoleConnection(token, applicationId, body) {
|
||||
return await rest.put<DiscordApplicationRoleConnection>(rest.routes.oauth2.roleConnections(applicationId), {
|
||||
body,
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
unauthorized: true,
|
||||
})
|
||||
},
|
||||
|
||||
async addGuildMember(guildId, userId, body) {
|
||||
return await rest.put(rest.routes.guilds.members.member(guildId, userId), {
|
||||
body,
|
||||
})
|
||||
},
|
||||
|
||||
preferSnakeCase(enabled: boolean) {
|
||||
|
||||
@@ -28,6 +28,8 @@ export class Queue {
|
||||
frozenAt: number = 0
|
||||
/** The time in milliseconds to wait before deleting this queue if it is empty. Defaults to 60000(one minute). */
|
||||
deleteQueueDelay: number = 60000
|
||||
/** The authentication header used for the OAuth2 request. Defaults to an empty string for non-OAuth2 requests */
|
||||
authentication: string = ''
|
||||
|
||||
constructor(rest: RestManager, options: QueueOptions) {
|
||||
this.rest = rest
|
||||
@@ -38,6 +40,7 @@ export class Queue {
|
||||
if (options.remaining) this.remaining = options.remaining
|
||||
if (options.timeoutId) this.timeoutId = options.timeoutId
|
||||
if (options.deleteQueueDelay) this.deleteQueueDelay = options.deleteQueueDelay
|
||||
if (options.authentication) this.authentication = options.authentication
|
||||
}
|
||||
|
||||
/** Check if there is any remaining requests that are allowed. */
|
||||
@@ -68,7 +71,7 @@ export class Queue {
|
||||
this.processing = true
|
||||
|
||||
while (this.waiting.length > 0) {
|
||||
logger.debug(`[Queue] ${this.url} process waiting while loop ran.`)
|
||||
logger.debug(`[Queue] ${this.isOauth2Queue() ? '' : 'Bearer '}${this.url} process waiting while loop ran.`)
|
||||
if (this.isRequestAllowed()) {
|
||||
// Resolve the next item in the queue
|
||||
this.waiting.shift()?.()
|
||||
@@ -90,7 +93,7 @@ export class Queue {
|
||||
this.processingPending = true
|
||||
|
||||
while (this.pending.length > 0) {
|
||||
logger.debug(`Queue ${this.url} process pending while loop ran with ${this.pending.length}.`)
|
||||
logger.debug(`Queue ${this.isOauth2Queue() ? '' : 'Bearer '}${this.url} process pending while loop ran with ${this.pending.length}.`)
|
||||
if (!this.firstRequest && !this.isRequestAllowed()) {
|
||||
const now = Date.now()
|
||||
const future = this.frozenAt + this.interval
|
||||
@@ -102,13 +105,12 @@ export class Queue {
|
||||
if (request) {
|
||||
const basicURL = this.rest.simplifyUrl(request.route, request.method)
|
||||
|
||||
// IF THIS URL IS STILL RATE LIMITED, TRY AGAIN
|
||||
// If this url is still rate limited, try again
|
||||
const urlResetIn = this.rest.checkRateLimits(basicURL)
|
||||
const urlResetIn = this.rest.checkRateLimits(basicURL, request.requestBodyOptions?.headers)
|
||||
if (urlResetIn) await delay(urlResetIn)
|
||||
|
||||
// IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS
|
||||
const bucketResetIn = request.bucketId ? this.rest.checkRateLimits(request.bucketId) : false
|
||||
const bucketResetIn = request.bucketId ? this.rest.checkRateLimits(request.bucketId, request.requestBodyOptions?.headers) : false
|
||||
if (bucketResetIn) await delay(bucketResetIn)
|
||||
|
||||
this.firstRequest = false
|
||||
@@ -133,7 +135,7 @@ export class Queue {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`Queue ${this.url} process pending while loop exited with ${this.pending.length}.`)
|
||||
logger.debug(`Queue ${this.isOauth2Queue() ? '' : 'Bearer '}${this.url} process pending while loop exited with ${this.pending.length}.`)
|
||||
|
||||
// Mark as false so next pending request can be triggered by new loop.
|
||||
this.processingPending = false
|
||||
@@ -172,11 +174,11 @@ export class Queue {
|
||||
return
|
||||
}
|
||||
|
||||
logger.debug(`[Queue] ${this.url}. Delaying delete for ${this.deleteQueueDelay}ms`)
|
||||
logger.debug(`[Queue] ${this.isOauth2Queue() ? '' : 'Bearer '}${this.url}. Delaying delete for ${this.deleteQueueDelay}ms`)
|
||||
// Delete in a minute giving a bit of time to allow new requests that may reuse this queue
|
||||
setTimeout(async () => {
|
||||
if (!this.isQueueClearable()) {
|
||||
logger.debug(`[Queue] ${this.url}. is not clearable. Restarting processing of queue.`)
|
||||
logger.debug(`[Queue] ${this.isOauth2Queue() ? '' : 'Bearer '}${this.url}. is not clearable. Restarting processing of queue.`)
|
||||
this.processPending()
|
||||
return
|
||||
}
|
||||
@@ -184,8 +186,11 @@ export class Queue {
|
||||
logger.debug(`[Queue] ${this.url}. Deleting`)
|
||||
if (this.timeoutId) clearTimeout(this.timeoutId)
|
||||
// No requests have been requested for this queue so we nuke this queue
|
||||
this.rest.queues.delete(this.url)
|
||||
logger.debug(`[Queue] ${this.url}. Deleted! Remaining: (${this.rest.queues.size})`, [...this.rest.queues.keys()])
|
||||
this.rest.queues.delete(`${this.authentication}${this.url}`)
|
||||
logger.debug(
|
||||
`[Queue] ${this.url}. Deleted! Remaining: (${this.rest.queues.size})`,
|
||||
[...this.rest.queues.values()].map((queue) => `${queue.isOauth2Queue() ? '' : 'Bearer '}${queue.url}`),
|
||||
)
|
||||
if (this.rest.queues.size) this.processPending()
|
||||
}, this.deleteQueueDelay)
|
||||
}
|
||||
@@ -201,6 +206,10 @@ export class Queue {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
isOauth2Queue(): boolean {
|
||||
return this.authentication === ''
|
||||
}
|
||||
}
|
||||
|
||||
export interface QueueOptions {
|
||||
@@ -216,4 +225,6 @@ export interface QueueOptions {
|
||||
url: string
|
||||
/** The time in milliseconds to wait before deleting this queue if it is empty. Defaults to 60000(one minute). */
|
||||
deleteQueueDelay?: number
|
||||
/** Authentication used for the request. In non-OAuth2 situations should be an empty string. Defaults to an empty string */
|
||||
authentication?: string
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ export function createRoutes(): RestRoutes {
|
||||
dm: () => {
|
||||
return '/users/@me/channels'
|
||||
},
|
||||
dmRecipient: (channelId, userId) => {
|
||||
return `/channels/${channelId}/recipients/${userId}`
|
||||
},
|
||||
pin: (channelId, messageId) => {
|
||||
return `/channels/${channelId}/pins/${messageId}`
|
||||
},
|
||||
@@ -222,6 +225,22 @@ export function createRoutes(): RestRoutes {
|
||||
all: () => {
|
||||
return '/guilds'
|
||||
},
|
||||
userGuilds: (options) => {
|
||||
let url = '/users/@me/guilds?'
|
||||
|
||||
if (options) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
if (options.after) url += `after=${options.after}`
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
if (options.before) url += `&before=${options.before}`
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
if (options.limit) url += `&limit=${options.limit}`
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
if (options.withCounts) url += `&with_counts=${options.withCounts}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
auditlogs: (guildId, options) => {
|
||||
let url = `/guilds/${guildId}/audit-logs?`
|
||||
|
||||
@@ -359,6 +378,9 @@ export function createRoutes(): RestRoutes {
|
||||
member: (guildId, userId) => {
|
||||
return `/guilds/${guildId}/members/${userId}`
|
||||
},
|
||||
currentMember: (guildId) => {
|
||||
return `/users/@me/guilds/${guildId}/member`
|
||||
},
|
||||
members: (guildId, options) => {
|
||||
let url = `/guilds/${guildId}/members?`
|
||||
|
||||
@@ -532,19 +554,37 @@ export function createRoutes(): RestRoutes {
|
||||
},
|
||||
},
|
||||
|
||||
// OAuth2 endpoints
|
||||
oauth2: {
|
||||
tokenExchange: () => {
|
||||
return '/oauth2/token'
|
||||
},
|
||||
tokenRevoke: () => {
|
||||
return '/oauth2/token/revoke'
|
||||
},
|
||||
currentAuthorization: () => {
|
||||
return '/oauth2/@me'
|
||||
},
|
||||
application: () => {
|
||||
return '/oauth2/applications/@me'
|
||||
},
|
||||
connections: () => {
|
||||
return '/users/@me/connections'
|
||||
},
|
||||
roleConnections: (applicationId) => {
|
||||
return `/users/@me/applications/${applicationId}/role-connection`
|
||||
},
|
||||
},
|
||||
|
||||
// User endpoints
|
||||
user(userId) {
|
||||
return `/users/${userId}`
|
||||
},
|
||||
|
||||
userBot() {
|
||||
currentUser() {
|
||||
return '/users/@me'
|
||||
},
|
||||
|
||||
oauth2Application() {
|
||||
return '/oauth2/applications/@me'
|
||||
},
|
||||
|
||||
gatewayBot() {
|
||||
return '/gateway/bot'
|
||||
},
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import type {
|
||||
AddDmRecipientOptions,
|
||||
AddGuildMemberOptions,
|
||||
ApplicationCommandPermissions,
|
||||
AtLeastOne,
|
||||
BeginGuildPrune,
|
||||
BigString,
|
||||
Camelize,
|
||||
CamelizedDiscordAccessTokenResponse,
|
||||
CamelizedDiscordActiveThreads,
|
||||
CamelizedDiscordApplication,
|
||||
CamelizedDiscordApplicationCommand,
|
||||
CamelizedDiscordApplicationRoleConnection,
|
||||
CamelizedDiscordArchivedThreads,
|
||||
CamelizedDiscordAuditLog,
|
||||
CamelizedDiscordAutoModerationRule,
|
||||
CamelizedDiscordBan,
|
||||
CamelizedDiscordChannel,
|
||||
CamelizedDiscordConnection,
|
||||
CamelizedDiscordCurrentAuthorization,
|
||||
CamelizedDiscordEmoji,
|
||||
CamelizedDiscordFollowedChannel,
|
||||
CamelizedDiscordGetGatewayBot,
|
||||
@@ -27,6 +33,7 @@ import type {
|
||||
CamelizedDiscordMemberWithUser,
|
||||
CamelizedDiscordMessage,
|
||||
CamelizedDiscordModifyGuildWelcomeScreen,
|
||||
CamelizedDiscordPartialGuild,
|
||||
CamelizedDiscordPrunedCount,
|
||||
CamelizedDiscordRole,
|
||||
CamelizedDiscordScheduledEvent,
|
||||
@@ -35,6 +42,8 @@ import type {
|
||||
CamelizedDiscordStickerPack,
|
||||
CamelizedDiscordTemplate,
|
||||
CamelizedDiscordThreadMember,
|
||||
CamelizedDiscordTokenExchange,
|
||||
CamelizedDiscordTokenRevocation,
|
||||
CamelizedDiscordUser,
|
||||
CamelizedDiscordVanityUrl,
|
||||
CamelizedDiscordVoiceRegion,
|
||||
@@ -44,7 +53,9 @@ import type {
|
||||
CreateAutoModerationRuleOptions,
|
||||
CreateChannelInvite,
|
||||
CreateForumPostWithMessage,
|
||||
CreateGlobalApplicationCommandOptions,
|
||||
CreateGuild,
|
||||
CreateGuildApplicationCommandOptions,
|
||||
CreateGuildBan,
|
||||
CreateGuildChannel,
|
||||
CreateGuildEmoji,
|
||||
@@ -67,14 +78,17 @@ import type {
|
||||
EditUserVoiceState,
|
||||
ExecuteWebhook,
|
||||
FileContent,
|
||||
GetApplicationCommandPermissionOptions,
|
||||
GetBans,
|
||||
GetGroupDmOptions,
|
||||
GetGuildAuditLog,
|
||||
GetGuildPruneCountQuery,
|
||||
GetInvite,
|
||||
GetMessagesOptions,
|
||||
GetReactions,
|
||||
GetScheduledEvents,
|
||||
GetScheduledEventUsers,
|
||||
GetScheduledEvents,
|
||||
GetUserGuilds,
|
||||
GetWebhookMessageOptions,
|
||||
InteractionCallbackData,
|
||||
InteractionResponse,
|
||||
@@ -92,6 +106,8 @@ import type {
|
||||
SearchMembers,
|
||||
StartThreadWithMessage,
|
||||
StartThreadWithoutMessage,
|
||||
UpsertGlobalApplicationCommandOptions,
|
||||
UpsertGuildApplicationCommandOptions,
|
||||
} from '@discordeno/types'
|
||||
import type { InvalidRequestBucket } from './invalidBucket.js'
|
||||
import type { Queue } from './queue.js'
|
||||
@@ -161,17 +177,22 @@ export interface RestManager {
|
||||
/** The routes that are available for this manager. */
|
||||
routes: RestRoutes
|
||||
/** Whether or not the rest manager should keep objects in raw snake case from discord. */
|
||||
preferSnakeCase: (enabled: boolean) => RestManager;
|
||||
preferSnakeCase: (enabled: boolean) => RestManager
|
||||
/** Check the rate limits for a url or a bucket. */
|
||||
checkRateLimits: (url: string) => number | false
|
||||
checkRateLimits: (url: string, headers?: Record<string, string>) => number | false
|
||||
/** Reshapes and modifies the obj as needed to make it ready for discords api. */
|
||||
changeToDiscordFormat: (obj: any) => any
|
||||
/** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */
|
||||
createRequestBody: (method: RequestMethods, options?: CreateRequestBodyOptions) => RequestBody
|
||||
/** This will create a infinite loop running in 1 seconds using tail recursion to keep rate limits clean. When a rate limit resets, this will remove it so the queue can proceed. */
|
||||
processRateLimitedPaths: () => void
|
||||
/** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */
|
||||
processHeaders: (url: string, headers: Headers) => string | undefined
|
||||
/**
|
||||
* Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available
|
||||
*
|
||||
* @remarks
|
||||
* The authenticationHeader should be defined ONLY if the request was done using a OAuth2 Access Token, in other cases it should be passed as an empty string
|
||||
*/
|
||||
processHeaders: (url: string, headers: Headers, authenticationHeader?: string) => string | undefined
|
||||
/** Sends a request to the api. */
|
||||
sendRequest: (options: SendRequestOptions) => Promise<void>
|
||||
/** Split a url to separate rate limit buckets based on major/minor parameters. */
|
||||
@@ -259,6 +280,35 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#add-thread-member}
|
||||
*/
|
||||
addThreadMember: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
/**
|
||||
* Adds a recipient to a group DM.
|
||||
*
|
||||
* @param channelId - The ID of the group dm to add the user to.
|
||||
* @param userId - The user ID of the user to add to the group dm.
|
||||
* @param options - The options for adding the user
|
||||
*
|
||||
* @remarks
|
||||
* Requires an OAuth2 access token with the `gdm.join` scope
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-add-recipient}
|
||||
*/
|
||||
addDmRecipient: (channelId: BigString, userId: BigString, options: AddDmRecipientOptions) => Promise<void>
|
||||
/**
|
||||
* Adds a member to a guild.
|
||||
*
|
||||
* @param guildId - The ID of the thread to add the member to.
|
||||
* @param userId - The user ID of the member to add to the thread.
|
||||
* @param options - The options for the add of a guild member
|
||||
*
|
||||
* @remarks
|
||||
* Requires the bot to be in the specified server
|
||||
* Requires an OAuth2 access token with the `guilds.join` scope
|
||||
*
|
||||
* Fires a _Guild Member Add_ gateway event.
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/guild#add-guild-member}
|
||||
*/
|
||||
addGuildMember: (guildId: BigString, userId: BigString, options: AddGuildMemberOptions) => Promise<void>
|
||||
/**
|
||||
* Creates an automod rule in a guild.
|
||||
*
|
||||
@@ -336,6 +386,7 @@ export interface RestManager {
|
||||
* Creates an application command accessible globally; across different guilds and channels.
|
||||
*
|
||||
* @param command - The command to create.
|
||||
* @param options - Additional options for the endpoint
|
||||
* @returns An instance of the created {@link ApplicationCommand}.
|
||||
*
|
||||
* @remarks
|
||||
@@ -343,9 +394,15 @@ export interface RestManager {
|
||||
* ⚠️ Global commands once created are cached for periods of __an hour__, so changes made to existing commands will take an hour to surface.
|
||||
* ⚠️ You can only create up to 200 _new_ commands daily.
|
||||
*
|
||||
* When using the bearer token the token needs the `applications.commands.update` scope and must be a `Client grant` token.
|
||||
* You will be able to update only your own application commands
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#create-global-application-command}
|
||||
*/
|
||||
createGlobalApplicationCommand: (command: CreateApplicationCommand) => Promise<CamelizedDiscordApplicationCommand>
|
||||
createGlobalApplicationCommand: (
|
||||
command: CreateApplicationCommand,
|
||||
options?: CreateGlobalApplicationCommandOptions,
|
||||
) => Promise<CamelizedDiscordApplicationCommand>
|
||||
/**
|
||||
* Creates a guild.
|
||||
*
|
||||
@@ -365,15 +422,23 @@ export interface RestManager {
|
||||
*
|
||||
* @param command - The command to create.
|
||||
* @param guildId - The ID of the guild to create the command for.
|
||||
* @param options - Additional options for the endpoint
|
||||
* @returns An instance of the created {@link ApplicationCommand}.
|
||||
*
|
||||
* @remarks
|
||||
* ⚠️ Creating a command with the same name as an existing command for your application will overwrite the old command.
|
||||
* ⚠️ You can only create up to 200 _new_ commands daily.
|
||||
*
|
||||
* When using the bearer token the token needs the `applications.commands.update` scope and must be a `Client grant` token.
|
||||
* You will be able to update only your own application commands
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command}
|
||||
*/
|
||||
createGuildApplicationCommand: (command: CreateApplicationCommand, guildId: BigString) => Promise<CamelizedDiscordApplicationCommand>
|
||||
createGuildApplicationCommand: (
|
||||
command: CreateApplicationCommand,
|
||||
guildId: BigString,
|
||||
options?: CreateGuildApplicationCommandOptions,
|
||||
) => Promise<CamelizedDiscordApplicationCommand>
|
||||
/**
|
||||
* Creates a guild from a template.
|
||||
*
|
||||
@@ -1264,6 +1329,24 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state}
|
||||
*/
|
||||
editUserVoiceState: (guildId: BigString, options: EditUserVoiceState) => Promise<void>
|
||||
/**
|
||||
* Edit the current user application role connection for the application.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @param applicationId - The id of the application to edit the role connection
|
||||
* @param options - The options to edit
|
||||
* @returns {CamelizedDiscordApplicationRoleConnection}
|
||||
*
|
||||
* @remarks
|
||||
* This requires the `role_connections.write` scope.
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#update-user-application-role-connection}
|
||||
*/
|
||||
editUserApplicationRoleConnection: (
|
||||
bearerToken: string,
|
||||
applicationId: BigString,
|
||||
options: CamelizedDiscordApplicationRoleConnection,
|
||||
) => Promise<CamelizedDiscordApplicationRoleConnection>
|
||||
/**
|
||||
* Edits a webhook.
|
||||
*
|
||||
@@ -1398,25 +1481,63 @@ export interface RestManager {
|
||||
getActiveThreads: (guildId: BigString) => Promise<CamelizedDiscordActiveThreads>
|
||||
/** Get the applications info */
|
||||
getApplicationInfo: () => Promise<CamelizedDiscordApplication>
|
||||
/**
|
||||
* Get the current authentication info for the authenticated user
|
||||
*
|
||||
* @param bearerToken - Any OAuth2 derived access token
|
||||
* @returns An instance of {@link CamelizedDiscordCurrentAuthorization}
|
||||
*
|
||||
* @remarks
|
||||
* The user object is not defined if the scopes do not include `identify`.
|
||||
* In the user object, if defined, the email is not included if the scopes do not include `email`
|
||||
*/
|
||||
getCurrentAuthenticationInfo: (bearerToken: string) => Promise<CamelizedDiscordCurrentAuthorization>
|
||||
/**
|
||||
* Exchange the information to get a OAuth2 accessToken token
|
||||
*
|
||||
* @param options - The options to make the exchange with discord
|
||||
*/
|
||||
exchangeToken: (options: CamelizedDiscordTokenExchange) => Promise<CamelizedDiscordAccessTokenResponse>
|
||||
/**
|
||||
* Revoke an access_token
|
||||
*
|
||||
* @param options - The options to revoke the access_token
|
||||
*/
|
||||
revokeToken: (options: CamelizedDiscordTokenRevocation) => Promise<void>
|
||||
/**
|
||||
* Gets the permissions of a guild application command.
|
||||
*
|
||||
* @param guildId - The ID of the guild the command is registered in.
|
||||
* @param commandId - The ID of the command to get the permissions of.
|
||||
* @param options - The OAuth2 related optional parameters for the endpoint
|
||||
* @returns An instance of {@link ApplicationCommandPermission}.
|
||||
*
|
||||
* @remarks
|
||||
* Then specifying the options object the access token passed-in requires the OAuth2 scope `applications.commands.permissions.update`
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions}
|
||||
*/
|
||||
getApplicationCommandPermission: (guildId: BigString, commandId: BigString) => Promise<CamelizedDiscordGuildApplicationCommandPermissions>
|
||||
getApplicationCommandPermission: (
|
||||
guildId: BigString,
|
||||
commandId: BigString,
|
||||
options?: GetApplicationCommandPermissionOptions,
|
||||
) => Promise<CamelizedDiscordGuildApplicationCommandPermissions>
|
||||
/**
|
||||
* Gets the permissions of all application commands registered in a guild by the ID of the guild.
|
||||
* Gets the permissions of all application commands registered in a guild by the ID of the guild and optionally an external application.
|
||||
*
|
||||
* @param guildId - The ID of the guild to get the permissions objects of.
|
||||
* @param options - The OAuth2 related optional parameters for the endpoint
|
||||
* @returns A collection of {@link ApplicationCommandPermission} objects assorted by command ID.
|
||||
*
|
||||
* @remarks
|
||||
* Then specifying the options object the access token passed-in requires the OAuth2 scope `applications.commands.permissions.update`
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions}
|
||||
*/
|
||||
getApplicationCommandPermissions: (guildId: BigString) => Promise<CamelizedDiscordGuildApplicationCommandPermissions[]>
|
||||
getApplicationCommandPermissions: (
|
||||
guildId: BigString,
|
||||
options?: GetApplicationCommandPermissionOptions,
|
||||
) => Promise<CamelizedDiscordGuildApplicationCommandPermissions[]>
|
||||
/**
|
||||
* Gets a guild's audit log.
|
||||
*
|
||||
@@ -1548,6 +1669,22 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#create-dm}
|
||||
*/
|
||||
getDmChannel: (userId: BigString) => Promise<CamelizedDiscordChannel>
|
||||
/**
|
||||
* Create a new group DM channel with multiple users.
|
||||
*
|
||||
* @param options - The options for create a new group dm
|
||||
* @returns An instance of {@link CamelizedDiscordChannel}.
|
||||
*
|
||||
* @remarks
|
||||
* The access tokens require to have the `gdm.join` scope
|
||||
*
|
||||
* This endpoint is limited to 10 active group DMs.
|
||||
*
|
||||
* Fires a _Channel create_ gateway event.
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#create-group-dm}
|
||||
*/
|
||||
getGroupDmChannel: (options: GetGroupDmOptions) => Promise<CamelizedDiscordChannel>
|
||||
/**
|
||||
* Gets an emoji by its ID.
|
||||
*
|
||||
@@ -1613,6 +1750,19 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/guild#get-guild}
|
||||
*/
|
||||
getGuild: (guildId: BigString, options?: { counts?: boolean }) => Promise<CamelizedDiscordGuild>
|
||||
/**
|
||||
* Get the user guilds.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @param options - The parameters for the fetching of the guild.
|
||||
* @returns An instance of {@link Guild}.
|
||||
*
|
||||
* @remarks
|
||||
* The access tokens needs to have the `guilds` scope
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#get-current-user-guilds}
|
||||
*/
|
||||
getGuilds: (bearerToken: string, options?: GetUserGuilds) => Promise<CamelizedDiscordPartialGuild[]>
|
||||
/**
|
||||
* Gets a guild application command by its ID.
|
||||
*
|
||||
@@ -1988,6 +2138,41 @@ export interface RestManager {
|
||||
* @returns {CamelizedDiscordUser}
|
||||
*/
|
||||
getUser: (id: BigString) => Promise<CamelizedDiscordUser>
|
||||
/**
|
||||
* Get the current user data.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @returns {CamelizedDiscordUser}
|
||||
*
|
||||
* @remarks
|
||||
* This requires the `identify` scope.
|
||||
*
|
||||
* To get the mail this also requires the `email` scope
|
||||
*/
|
||||
getCurrentUser: (bearerToken: string) => Promise<CamelizedDiscordUser>
|
||||
/**
|
||||
* Get the current user connections.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @returns {CamelizedDiscordConnection[]}
|
||||
*
|
||||
* @remarks
|
||||
* This requires the `connections` scope.
|
||||
*/
|
||||
getUserConnections: (bearerToken: string) => Promise<CamelizedDiscordConnection[]>
|
||||
/**
|
||||
* Get the current user application role connection for the application.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @param applicationId - The id of the application to get the role connection
|
||||
* @returns {CamelizedDiscordApplicationRoleConnection}
|
||||
*
|
||||
* @remarks
|
||||
* The access token requires the `role_connections.write` scope.
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#get-user-application-role-connection}
|
||||
*/
|
||||
getUserApplicationRoleConnection: (bearerToken: string, applicationId: BigString) => Promise<CamelizedDiscordApplicationRoleConnection>
|
||||
/**
|
||||
* Gets information about the vanity url of a guild.
|
||||
*
|
||||
@@ -2172,6 +2357,15 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#remove-thread-member}
|
||||
*/
|
||||
removeThreadMember: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
/**
|
||||
* Removes a member from a Group DM.
|
||||
*
|
||||
* @param channelId - The ID of the channel to remove the recipient user of.
|
||||
* @param userId - The user ID of the user to remove.
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient}
|
||||
*/
|
||||
removeDmRecipient: (channelId: BigString, userId: BigString) => Promise<void>
|
||||
/**
|
||||
* Sends a message to a channel.
|
||||
*
|
||||
@@ -2315,6 +2509,7 @@ export interface RestManager {
|
||||
* Re-registers the list of global application commands, overwriting the previous commands completely.
|
||||
*
|
||||
* @param commands - The list of commands to use to overwrite the previous list.
|
||||
* @param options - Additional options for the endpoint.
|
||||
* @returns A collection of {@link ApplicationCommand} objects assorted by command ID.
|
||||
*
|
||||
* @remarks
|
||||
@@ -2322,14 +2517,21 @@ export interface RestManager {
|
||||
*
|
||||
* ⚠️ Commands that do not already exist will count towards the daily limit of _200_ new commands.
|
||||
*
|
||||
* When using the bearer token the token needs the `applications.commands.update` scope and must be a `Client grant` token.
|
||||
* You will be able to update only your own application commands
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands}
|
||||
*/
|
||||
upsertGlobalApplicationCommands: (commands: CreateApplicationCommand[]) => Promise<CamelizedDiscordApplicationCommand[]>
|
||||
upsertGlobalApplicationCommands: (
|
||||
commands: CreateApplicationCommand[],
|
||||
options?: UpsertGlobalApplicationCommandOptions,
|
||||
) => Promise<CamelizedDiscordApplicationCommand[]>
|
||||
/**
|
||||
* Re-registers the list of application commands registered in a guild, overwriting the previous commands completely.
|
||||
*
|
||||
* @param guildId - The ID of the guild whose list of commands to overwrite.
|
||||
* @param commands - The list of commands to use to overwrite the previous list.
|
||||
* @param options - Additional options for the endpoint.
|
||||
* @returns A collection of {@link ApplicationCommand} objects assorted by command ID.
|
||||
*
|
||||
* @remarks
|
||||
@@ -2337,9 +2539,16 @@ export interface RestManager {
|
||||
*
|
||||
* ⚠️ Commands that do not already exist will count towards the daily limit of _200_ new commands.
|
||||
*
|
||||
* When using the bearer token the token needs the `applications.commands.update` scope and must be a `Client grant` token.
|
||||
* You will be able to update only your own application commands
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands}
|
||||
*/
|
||||
upsertGuildApplicationCommands: (guildId: BigString, commands: CreateApplicationCommand[]) => Promise<CamelizedDiscordApplicationCommand[]>
|
||||
upsertGuildApplicationCommands: (
|
||||
guildId: BigString,
|
||||
commands: CreateApplicationCommand[],
|
||||
options?: UpsertGuildApplicationCommandOptions,
|
||||
) => Promise<CamelizedDiscordApplicationCommand[]>
|
||||
/**
|
||||
* Bans a user from a guild.
|
||||
*
|
||||
@@ -2398,6 +2607,19 @@ export interface RestManager {
|
||||
* @see {@link https://discord.com/developers/docs/resources/guild#get-guild-member}
|
||||
*/
|
||||
getMember: (guildId: BigString, userId: BigString) => Promise<CamelizedDiscordMemberWithUser>
|
||||
/**
|
||||
* Gets the current member object.
|
||||
*
|
||||
* @param bearerToken - The access token of the user
|
||||
* @param guildId - The ID of the guild to get the member object for.
|
||||
* @returns An instance of {@link CamelizedDiscordMemberWithUser}.
|
||||
*
|
||||
* @remarks
|
||||
* The access tokens needs the `guilds.members.read` scope
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#get-current-user-guild-member}
|
||||
*/
|
||||
getCurrentMember: (guildId: BigString, bearerToken: string) => Promise<CamelizedDiscordMemberWithUser>
|
||||
/**
|
||||
* Gets the list of members for a guild.
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
GetMessagesOptions,
|
||||
GetReactions,
|
||||
GetScheduledEventUsers,
|
||||
GetUserGuilds,
|
||||
ListArchivedThreads,
|
||||
ListGuildMembers,
|
||||
} from '@discordeno/types'
|
||||
@@ -14,10 +15,6 @@ import type {
|
||||
export interface RestRoutes {
|
||||
/** A specific user route. */
|
||||
user: (id: BigString) => string
|
||||
/** Current bot user route. */
|
||||
userBot: () => string
|
||||
// OAuth2
|
||||
oauth2Application: () => string
|
||||
// Gateway Bot
|
||||
gatewayBot: () => string
|
||||
// Nitro Sticker Packs
|
||||
@@ -39,6 +36,8 @@ export interface RestRoutes {
|
||||
bulk: (channelId: BigString) => string
|
||||
/** Route for non-specific dm channel. */
|
||||
dm: () => string
|
||||
/** Route to add a user to an exiting group DM, requires an access token with the OAuth2 `gdm.join` scope */
|
||||
dmRecipient: (channelId: BigString, userId: BigString) => string
|
||||
/** Route for handling a specific pin. */
|
||||
pin: (channelId: BigString, messageId: BigString) => string
|
||||
/** Route for handling a channels pins. */
|
||||
@@ -108,6 +107,8 @@ export interface RestRoutes {
|
||||
guilds: {
|
||||
/** Routes for handling a non-specific guild. */
|
||||
all: () => string
|
||||
/** Route for fetching an user guilds. Requires `guilds` OAuth2 scope */
|
||||
userGuilds: (options?: GetUserGuilds) => string
|
||||
/** Route for handling audit logs in a guild. */
|
||||
auditlogs: (guildId: BigString, options?: GetGuildAuditLog) => string
|
||||
/** Routes for a guilds automoderation. */
|
||||
@@ -168,6 +169,8 @@ export interface RestRoutes {
|
||||
bot: (guildId: BigString) => string
|
||||
/** Route for handling a specific guild member. */
|
||||
member: (guildId: BigString, userId: BigString) => string
|
||||
/** Route to get the authenticated user. Requires the `guilds.members.read` OAuth2 scope */
|
||||
currentMember: (guildId: BigString) => string
|
||||
/** Route for handling non-specific guild members. */
|
||||
members: (guildId: BigString, options?: ListGuildMembers) => string
|
||||
/** Route for handling member searching in a guild. */
|
||||
@@ -234,6 +237,23 @@ export interface RestRoutes {
|
||||
message: (applicationId: BigString, token: string, messageId: BigString) => string
|
||||
}
|
||||
}
|
||||
/** Routes related to OAuth2 */
|
||||
oauth2: {
|
||||
/** Route to generate a new access token */
|
||||
tokenExchange: () => string
|
||||
/** Route to revoke an OAuth2 access_token */
|
||||
tokenRevoke: () => string
|
||||
/** Route to get information about the current authorization. Requires an access token */
|
||||
currentAuthorization: () => string
|
||||
/** Route to get information about the current application. Requires an access token */
|
||||
application: () => string
|
||||
/** Route to get the connection the user has. Requires the `connections` OAuth2 scope */
|
||||
connections: () => string
|
||||
/** Route to handling role-connection for an application */
|
||||
roleConnections: (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. */
|
||||
sticker: (stickerId: BigString) => string
|
||||
/** Route for handling all voice regions. */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
DiscordAccessTokenResponse,
|
||||
DiscordActionRow,
|
||||
DiscordActiveThreads,
|
||||
DiscordActivity,
|
||||
@@ -14,6 +15,7 @@ import type {
|
||||
DiscordApplicationCommandOption,
|
||||
DiscordApplicationCommandOptionChoice,
|
||||
DiscordApplicationCommandPermissions,
|
||||
DiscordApplicationRoleConnection,
|
||||
DiscordApplicationWebhook,
|
||||
DiscordArchivedThreads,
|
||||
DiscordAttachment,
|
||||
@@ -31,12 +33,14 @@ import type {
|
||||
DiscordChannelMention,
|
||||
DiscordChannelPinsUpdate,
|
||||
DiscordClientStatus,
|
||||
DiscordConnection,
|
||||
DiscordCreateApplicationCommand,
|
||||
DiscordCreateForumPostWithMessage,
|
||||
DiscordCreateGuildChannel,
|
||||
DiscordCreateGuildEmoji,
|
||||
DiscordCreateMessage,
|
||||
DiscordCreateWebhook,
|
||||
DiscordCurrentAuthorization,
|
||||
DiscordDefaultReactionEmoji,
|
||||
DiscordEditChannelPermissionOverridesOptions,
|
||||
DiscordEmbed,
|
||||
@@ -108,6 +112,7 @@ import type {
|
||||
DiscordModifyGuildWelcomeScreen,
|
||||
DiscordOptionalAuditEntryInfo,
|
||||
DiscordOverwrite,
|
||||
DiscordPartialGuild,
|
||||
DiscordPresenceUpdate,
|
||||
DiscordPrunedCount,
|
||||
DiscordReaction,
|
||||
@@ -133,6 +138,11 @@ import type {
|
||||
DiscordThreadMemberUpdate,
|
||||
DiscordThreadMembersUpdate,
|
||||
DiscordThreadMetadata,
|
||||
DiscordTokenExchange,
|
||||
DiscordTokenExchangeAuthorizationCode,
|
||||
DiscordTokenExchangeClientCredentials,
|
||||
DiscordTokenExchangeRefreshToken,
|
||||
DiscordTokenRevocation,
|
||||
DiscordTypingStart,
|
||||
DiscordUnavailableGuild,
|
||||
DiscordUser,
|
||||
@@ -157,6 +167,15 @@ export interface CamelizedDiscordGuildIntegrationsUpdate extends Camelize<Discor
|
||||
export interface CamelizedDiscordTypingStart extends Camelize<DiscordTypingStart> {}
|
||||
export interface CamelizedDiscordMember extends Camelize<DiscordMember> {}
|
||||
export interface CamelizedDiscordApplication extends Camelize<DiscordApplication> {}
|
||||
export interface CamelizedDiscordApplicationRoleConnection extends Camelize<DiscordApplicationRoleConnection> {}
|
||||
export type CamelizedDiscordTokenExchange = Camelize<DiscordTokenExchange>
|
||||
export interface CamelizedDiscordTokenExchangeAuthorizationCode extends Camelize<DiscordTokenExchangeAuthorizationCode> {}
|
||||
export interface CamelizedDiscordTokenExchangeRefreshToken extends Camelize<DiscordTokenExchangeRefreshToken> {}
|
||||
export interface CamelizedDiscordTokenExchangeClientCredentials extends Camelize<DiscordTokenExchangeClientCredentials> {}
|
||||
export interface CamelizedDiscordAccessTokenResponse extends Camelize<DiscordAccessTokenResponse> {}
|
||||
export interface CamelizedDiscordTokenRevocation extends Camelize<DiscordTokenRevocation> {}
|
||||
export interface CamelizedDiscordCurrentAuthorization extends Camelize<DiscordCurrentAuthorization> {}
|
||||
export interface CamelizedDiscordConnection extends Camelize<DiscordConnection> {}
|
||||
export interface CamelizedDiscordTeam extends Camelize<DiscordTeam> {}
|
||||
export interface CamelizedDiscordTeamMember extends Camelize<DiscordTeamMember> {}
|
||||
export interface CamelizedDiscordWebhookUpdate extends Camelize<DiscordWebhookUpdate> {}
|
||||
@@ -174,6 +193,7 @@ export type CamelizedDiscordWebhook = Camelize<DiscordWebhook>
|
||||
export interface CamelizedDiscordIncomingWebhook extends Camelize<DiscordIncomingWebhook> {}
|
||||
export interface CamelizedDiscordApplicationWebhook extends Camelize<DiscordApplicationWebhook> {}
|
||||
export interface CamelizedDiscordGuild extends Camelize<DiscordGuild> {}
|
||||
export interface CamelizedDiscordPartialGuild extends Camelize<DiscordPartialGuild> {}
|
||||
export interface CamelizedDiscordRole extends Camelize<DiscordRole> {}
|
||||
export interface CamelizedDiscordRoleTags extends Camelize<DiscordRoleTags> {}
|
||||
export interface CamelizedDiscordEmoji extends Camelize<DiscordEmoji> {}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
ApplicationCommandTypes,
|
||||
ApplicationFlags,
|
||||
AuditLogEvents,
|
||||
BigString,
|
||||
ButtonStyles,
|
||||
ChannelFlags,
|
||||
ChannelTypes,
|
||||
@@ -78,6 +79,135 @@ export interface DiscordUser {
|
||||
banner?: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes */
|
||||
export enum OAuth2Scope {
|
||||
/**
|
||||
* Allows your app to fetch data from a user's "Now Playing/Recently Played" list
|
||||
*
|
||||
* @remarks
|
||||
* This scope is not currently available for apps
|
||||
*/
|
||||
ActivitiesRead = 'activities.read',
|
||||
/**
|
||||
* Allows your app to update a user's activity
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
ActivitiesWrite = 'activities.write',
|
||||
/** Allows your app to read build data for a user's applications */
|
||||
ApplicationsBuildsRead = 'applications.builds.read',
|
||||
/**
|
||||
* Allows your app to upload/update builds for a user's applications
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
ApplicationsBuildsUpload = 'applications.builds.upload',
|
||||
/** Allows your app to use Application Commands in a guild */
|
||||
ApplicationsCommands = 'applications.commands',
|
||||
/**
|
||||
* Allows your app to update its Application Commands via this bearer token
|
||||
*
|
||||
* @remarks
|
||||
* This scope can only be used when using a [Client Credential Grant](https://discord.com/developers/docs/topics/oauth2#client-credentials-grant)
|
||||
*/
|
||||
ApplicationsCommandsUpdate = 'applications.commands.update',
|
||||
/** Allows your app to update permissions for its commands in a guild a user has permissions to */
|
||||
ApplicationCommandsPermissionsUpdate = 'applications.commands.permissions.update',
|
||||
/** Allows your app to read entitlements for a user's applications */
|
||||
ApplicationsEntitlements = 'applications.entitlements',
|
||||
/** Allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications */
|
||||
ApplicationsStoreUpdate = 'applications.store.update',
|
||||
/** For oauth2 bots, this puts the bot in the user's selected guild by default */
|
||||
Bot = 'bot',
|
||||
/** Allows requests to [/users/@me/connections](https://discord.com/developers/docs/resources/user#get-user-connections) */
|
||||
Connections = 'connections',
|
||||
/**
|
||||
* Allows your app to see information about the user's DMs and group DMs
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
DMChannelsRead = 'dm_channels.read',
|
||||
/** Adds the `email` filed to [/users/@me](https://discord.com/developers/docs/resources/user#get-current-user) */
|
||||
Email = 'email',
|
||||
/** Allows your app to join users to a group dm */
|
||||
GroupDMJoins = 'gdm.join',
|
||||
/** Allows requests to [/users/@me/guilds](https://discord.com/developers/docs/resources/user#get-current-user-guilds) */
|
||||
Guilds = 'guilds',
|
||||
/** Allows requests to [/guilds/{guild.id}/members/{user.id}](https://discord.com/developers/docs/resources/guild#add-guild-member) */
|
||||
GuildsJoin = 'guilds.join',
|
||||
/** Allows requests to [/users/@me/guilds/{guild.id}/member](https://discord.com/developers/docs/resources/user#get-current-user-guild-member) */
|
||||
GuildsMembersRead = 'guilds.members.read',
|
||||
/**
|
||||
* Allows requests to [/users/@me](https://discord.com/developers/docs/resources/user#get-current-user)
|
||||
*
|
||||
* @remarks
|
||||
* The return object from [/users/@me](https://discord.com/developers/docs/resources/user#get-current-user)
|
||||
* does NOT contain the `email` field unless the scope `email` is also used
|
||||
*/
|
||||
Identify = 'identify',
|
||||
/**
|
||||
* For local rpc server api access, this allows you to read messages from all client channels
|
||||
* (otherwise restricted to channels/guilds your app creates)
|
||||
*/
|
||||
MessagesRead = 'messages.read',
|
||||
/**
|
||||
* Allows your app to know a user's friends and implicit relationships
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RelationshipsRead = 'relationships.read',
|
||||
/** Allows your app to update a user's connection and metadata for the app */
|
||||
RoleConnectionsWrite = 'role_connections.write',
|
||||
/**
|
||||
* For local rpc server access, this allows you to control a user's local Discord client
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RPC = 'rpc',
|
||||
/**
|
||||
* For local rpc server access, this allows you to update a user's activity
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RPCActivitiesWrite = 'rpc.activities.write',
|
||||
/**
|
||||
* For local rpc server api access, this allows you to receive notifications pushed out to the user
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RPCNotificationsRead = 'rpc.notifications.read',
|
||||
/**
|
||||
* For local rpc server access, this allows you to read a user's voice settings and listen for voice events
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RPCVoiceRead = 'rpc.voice.read',
|
||||
/**
|
||||
* For local rpc server access, this allows you to update a user's voice settings
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
RPCVoiceWrite = 'rpc.voice.write',
|
||||
/**
|
||||
* Allows your app to connect to voice on user's behalf and see all the voice members
|
||||
*
|
||||
* @remarks
|
||||
* This scope requires Discord approval to be used
|
||||
*/
|
||||
Voice = 'voice',
|
||||
/** Generate a webhook that is returned in the oauth token response for authorization code grants */
|
||||
WebhookIncoming = 'webhook.incoming',
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#integration-object-integration-structure */
|
||||
export interface DiscordIntegration {
|
||||
/** Integration Id */
|
||||
@@ -111,7 +241,7 @@ export interface DiscordIntegration {
|
||||
/** The bot/OAuth2 application for discord integrations */
|
||||
application?: DiscordIntegrationApplication
|
||||
/** the scopes the application has been authorized for */
|
||||
scopes: string[]
|
||||
scopes: OAuth2Scope[]
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure */
|
||||
@@ -192,7 +322,7 @@ export interface DiscordMember {
|
||||
joined_at: string
|
||||
/** When the user started boosting the guild */
|
||||
premium_since?: string | null
|
||||
/** The permissions this member has in the guild. Only present on interaction events. */
|
||||
/** The permissions this member has in the guild. Only present on interaction events and OAuth2 current member fetch. */
|
||||
permissions?: string
|
||||
/** when the user's timeout will expire and the user will be able to communicate in the guild again (set null to remove timeout), null or a time in the past if the user is not timed out */
|
||||
communication_disabled_until?: string | null
|
||||
@@ -244,6 +374,149 @@ export interface DiscordApplication {
|
||||
role_connections_verification_url?: string
|
||||
}
|
||||
|
||||
export type DiscordTokenExchange = DiscordTokenExchangeAuthorizationCode | DiscordTokenExchangeRefreshToken | DiscordTokenExchangeClientCredentials
|
||||
|
||||
export interface DiscordTokenExchangeAuthorizationCode {
|
||||
/** Application's client id */
|
||||
client_id: BigString
|
||||
/** application's client secret */
|
||||
client_secret: string
|
||||
grant_type: 'authorization_code'
|
||||
/** The code for the token exchange */
|
||||
code: string
|
||||
/** The redirect_uri associated with this authorization */
|
||||
redirect_uri: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/oauth2#client-credentials-grant */
|
||||
export interface DiscordTokenExchangeRefreshToken {
|
||||
/** Application's client id */
|
||||
client_id: BigString
|
||||
/** application's client secret */
|
||||
client_secret: string
|
||||
grant_type: 'refresh_token'
|
||||
/** the user's refresh token */
|
||||
refresh_token: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/oauth2#client-credentials-grant */
|
||||
export interface DiscordTokenExchangeClientCredentials {
|
||||
/** Application's client id */
|
||||
client_id: BigString
|
||||
/** application's client secret */
|
||||
client_secret: string
|
||||
grant_type: 'client_credentials'
|
||||
/** The scope(s) for the access token */
|
||||
scope: OAuth2Scope[]
|
||||
}
|
||||
|
||||
export interface DiscordAccessTokenResponse {
|
||||
/** The access token of the user */
|
||||
access_token: string
|
||||
/** The type of token */
|
||||
token_type: string
|
||||
/** The number of seconds after that the access token is expired */
|
||||
expires_in: number
|
||||
/**
|
||||
* The refresh token to refresh the access token
|
||||
*
|
||||
* @remarks
|
||||
* When the token exchange is a client credentials type grant this value is not defined.
|
||||
*/
|
||||
refresh_token: string
|
||||
/** The scopes for the access token */
|
||||
scope: string
|
||||
/** The webhook the user created for the application. Requires the `webhook.incoming` scope */
|
||||
webhook?: DiscordIncomingWebhook
|
||||
/** The guild the bot has been added. Requires the `bot` scope */
|
||||
guild?: DiscordGuild
|
||||
}
|
||||
|
||||
export interface DiscordTokenRevocation {
|
||||
/** Application's client id */
|
||||
client_id: BigString
|
||||
/** application's client secret */
|
||||
client_secret: string
|
||||
/** The access token to revoke */
|
||||
token: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/oauth2#get-current-authorization-information-response-structure */
|
||||
export interface DiscordCurrentAuthorization {
|
||||
application: DiscordApplication
|
||||
/** the scopes the user has authorized the application for */
|
||||
scopes: OAuth2Scope[]
|
||||
/** when the access token expires */
|
||||
expires: string
|
||||
/** the user who has authorized, if the user has authorized with the `identify` scope */
|
||||
user?: DiscordUser
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#connection-object-connection-structure */
|
||||
export interface DiscordConnection {
|
||||
/** id of the connection account */
|
||||
id: string
|
||||
/** the username of the connection account */
|
||||
name: string
|
||||
/** the service of this connection */
|
||||
type: DiscordConnectionServiceType
|
||||
/** whether the connection is revoked */
|
||||
revoked?: boolean
|
||||
/** an array of partial server integrations */
|
||||
integrations?: Array<Partial<DiscordIntegration>>
|
||||
/** whether the connection is verified */
|
||||
verified: boolean
|
||||
/** whether friend sync is enabled for this connection */
|
||||
friend_sync: boolean
|
||||
/** whether activities related to this connection will be shown in presence updates */
|
||||
show_activity: boolean
|
||||
/** whether this connection has a corresponding third party OAuth2 token */
|
||||
two_way_link: boolean
|
||||
/** visibility of this connection */
|
||||
visibility: DiscordConnectionVisibility
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#connection-object-services */
|
||||
export enum DiscordConnectionServiceType {
|
||||
BattleNet = 'battlenet',
|
||||
eBay = 'ebay',
|
||||
EpicGames = 'epicgames',
|
||||
Facebook = 'facebook',
|
||||
GitHub = 'github',
|
||||
Instagram = 'instagram',
|
||||
LeagueOfLegends = 'leagueoflegends',
|
||||
PayPal = 'paypal',
|
||||
PlayStationNetwork = 'playstation',
|
||||
Reddit = 'reddit',
|
||||
RiotGames = 'riotgames',
|
||||
Spotify = 'spotify',
|
||||
Skype = 'skype',
|
||||
Steam = 'steam',
|
||||
TikTok = 'tiktok',
|
||||
Twitch = 'twitch',
|
||||
Twitter = 'twitter',
|
||||
Xbox = 'xbox',
|
||||
YouTube = 'youtube',
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#connection-object-visibility-types */
|
||||
export enum DiscordConnectionVisibility {
|
||||
/** invisible to everyone except the user themselves */
|
||||
None = 0,
|
||||
/** visible to everyone */
|
||||
Everyone = 1,
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#application-role-connection-object-application-role-connection-structure */
|
||||
export interface DiscordApplicationRoleConnection {
|
||||
/** the vanity name of the platform a bot has connected (max 50 characters) */
|
||||
platform_name: string | null
|
||||
/** the username on the platform a bot has connected (max 100 characters) */
|
||||
platform_username: string | null
|
||||
/** object mapping application role connection metadata keys to their stringified value (max 100 characters) for the user on the platform a bot has connected */
|
||||
metadata: Record<string, string>
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/teams#data-models-team-object */
|
||||
export interface DiscordTeam {
|
||||
/** A hash of the image of the team's icon */
|
||||
@@ -584,6 +857,25 @@ export interface DiscordGuild {
|
||||
stickers?: DiscordSticker[]
|
||||
}
|
||||
|
||||
export interface DiscordPartialGuild {
|
||||
/** Guild name (2-100 characters, excluding trailing and leading whitespace) */
|
||||
name: string
|
||||
/** Guild id */
|
||||
id: string
|
||||
/** Icon hash */
|
||||
icon: string | null
|
||||
/** true if the user is the owner of the guild */
|
||||
owner: boolean
|
||||
/** total permissions for the user in the guild (excludes overwrites and implicit permissions) */
|
||||
permissions: string
|
||||
/** Enabled guild features */
|
||||
features: GuildFeatures[]
|
||||
/** Approximate number of members in this guild, returned from the GET /guilds/id endpoint when with_counts is true */
|
||||
approximate_member_count?: number
|
||||
/** Approximate number of non-offline members in this guild, returned from the GET /guilds/id endpoint when with_counts is true */
|
||||
approximate_presence_count?: number
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/permissions#role-object-role-structure */
|
||||
export interface DiscordRole {
|
||||
/** Role id */
|
||||
@@ -2320,7 +2612,7 @@ export interface DiscordGuildWidgetSettings {
|
||||
|
||||
export interface DiscordInstallParams {
|
||||
/** the scopes to add the application to the server with */
|
||||
scopes: string[]
|
||||
scopes: OAuth2Scope[]
|
||||
/** the permissions to request for the bot role */
|
||||
permissions: string
|
||||
}
|
||||
|
||||
@@ -337,6 +337,18 @@ export interface GetGuildAuditLog {
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#get-current-user-guilds-query-string-params */
|
||||
export interface GetUserGuilds {
|
||||
/** Get guilds before this guild ID */
|
||||
before?: BigString
|
||||
/** Get guilds after this guild ID */
|
||||
after?: BigString
|
||||
/** Maximum number of entries (between 1-200) to return, defaults to 200 */
|
||||
limit?: number
|
||||
/** Include approximate member and presence counts in response, defaults to false */
|
||||
withCounts?: boolean
|
||||
}
|
||||
|
||||
export interface GetBans {
|
||||
/** Number of users to return (up to maximum 1000). Default: 1000 */
|
||||
limit?: number
|
||||
@@ -532,6 +544,56 @@ export interface CreateGuildChannel {
|
||||
defaultSortOrder?: SortOrderTypes | null
|
||||
}
|
||||
|
||||
export interface CreateGlobalApplicationCommandOptions {
|
||||
/** The bearer token of the developer of the application */
|
||||
bearerToken: string
|
||||
}
|
||||
|
||||
export interface CreateGuildApplicationCommandOptions {
|
||||
/** The bearer token of the developer of the application */
|
||||
bearerToken: string
|
||||
}
|
||||
|
||||
export interface UpsertGlobalApplicationCommandOptions {
|
||||
/** The bearer token of the developer of the application */
|
||||
bearerToken: string
|
||||
}
|
||||
|
||||
export interface UpsertGuildApplicationCommandOptions {
|
||||
/** The bearer token of the developer of the application */
|
||||
bearerToken: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#create-group-dm-json-params */
|
||||
export interface GetGroupDmOptions {
|
||||
/** Access tokens of users that have granted your app the `gdm.join` scope */
|
||||
accessTokens: string[]
|
||||
/** A mapping of user ids to their respective nicknames */
|
||||
nicks?: Record<string, string>
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#group-dm-add-recipient-json-params */
|
||||
export interface AddDmRecipientOptions {
|
||||
/** access token of a user that has granted your app the `gdm.join` scope */
|
||||
accessToken: string
|
||||
/** nickname of the user being added */
|
||||
nick?: string
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#add-guild-member-json-params */
|
||||
export interface AddGuildMemberOptions {
|
||||
/** access token of a user that has granted your app the `guilds.join` scope */
|
||||
accessToken: string
|
||||
/** Value to set user's nickname to. Requires MANAGE_NICKNAMES permission on the bot */
|
||||
nick?: string
|
||||
/** Array of role ids the member is assigned. Requires MANAGE_ROLES permission on the bot */
|
||||
roles?: string[]
|
||||
/** Whether the user is muted in voice channels. Requires MUTE_MEMBERS permission on the bot */
|
||||
mute?: boolean
|
||||
/** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission on the bot */
|
||||
deaf?: boolean
|
||||
}
|
||||
|
||||
export interface ModifyChannel {
|
||||
/** 1-100 character channel name */
|
||||
name?: string
|
||||
@@ -872,6 +934,14 @@ export interface ApplicationCommandPermissions {
|
||||
permission: boolean
|
||||
}
|
||||
|
||||
/** Additional proprieties for https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions and https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions */
|
||||
export interface GetApplicationCommandPermissionOptions {
|
||||
/** Access token of the user. Requires the `applications.commands.permissions.update` scope */
|
||||
accessToken: string
|
||||
/** Id of the application */
|
||||
applicationId: BigString
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#create-guild */
|
||||
export interface CreateGuild {
|
||||
/** Name of the guild (1-100 characters) */
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
export * from './Collection.js'
|
||||
export * from './base64.js'
|
||||
export * from './bucket.js'
|
||||
export * from './casing.js'
|
||||
export * from './Collection.js'
|
||||
export * from './colors.js'
|
||||
export * from './hash.js'
|
||||
export * from './images.js'
|
||||
export * from './logger.js'
|
||||
export * from './oauth2.js'
|
||||
export * from './permissions.js'
|
||||
export * from './reactions.js'
|
||||
export * from './token.js'
|
||||
|
||||
75
packages/utils/src/oauth2.ts
Normal file
75
packages/utils/src/oauth2.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { BigString, OAuth2Scope, PermissionStrings } from '@discordeno/types'
|
||||
import { calculateBits } from './permissions.js'
|
||||
|
||||
export function createOAuth2Link(options: CreateOAuth2LinkOptions): string {
|
||||
const joinedScopeString = options.scope.join(' ')
|
||||
|
||||
let url = `https://discord.com/oauth2/authorize?client_id=${options.clientId}&scope=${joinedScopeString}`
|
||||
|
||||
if (options.responseType) url += `&response_type=${options.responseType}`
|
||||
if (options.state) url += `&state=${encodeURIComponent(options.state)}`
|
||||
if (options.redirectUri) url += `&redirect_uri=${encodeURIComponent(options.redirectUri)}`
|
||||
if (options.prompt) url += `&prompt=${options.prompt}`
|
||||
if (options.permissions) url += `&permissions=${Array.isArray(options.permissions) ? calculateBits(options.permissions) : options.permissions}`
|
||||
if (options.guildId) url += `&guild_id=${options.guildId}`
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
if (options.disableGuildSelect !== undefined) url += `&disable_guild_select=${options.disableGuildSelect}`
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
export interface CreateOAuth2LinkOptions {
|
||||
/**
|
||||
* The type of response
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only if using either OAuth2 authorization, implicit or not, or [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
responseType?: 'code' | 'token'
|
||||
/** The id of the application */
|
||||
clientId: BigString
|
||||
/** The scopes for the application */
|
||||
scope: OAuth2Scope[]
|
||||
/**
|
||||
* The optional state for security
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/oauth2#state-and-security
|
||||
*/
|
||||
state?: string
|
||||
/**
|
||||
* The redirect uri for after the authentication
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only if using either OAuth2 authorization, implicit or not, or [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
redirectUri?: string
|
||||
/**
|
||||
* The type of prompt to give to the user
|
||||
*
|
||||
* @remarks
|
||||
* If set to `none`, it will skip the authorization screen and redirect them back to your redirect URI without requesting their authorization.
|
||||
* For passthrough scopes, like bot and webhook.incoming, authorization is always required.
|
||||
*/
|
||||
prompt?: 'consent' | 'none'
|
||||
/**
|
||||
* The permissions of the invited bot
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow) or with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
permissions?: BigString | PermissionStrings[]
|
||||
/**
|
||||
* Pre-fills the dropdown picker with a guild for the user
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow) or with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization) or with the `webhook.incoming` scope
|
||||
*/
|
||||
guildId?: BigString
|
||||
/**
|
||||
* Disallows the user from changing the guild dropdown if set to true
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow), with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization) or with the `webhook.incoming` scope
|
||||
*/
|
||||
disableGuildSelect?: boolean
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
/** Removes the Bot before the token. */
|
||||
const validTokenPrefixes = ['Bot', 'Bearer']
|
||||
|
||||
/** Removes the Bot/Bearer before the token. */
|
||||
export function removeTokenPrefix(token?: string, type: 'GATEWAY' | 'REST' = 'REST'): string {
|
||||
// If no token is provided, throw an error
|
||||
if (token === undefined) {
|
||||
throw new Error(`The ${type} was not given a token. Please provide a token and try again.`)
|
||||
}
|
||||
|
||||
const splittedToken = token.split(' ')
|
||||
|
||||
// If the token does not have a prefix just return token
|
||||
if (!token.startsWith('Bot ')) return token
|
||||
if (splittedToken.length < 2 || !validTokenPrefixes.includes(splittedToken[0])) return token
|
||||
// Remove the prefix and return only the token.
|
||||
return token.substring(token.indexOf(' ') + 1)
|
||||
return splittedToken.splice(1).join(' ')
|
||||
}
|
||||
|
||||
/** Get the bot id from the bot token. WARNING: Discord staff has mentioned this may not be stable forever. Use at your own risk. However, note for over 5 years this has never broken. */
|
||||
|
||||
Reference in New Issue
Block a user