diff --git a/src/cache/index.ts b/src/cache/index.ts index 40ef70c..687020f 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -620,7 +620,7 @@ export class Cache { name: '[CACHE]', }); await this.adapter.flush(); - // this method will only check the cache for `users`, `members` y `channels` + // this method will only check the cache for `users`, `members`, and `channels` // likewise these have the three types of resources (GuildRelatedResource, GuildBasedResource, BaseResource) // will also check `overwrites`, since the latter stores an array not as an object but as data. diff --git a/src/client/client.ts b/src/client/client.ts index 50bc1cc..c7afd87 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -3,7 +3,7 @@ import { type Awaitable, type DeepPartial, type If, - type MakePartial, + type PickPartial, type WatcherPayload, type WatcherSendToShard, assertString, @@ -219,5 +219,5 @@ export interface ClientOptions extends BaseClientOptions { reply?: (ctx: CommandContext) => boolean; }; handlePayload?: ShardManagerOptions['handlePayload']; - resharding?: MakePartial, 'getInfo'>; + resharding?: PickPartial, 'getInfo'>; } diff --git a/src/common/bot/watcher.ts b/src/common/bot/watcher.ts index e55c4b6..318b04a 100644 --- a/src/common/bot/watcher.ts +++ b/src/common/bot/watcher.ts @@ -1,9 +1,9 @@ import type { GatewayDispatchPayload, GatewaySendPayload } from '../../types'; import type { ShardManager, ShardManagerOptions } from '../../websocket'; -import type { MakePartial } from '../types/util'; +import type { PickPartial } from '../types/util'; export interface WatcherOptions - extends MakePartial< + extends PickPartial< Omit, | 'compress' | 'presence' diff --git a/src/common/types/util.ts b/src/common/types/util.ts index eea49db..0dff3ea 100644 --- a/src/common/types/util.ts +++ b/src/common/types/util.ts @@ -14,7 +14,7 @@ export type ToClass = new ( export type StringToNumber = T extends `${infer N extends number}` ? N : never; -export type MakePartial = Omit & { [P in K]?: T[P] }; +export type PickPartial = Omit & { [P in K]?: T[P] }; export type MakeDeepPartial = Omit & { [P in K]?: DeepPartial; @@ -90,6 +90,7 @@ export type NulleableCoalising = NonFalsy extends never ? B : A; export type TupleOr = ValueOf extends never ? A : TupleOr, Tail>; export type MakeRequired = T & { [P in K]-?: NonFalsy }; +export type PickRequired = Omit & { [P in K]: NonFalsy }; export type NonFalsy = T extends false | 0 | '' | null | undefined | 0n ? never : T; diff --git a/src/components/handler.ts b/src/components/handler.ts index fb8ac3a..89d1f93 100644 --- a/src/components/handler.ts +++ b/src/components/handler.ts @@ -58,28 +58,31 @@ export class ComponentHandler extends BaseHandler { channelId: string, guildId: string | undefined, options: ListenerOptions = {}, + components: COMPONENTS['components'] = [], ): CreateComponentCollectorResult { this.values.set(messageId, { messageId, channelId, guildId, options, - components: [], + components, idle: options.idle && options.idle > 0 ? setTimeout(() => { - this.deleteValue(messageId); + const old = this.clearValue(messageId); + if (!old) return; options.onStop?.('idle', () => { - this.createComponentCollector(messageId, channelId, guildId, options); + this.createComponentCollector(messageId, channelId, guildId, options, old.components); }); }, options.idle) : undefined, timeout: options.timeout && options.timeout > 0 ? setTimeout(() => { - this.deleteValue(messageId); + const old = this.clearValue(messageId); + if (!old) return; options.onStop?.('timeout', () => { - this.createComponentCollector(messageId, channelId, guildId, options); + this.createComponentCollector(messageId, channelId, guildId, options, old.components); }); }, options.timeout) : undefined, @@ -97,9 +100,10 @@ export class ComponentHandler extends BaseHandler { //@ts-expect-error generic run: this.values.get(messageId)!.__run, stop: (reason?: string) => { - this.deleteValue(messageId); + const old = this.clearValue(messageId); + if (!old) return; options.onStop?.(reason, () => { - this.createComponentCollector(messageId, channelId, guildId, options); + this.createComponentCollector(messageId, channelId, guildId, options, old.components); }); }, }; @@ -116,10 +120,10 @@ export class ComponentHandler extends BaseHandler { await component.callback( interaction, reason => { + this.clearValue(id); row.options?.onStop?.(reason ?? 'stop', () => { - this.createComponentCollector(row.messageId, row.channelId, row.guildId, row.options); + this.createComponentCollector(row.messageId, row.channelId, row.guildId, row.options, row.components); }); - this.deleteValue(id); }, () => { this.resetTimeouts(id); @@ -149,15 +153,26 @@ export class ComponentHandler extends BaseHandler { } deleteValue(id: string, reason?: string) { + const component = this.clearValue(id); + if (!component) return; + component.options?.onStop?.(reason, () => { + this.createComponentCollector( + component.messageId, + component.channelId, + component.guildId, + component.options, + component.components, + ); + }); + } + + clearValue(id: string) { const component = this.values.get(id); - if (component) { - component.options?.onStop?.(reason, () => { - this.createComponentCollector(component.messageId, component.channelId, component.guildId, component.options); - }); - clearTimeout(component.timeout); - clearTimeout(component.idle); - this.values.delete(id); - } + if (!component) return; + clearTimeout(component.timeout); + clearTimeout(component.idle); + this.values.delete(id); + return component; } stablishDefaults(component: ComponentCommands) { diff --git a/src/components/modalcontext.ts b/src/components/modalcontext.ts index a3eac3e..aaf225c 100644 --- a/src/components/modalcontext.ts +++ b/src/components/modalcontext.ts @@ -1,4 +1,4 @@ -import type { AllChannels, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..'; +import type { AllChannels, Interaction, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..'; import type { GuildMemberStructure, GuildStructure } from '../client/transformers'; import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands'; import { BaseContext } from '../commands/basecontext'; @@ -9,7 +9,6 @@ import type { ModalCreateBodyRequest, UnionToTuple, } from '../common'; -import type { Interaction } from '../structures/Interaction'; import { MessageFlags } from '../types'; export interface ModalContext extends BaseContext, ExtendContext {} diff --git a/src/deps/mixer.ts b/src/deps/mixer.ts index 1d02567..de0a214 100644 --- a/src/deps/mixer.ts +++ b/src/deps/mixer.ts @@ -3,7 +3,23 @@ * @param c The class to get the descriptors of. * @returns The descriptors of the class. */ -function getDescriptors(c: TypeClass) { +function getDenoDescriptors(c: TypeClass) { + const protos = [c.prototype]; + + let v = c; + while ((v = Object.getPrototypeOf(v))) { + if (v.prototype) protos.push(v.prototype); + } + + return protos.map(x => Object.getOwnPropertyDescriptors(x)); +} + +/** + * Gets the descriptors of a class. + * @param c The class to get the descriptors of. + * @returns The descriptors of the class. + */ +function getNodeDescriptors(c: TypeClass) { let proto = c.prototype; const result: Record | PropertyDescriptor>[] = []; while (proto) { @@ -16,6 +32,12 @@ function getDescriptors(c: TypeClass) { return result; } +function getDescriptors(c: TypeClass) { + //@ts-expect-error + // biome-ignore lint/correctness/noUndeclaredVariables: + return typeof Deno === 'undefined' ? getNodeDescriptors(c) : getDenoDescriptors(c); +} + /** * Mixes a class with other classes. * @param args The classes to mix. diff --git a/src/events/hooks/thread.ts b/src/events/hooks/thread.ts index 3ed6375..16ea1c9 100644 --- a/src/events/hooks/thread.ts +++ b/src/events/hooks/thread.ts @@ -14,8 +14,8 @@ export const THREAD_CREATE = (self: UsingClient, data: GatewayThreadCreateDispat return Transformers.ThreadChannel(self, data); }; -export const THREAD_DELETE = (self: UsingClient, data: GatewayThreadDeleteDispatchData) => { - return Transformers.ThreadChannel(self, data); +export const THREAD_DELETE = async (self: UsingClient, data: GatewayThreadDeleteDispatchData) => { + return (await self.cache.channels?.get(data.id)) ?? toCamelCase(data); }; export const THREAD_LIST_SYNC = (_self: UsingClient, data: GatewayThreadListSyncDispatchData) => { diff --git a/src/structures/Interaction.ts b/src/structures/Interaction.ts index fd83803..6707cd8 100644 --- a/src/structures/Interaction.ts +++ b/src/structures/Interaction.ts @@ -363,7 +363,7 @@ export class BaseInteraction< } } - fetchGuild(force = false) { + async fetchGuild(force = false) { return this.guildId ? this.client.guilds.fetch(this.guildId, force) : undefined; } } diff --git a/src/structures/channels.ts b/src/structures/channels.ts index 144402a..db52ab1 100644 --- a/src/structures/channels.ts +++ b/src/structures/channels.ts @@ -288,6 +288,7 @@ export class MessagesMethods extends DiscordBase { ctx.client.reactions.purge(messageId, ctx.channelId, emoji), }; } + static pins(ctx: MethodContext<{ channelId: string }>) { return { fetch: () => ctx.client.channels.pins(ctx.channelId), @@ -528,15 +529,16 @@ export class ForumChannel extends BaseGuildChannel { export interface ThreadChannel extends ObjectToLower>, - TextBaseGuildChannel {} + Omit {} @mix(TextBaseGuildChannel) export class ThreadChannel extends BaseChannel< ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread > { + parentId!: string; declare type: ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread; webhooks = WebhookChannelMethods.channel({ client: this.client, - channelId: this.parentId!, + channelId: this.parentId, }); async join() { diff --git a/src/types/payloads/channel.ts b/src/types/payloads/channel.ts index 88fe332..404c544 100644 --- a/src/types/payloads/channel.ts +++ b/src/types/payloads/channel.ts @@ -2,6 +2,7 @@ * Types extracted from https://discord.com/developers/docs/resources/channel */ +import type { PickRequired } from '../../common'; import type { ChannelType, OverwriteType, Permissions, Snowflake, VideoQualityMode } from '../index'; import type { APIApplication } from './application'; import type { APIPartialEmoji } from './emoji'; @@ -209,7 +210,7 @@ export type ThreadChannelType = ChannelType.AnnouncementThread | ChannelType.Pri export interface APIThreadChannel extends Omit, 'name'>, - APIGuildChannel { + PickRequired, 'parent_id'> { /** * The client users member for the thread, only included in select endpoints */ diff --git a/src/websocket/discord/workermanager.ts b/src/websocket/discord/workermanager.ts index b4860f1..3a27c26 100644 --- a/src/websocket/discord/workermanager.ts +++ b/src/websocket/discord/workermanager.ts @@ -4,7 +4,7 @@ import type { Worker as WorkerThreadsWorker } from 'node:worker_threads'; import { ApiHandler, type CustomWorkerManagerEvents, Logger, type UsingClient, type WorkerClient } from '../..'; import { type Adapter, MemoryAdapter } from '../../cache'; import { BaseClient, type InternalRuntimeConfig } from '../../client/base'; -import { BASE_HOST, type Identify, type MakePartial, MergeOptions, lazyLoadPackage } from '../../common'; +import { BASE_HOST, type Identify, MergeOptions, type PickPartial, lazyLoadPackage } from '../../common'; import type { GatewayPresenceUpdateData, GatewaySendPayload, RESTGetAPIGatewayBotResult } from '../../types'; import { WorkerManagerDefaults, properties } from '../constants'; import { DynamicBucket } from '../structures'; @@ -46,7 +46,7 @@ export class WorkerManager extends Map< return chunks; } - options: MakePartial, 'adapter' | 'handleWorkerMessage' | 'handlePayload'>; + options: PickPartial, 'adapter' | 'handleWorkerMessage' | 'handlePayload'>; debugger?: Logger; connectQueue!: ConnectQueue; workerQueue: (() => void)[] = []; @@ -58,10 +58,10 @@ export class WorkerManager extends Map< constructor( options: Omit< - MakePartial, + PickPartial, 'resharding' > & { - resharding?: MakePartial, 'getInfo'>; + resharding?: PickPartial, 'getInfo'>; }, ) { super();