From 05a20c3bf8fc33033b3a1bd606ab00f9cd9143ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20Oszcz=C4=99da?= Date: Fri, 8 Mar 2024 04:39:48 +0000 Subject: [PATCH] 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. --- packages/benchmark/src/benchmarks/rest.ts | 2 +- packages/bot/src/bot.ts | 21 ++++++++++----- .../handlers/members/GUILD_MEMBERS_CHUNK.ts | 2 +- packages/gateway/src/manager.ts | 26 +++++++++++++------ packages/rest/src/manager.ts | 7 +---- packages/rest/src/types.ts | 4 +-- packages/rest/tests/unit/manager.spec.ts | 6 +---- 7 files changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/benchmark/src/benchmarks/rest.ts b/packages/benchmark/src/benchmarks/rest.ts index 1cca89ae1..d0f2ad129 100644 --- a/packages/benchmark/src/benchmarks/rest.ts +++ b/packages/benchmark/src/benchmarks/rest.ts @@ -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') diff --git a/packages/bot/src/bot.ts b/packages/bot/src/bot.ts index b4040ef27..5734452e9 100644 --- a/packages/bot/src/bot.ts +++ b/packages/bot/src/bot.ts @@ -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> /** Any options you wish to provide to the gateway manager. */ - gateway?: CreateGatewayManagerOptions + gateway?: CreateGatewayManagerOptions & Partial> /** The event handlers. */ - events: Partial + events?: Partial + /** The functions that should transform discord objects to discordeno shaped objects. */ + transformers?: Partial + /** The handler functions that should handle incoming discord payloads from gateway and call an event. */ + handlers?: Partial /** * @deprecated Use with caution * diff --git a/packages/bot/src/handlers/members/GUILD_MEMBERS_CHUNK.ts b/packages/bot/src/handlers/members/GUILD_MEMBERS_CHUNK.ts index a06417880..42c8909ef 100644 --- a/packages/bot/src/handlers/members/GUILD_MEMBERS_CHUNK.ts +++ b/packages/bot/src/handlers/members/GUILD_MEMBERS_CHUNK.ts @@ -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 diff --git a/packages/gateway/src/manager.ts b/packages/gateway/src/manager.ts index ceb639e1a..4f6bd6636 100644 --- a/packages/gateway/src/manager.ts +++ b/packages/gateway/src/manager.ts @@ -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>((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 } } } @@ -541,6 +539,18 @@ export interface GatewayManager extends Required { * @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state} */ leaveVoiceChannel: (guildId: BigString) => Promise + /** 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 + } + } } export interface RequestMemberRequest { diff --git a/packages/rest/src/manager.ts b/packages/rest/src/manager.ts index 53dc5404b..6ad12387e 100644 --- a/packages/rest/src/manager.ts +++ b/packages/rest/src/manager.ts @@ -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 diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index 4a9e28aa3..30639a417 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -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. */ diff --git a/packages/rest/tests/unit/manager.spec.ts b/packages/rest/tests/unit/manager.spec.ts index d55f4670c..03e4956e0 100644 --- a/packages/rest/tests/unit/manager.spec.ts +++ b/packages/rest/tests/unit/manager.spec.ts @@ -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() })