diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index a85d8fc..8efa31e 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -99,238 +99,248 @@ export const gatewayHandlers: { INTERACTION_CREATE: interactionCreate } -export interface EventTypes { - [name: string]: (...args: any[]) => void -} - export interface VoiceServerUpdateData { token: string endpoint: string guild: Guild } -export interface ClientEvents extends EventTypes { +export interface ClientEvents { /** When Client has successfully connected to Discord */ - ready: () => void + ready: [] /** When a successful reconnect has been made */ - reconnect: () => void + reconnect: [] /** When a successful session resume has been done */ - resumed: () => void + resumed: [] /** * When a new Channel is created * @param channel New Channel object */ - channelCreate: (channel: EveryChannelTypes) => void + channelCreate: [channel: EveryChannelTypes] /** * When a Channel was deleted * @param channel Channel object which was deleted */ - channelDelete: (channel: EveryChannelTypes) => void + channelDelete: [channel: EveryChannelTypes] /** * Channel's Pinned Messages were updated * @param before Channel object before update * @param after Channel object after update */ - channelPinsUpdate: ( + channelPinsUpdate: [ before: EveryTextChannelTypes, after: EveryTextChannelTypes - ) => void + ] /** * A Channel was updated * @param before Channel object before update * @param after Channel object after update */ - channelUpdate: (before: EveryChannelTypes, after: EveryChannelTypes) => void + channelUpdate: [before: EveryChannelTypes, after: EveryChannelTypes] /** * A User was banned from a Guild * @param guild The Guild from which User was banned * @param user The User who was banned */ - guildBanAdd: (guild: Guild, user: User) => void + guildBanAdd: [guild: Guild, user: User] /** * A ban from a User in Guild was elevated * @param guild Guild from which ban was removed * @param user User of which ban was elevated */ - guildBanRemove: (guild: Guild, user: User) => void + guildBanRemove: [guild: Guild, user: User] /** * Client has joined a new Guild. * @param guild The new Guild object */ - guildCreate: (guild: Guild) => void + guildCreate: [guild: Guild] /** * A Guild in which Client was either deleted, or bot was kicked * @param guild The Guild object */ - guildDelete: (guild: Guild) => void + guildDelete: [guild: Guild] /** * A new Emoji was added to Guild * @param guild Guild in which Emoji was added * @param emoji The Emoji which was added */ - guildEmojiAdd: (guild: Guild, emoji: Emoji) => void + guildEmojiAdd: [guild: Guild, emoji: Emoji] /** * An Emoji was deleted from Guild * @param guild Guild from which Emoji was deleted * @param emoji Emoji which was deleted */ - guildEmojiDelete: (guild: Guild, emoji: Emoji) => void + guildEmojiDelete: [Guild, Emoji] /** * An Emoji in a Guild was updated * @param guild Guild in which Emoji was updated * @param before Emoji object before update * @param after Emoji object after update */ - guildEmojiUpdate: (guild: Guild, before: Emoji, after: Emoji) => void + guildEmojiUpdate: [guild: Guild, before: Emoji, after: Emoji] /** * Guild's Integrations were updated * @param guild The Guild object */ - guildIntegrationsUpdate: (guild: Guild) => void + guildIntegrationsUpdate: [guild: Guild] /** * A new Member has joined a Guild * @param member The Member object */ - guildMemberAdd: (member: Member) => void + guildMemberAdd: [member: Member] /** * A Guild Member has either left or was kicked from Guild * @param member The Member object */ - guildMemberRemove: (member: Member) => void + guildMemberRemove: [member: Member] /** * A Guild Member was updated. Nickname changed, role assigned, etc. * @param before Member object before update * @param after Meber object after update */ - guildMemberUpdate: (before: Member, after: Member) => void + guildMemberUpdate: [before: Member, after: Member] /** * A new Role was created in Guild * @param role The new Role object */ - guildRoleCreate: (role: Role) => void + guildRoleCreate: [role: Role] /** * A Role was deleted from the Guild * @param role The Role object */ - guildRoleDelete: (role: Role) => void + guildRoleDelete: [role: Role] /** * A Role was updated in a Guild * @param before Role object before update * @param after Role object after updated */ - guildRoleUpdate: (before: Role, after: Role) => void + guildRoleUpdate: [before: Role, after: Role] /** * A Guild has been updated. For example name, icon, etc. * @param before Guild object before update * @param after Guild object after update */ - guildUpdate: (before: Guild, after: Guild) => void + guildUpdate: [before: Guild, after: Guild] /** * A new Message was created (sent) * @param message The new Message object */ - messageCreate: (message: Message) => void + messageCreate: [message: Message] /** * A Message was deleted. * @param message The Message object */ - messageDelete: (message: Message) => void + messageDelete: [message: Message] /** * Messages were bulk deleted in a Guild Text Channel * @param channel Channel in which Messages were deleted * @param messages Collection of Messages deleted * @param uncached Set of Messages deleted's IDs which were not cached */ - messageDeleteBulk: ( + messageDeleteBulk: [ channel: GuildTextChannel, messages: Collection, uncached: Set - ) => void + ] /** * A Message was updated. For example content, embed, etc. * @param before Message object before update * @param after Message object after update */ - messageUpdate: (before: Message, after: Message) => void + messageUpdate: [before: Message, after: Message] /** * Reaction was added to a Message * @param reaction Reaction object * @param user User who added the reaction */ - messageReactionAdd: (reaction: MessageReaction, user: User) => void + messageReactionAdd: [reaction: MessageReaction, user: User] /** * Reaction was removed fro a Message * @param reaction Reaction object * @param user User to who removed the reaction */ - messageReactionRemove: (reaction: MessageReaction, user: User) => void + messageReactionRemove: [reaction: MessageReaction, user: User] /** * All reactions were removed from a Message * @param message Message from which reactions were removed */ - messageReactionRemoveAll: (message: Message) => void + messageReactionRemoveAll: [message: Message] /** * All reactions of a single Emoji were removed * @param message The Message object * @param emoji The Emoji object */ - messageReactionRemoveEmoji: (message: Message, emoji: Emoji) => void + messageReactionRemoveEmoji: [message: Message, emoji: Emoji] /** * A User has started typing in a Text Channel + * @param user User who started typing + * @param channel Channel which user started typing in + * @param at Date when user started typing + * @param guild Guild which user started typing in (can be undefined) */ - typingStart: ( + typingStart: [ user: User, channel: TextChannel, at: Date, - guildData?: TypingStartGuildData - ) => void + guild: TypingStartGuildData | undefined + ] /** * A new Invite was created * @param invite New Invite object */ - inviteCreate: (invite: Invite) => void + inviteCreate: [invite: Invite] /** * An Invite was deleted * @param invite Invite object */ - inviteDelete: (invite: Invite) => void + inviteDelete: [invite: Invite] /** * A User was updated. For example username, avatar, etc. * @param before The User object before update * @param after The User object after update */ - userUpdate: (before: User, after: User) => void + userUpdate: [before: User, after: User] /** * Client has received credentials for establishing connection to Voice Server + * @param data Updated voice server object */ - voiceServerUpdate: (data: VoiceServerUpdateData) => void + voiceServerUpdate: [data: VoiceServerUpdateData] /** * A User has joined a Voice Channel + * @param state Added voice state object */ - voiceStateAdd: (state: VoiceState) => void + voiceStateAdd: [state: VoiceState] /** * A User has left a Voice Channel + * @param state Removed voice state object */ - voiceStateRemove: (state: VoiceState) => void + voiceStateRemove: [state: VoiceState] /** * Voice State of a User has been updated * @param before Voice State object before update * @param after Voice State object after update */ - voiceStateUpdate: (state: VoiceState, after: VoiceState) => void + voiceStateUpdate: [before: VoiceState, after: VoiceState] /** * A User's presence has been updated * @param presence New Presence */ - presenceUpdate: (presence: Presence) => void + presenceUpdate: [presence: Presence] /** * Webhooks of a Channel in a Guild has been updated * @param guild Guild in which Webhooks were updated * @param channel Channel of which Webhooks were updated */ - webhooksUpdate: (guild: Guild, channel: GuildTextChannel) => void + webhooksUpdate: [guild: Guild, channel: GuildTextChannel] /** * An Interaction was created + * @param interaction Created interaction object */ - interactionCreate: (interaction: Interaction) => void + interactionCreate: [interaction: Interaction] + + /** + * When debug message was made + * @param message Debug message + */ + debug: [message: string] } diff --git a/src/models/client.ts b/src/models/client.ts index f0bcc73..e72854e 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/method-signature-style */ import { User } from '../structures/user.ts' import { GatewayIntents } from '../types/gateway.ts' import { Gateway } from '../gateway/index.ts' @@ -10,13 +11,13 @@ import { ChannelsManager } from '../managers/channels.ts' import { ClientPresence } from '../structures/presence.ts' import { EmojisManager } from '../managers/emojis.ts' import { ActivityGame, ClientActivity } from '../types/presence.ts' -import { ClientEvents } from '../gateway/handlers/index.ts' import { Extension } from './extensions.ts' import { SlashClient } from './slashClient.ts' import { Interaction } from '../structures/slash.ts' import { SlashModule } from './slashModule.ts' import type { ShardManager } from './shard.ts' import { Application } from '../structures/application.ts' +import { ClientEvents } from '../gateway/handlers/index.ts' /** OS related properties sent with Gateway Identify */ export interface ClientProperties { @@ -53,6 +54,32 @@ export interface ClientOptions { enableSlash?: boolean } +export declare interface Client { + on( + event: K, + listener: (...args: ClientEvents[K]) => void + ): this + on(event: string | symbol, listener: (...args: any[]) => void): this + + once( + event: K, + listener: (...args: ClientEvents[K]) => void + ): this + once(event: string | symbol, listener: (...args: any[]) => void): this + + emit( + event: K, + ...args: ClientEvents[K] + ): boolean + emit(event: string | symbol, ...args: any[]): boolean + + off( + event: K, + listener: (...args: ClientEvents[K]) => void + ): this + off(event: string | symbol, listener: (...args: any[]) => void): this +} + /** * Discord Client. */ @@ -93,7 +120,10 @@ export class Client extends EventEmitter { canary: boolean = false /** Client's presence. Startup one if set before connecting */ presence: ClientPresence = new ClientPresence() - _decoratedEvents?: { [name: string]: (...args: any[]) => any } + _decoratedEvents?: { + [name: string]: (...args: any[]) => void + } + _decoratedSlash?: Array<{ name: string guild?: string @@ -105,18 +135,6 @@ export class Client extends EventEmitter { _decoratedSlashModules?: SlashModule[] _id?: string - private readonly _untypedOn = this.on - - private readonly _untypedEmit = this.emit - - public on = (event: K, listener: ClientEvents[K]): this => - this._untypedOn(event, listener) - - public emit = ( - event: K, - ...args: Parameters - ): boolean => this._untypedEmit(event, ...args) - /** Shard on which this Client is */ shard: number = 0 /** Shard Manager of this Client if Sharded */ @@ -233,17 +251,43 @@ export class Client extends EventEmitter { } else throw new Error('No Gateway Intents were provided') this.gateway = new Gateway(this, token, intents) } + + async waitFor( + event: K, + checkFunction: (...args: ClientEvents[K]) => boolean, + timeout?: number + ): Promise { + return await new Promise((resolve) => { + let timeoutID: number | undefined + if (timeout !== undefined) { + timeoutID = setTimeout(() => { + this.off(event, eventFunc) + resolve([]) + }, timeout) + } + const eventFunc = (...args: ClientEvents[K]): void => { + if (checkFunction(...args)) { + resolve(args) + this.off(event, eventFunc) + if (timeoutID !== undefined) clearTimeout(timeoutID) + } + } + this.on(event, eventFunc) + }) + } } -export function event(name?: string) { - return function (client: Client | Extension, prop: string) { +export function event(name?: keyof ClientEvents) { + return function (client: Client | Extension, prop: keyof ClientEvents) { const listener = ((client as unknown) as { - [name: string]: (...args: any[]) => any + [name in keyof ClientEvents]: (...args: ClientEvents[name]) => any })[prop] if (typeof listener !== 'function') throw new Error('@event decorator requires a function') if (client._decoratedEvents === undefined) client._decoratedEvents = {} - client._decoratedEvents[name === undefined ? prop : name] = listener + const key = name === undefined ? prop : name + + client._decoratedEvents[key] = listener } } diff --git a/src/test/index.ts b/src/test/index.ts index c0d14c3..72a4fcf 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -114,6 +114,14 @@ client.on('messageCreate', async (msg: Message) => { } } else if (msg.content === '!react') { msg.addReaction('🤔') + } else if (msg.content === '!wait_for') { + msg.channel.send('Send anything!') + const [receivedMsg] = await client.waitFor( + 'messageCreate', + (message) => message.author.id === msg.author.id + ) + + msg.channel.send(`Received: ${receivedMsg?.content}`) } })