refactor(bot, gateway, rest)!: Improve the interface of the createBot() function. (#3422)

* misc: Make the token a required property on the rest manager.

* refactor: Make unnecessarily required properties optional.

* misc: Improve interface for `createBot()` and allow passing in `transformers`/`handlers`.

* fix: Test made redundant by changes still being included.

* fix: Missing non-null assertions.

* fix: Benchmarks failing.

* misc: Remove `cache.requestMembers.pending` as an exposed option.

* style: Switch back to interface approach.
This commit is contained in:
Dorian Oszczęda
2024-03-07 22:39:48 -06:00
committed by GitHub
parent 46c93385c1
commit 05a20c3bf8
7 changed files with 38 additions and 30 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import { createRestManager } from '@discordeno/rest'
import { suite } from '../benchmarkSuite.js'
const rest = createRestManager({ applicationId: 1n })
const rest = createRestManager({ token: '1', applicationId: 1n })
suite.add(`rest.simplifyUrl`, () => {
rest.simplifyUrl('/messages/555555555555555555', 'PUT')
+14 -7
View File
@@ -27,6 +27,7 @@ import type { Sticker } from './transformers/sticker.js'
import type { ThreadMember } from './transformers/threadMember.js'
import type { User } from './transformers/user.js'
import type { VoiceState } from './transformers/voiceState.js'
import type { BotGatewayHandlerOptions } from './typings.js'
/**
* Create a bot object that will maintain the rest and gateway connection.
@@ -36,7 +37,10 @@ import type { VoiceState } from './transformers/voiceState.js'
*/
export function createBot(options: CreateBotOptions): Bot {
if (!options.rest) options.rest = { token: options.token, applicationId: options.applicationId }
if (!options.gateway) options.gateway = { token: options.token, events: {} }
if (!options.rest.token) options.rest.token = options.token
if (!options.gateway) options.gateway = { token: options.token }
if (!options.gateway.token) options.gateway.token = options.token
if (!options.gateway.events) options.gateway.events = {}
if (!options.gateway.events.message) {
options.gateway.events.message = async (shard, data) => {
// TRIGGER RAW EVENT
@@ -50,7 +54,6 @@ export function createBot(options: CreateBotOptions): Bot {
}
}
options.rest.token = options.token
options.gateway.intents = options.intents
options.gateway.preferSnakeCase = true
@@ -59,8 +62,8 @@ export function createBot(options: CreateBotOptions): Bot {
const bot: Bot = {
id,
applicationId: id,
transformers: createTransformers({}, { defaultDesiredPropertiesValue: options.defaultDesiredPropertiesValue ?? false }),
handlers: createBotGatewayHandlers({}),
transformers: createTransformers(options.transformers ?? {}, { defaultDesiredPropertiesValue: options.defaultDesiredPropertiesValue ?? false }),
handlers: createBotGatewayHandlers(options.handlers ?? {}),
rest: createRestManager(options.rest),
gateway: createGatewayManager(options.gateway),
events: options.events ?? {},
@@ -101,11 +104,15 @@ export interface CreateBotOptions {
/** The bot's intents that will be used to make a connection with discords gateway. */
intents?: GatewayIntents
/** Any options you wish to provide to the rest manager. */
rest?: CreateRestManagerOptions
rest?: CreateRestManagerOptions & Partial<Pick<CreateRestManagerOptions, 'token'>>
/** Any options you wish to provide to the gateway manager. */
gateway?: CreateGatewayManagerOptions
gateway?: CreateGatewayManagerOptions & Partial<Pick<CreateGatewayManagerOptions, 'token'>>
/** The event handlers. */
events: Partial<EventHandlers>
events?: Partial<EventHandlers>
/** The functions that should transform discord objects to discordeno shaped objects. */
transformers?: Partial<Transformers>
/** The handler functions that should handle incoming discord payloads from gateway and call an event. */
handlers?: Partial<BotGatewayHandlerOptions>
/**
* @deprecated Use with caution
*
@@ -6,7 +6,7 @@ export async function handleGuildMembersChunk(bot: Bot, data: DiscordGatewayPayl
const payload = data.d as DiscordGuildMembersChunk
// If it's not enabled skip checks.
if (!bot.gateway.cache.requestMembers?.enabled) return
if (!bot.gateway.cache.requestMembers.enabled) return
// If this request has no nonce, skip checks.
if (!payload.nonce) return
+18 -8
View File
@@ -26,7 +26,7 @@ export function createGatewayManager(options: CreateGatewayManagerOptions): Gate
}
const gateway: GatewayManager = {
events: options.events,
events: options.events ?? {},
compress: options.compress ?? false,
intents: options.intents ?? 0,
properties: {
@@ -172,7 +172,7 @@ export function createGatewayManager(options: CreateGatewayManagerOptions): Gate
url: this.url,
version: this.version,
},
events: options.events,
events: options.events ?? {},
requestIdentify: async () => {
await gateway.identify(shardId)
},
@@ -186,7 +186,7 @@ export function createGatewayManager(options: CreateGatewayManagerOptions): Gate
if (this.preferSnakeCase) {
shard.forwardToBot = async (payload) => {
options.events.message?.(shard!, payload)
shard!.events.message?.(shard!, payload)
}
}
@@ -294,11 +294,11 @@ export function createGatewayManager(options: CreateGatewayManagerOptions): Gate
}
const members =
!gateway.cache.requestMembers?.enabled || !options?.nonce
!gateway.cache.requestMembers.enabled || !options?.nonce
? []
: new Promise<Camelize<DiscordMemberWithUser[]>>((resolve, reject) => {
// Should never happen.
if (!gateway.cache.requestMembers?.enabled || !options?.nonce) {
if (!gateway.cache.requestMembers.enabled || !options?.nonce) {
reject(new Error("Can't request the members without the nonce or with the feature disabled."))
return
}
@@ -425,7 +425,7 @@ export interface CreateGatewayManagerOptions {
*/
version?: number
/** The events handlers */
events: ShardEvents
events?: ShardEvents
/** This managers cache related settings. */
cache?: {
requestMembers?: {
@@ -434,8 +434,6 @@ export interface CreateGatewayManagerOptions {
* @default false
*/
enabled?: boolean
/** The pending requests. */
pending: Collection<string, RequestMemberRequest>
}
}
}
@@ -541,6 +539,18 @@ export interface GatewayManager extends Required<CreateGatewayManagerOptions> {
* @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state}
*/
leaveVoiceChannel: (guildId: BigString) => Promise<void>
/** This managers cache related settings. */
cache: {
requestMembers: {
/**
* Whether or not request member requests should be cached.
* @default false
*/
enabled: boolean
/** The pending requests. */
pending: Collection<string, RequestMemberRequest>
}
}
}
export interface RequestMemberRequest {
+1 -6
View File
@@ -76,12 +76,7 @@ export const RATE_LIMIT_LIMIT_HEADER = 'x-ratelimit-limit'
export const RATE_LIMIT_SCOPE_HEADER = 'x-ratelimit-scope'
export function createRestManager(options: CreateRestManagerOptions): RestManager {
const applicationId = options.applicationId ? BigInt(options.applicationId) : options.token ? getBotIdFromToken(options.token) : undefined
if (!applicationId) {
throw new Error(
'`applicationId` was not provided and was not able to extract the id from the bots token. Please explicitly pass `applicationId` to the rest manager.',
)
}
const applicationId = options.applicationId ? BigInt(options.applicationId) : getBotIdFromToken(options.token)
const baseUrl = options.proxy?.baseUrl ?? DISCORD_API_URL
+2 -2
View File
@@ -134,7 +134,7 @@ import type { RestRoutes } from './typings/routes.js'
export interface CreateRestManagerOptions {
/** The bot token which will be used to make requests. */
token?: string
token: string
/**
* For old bots that have a different bot id and application id.
* @default bot id from token
@@ -170,7 +170,7 @@ export interface CreateRestManagerOptions {
export interface RestManager {
/** The bot token which will be used to make requests. */
token?: string
token: string
/** The application id. Normally this is not required for recent bots but old bot's application id is sometimes different from the bot id so it is required for those bots. */
applicationId: bigint
/** The api version to use when making requests. Only the latest supported version will be tested. */
+1 -5
View File
@@ -38,10 +38,6 @@ describe('[rest] manager', () => {
expect(rest.baseUrl).to.be.equal(options.proxy.baseUrl)
})
it('With a falsy token', () => {
expect(() => createRestManager({ token: '' })).throws()
})
it('With an application id', () => {
const subrest = createRestManager({ ...options, applicationId: '130136895395987456' })
expect(subrest.applicationId).to.be.equal(130136895395987456n)
@@ -173,7 +169,7 @@ describe('[rest] manager', () => {
let time: sinon.SinonFakeTimers
beforeEach(() => {
rest = createRestManager({ applicationId: 1n })
rest = createRestManager({ token: '1', applicationId: 1n })
time = sinon.useFakeTimers()
})