diff --git a/src/gateway/handlers/guildEmojiUpdate.ts b/src/gateway/handlers/guildEmojiUpdate.ts index db5e254..fddb657 100644 --- a/src/gateway/handlers/guildEmojiUpdate.ts +++ b/src/gateway/handlers/guildEmojiUpdate.ts @@ -17,7 +17,7 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( const _updated: EmojiPayload[] = [] for (const raw of d.emojis) { - const emojiID = raw.id !== null ? raw.id : raw.name + const emojiID = (raw.id !== null ? raw.id : raw.name) as string const has = emojis.get(emojiID) if (has === undefined) { await guild.emojis.set(emojiID, raw) @@ -27,7 +27,7 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( } for (const emoji of emojis.values()) { - const emojiID = emoji.id !== null ? emoji.id : emoji.name + const emojiID = (emoji.id !== null ? emoji.id : emoji.name) as string const find = _updated.find((e) => { const eID = e.id !== null ? e.id : e.name return emojiID === eID @@ -36,7 +36,7 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( await guild.emojis.delete(emojiID) deleted.push(emoji) } else { - const foundID = find.id !== null ? find.id : find.name + const foundID = (find.id !== null ? find.id : find.name) as string const before = (await guild.emojis.get(foundID)) as Emoji await guild.emojis.set(foundID, find) const after = (await guild.emojis.get(foundID)) as Emoji diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 8def9a1..6dacb68 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 Member 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/gateway/handlers/messageReactionAdd.ts b/src/gateway/handlers/messageReactionAdd.ts index 5b05ddd..edc88ae 100644 --- a/src/gateway/handlers/messageReactionAdd.ts +++ b/src/gateway/handlers/messageReactionAdd.ts @@ -29,7 +29,7 @@ export const messageReactionAdd: GatewayEventHandler = async ( } else return } - const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name + const emojiID = (d.emoji.id !== null ? d.emoji.id : d.emoji.name) as string let reaction = await message.reactions.get(emojiID) if (reaction === undefined) { await message.reactions.set(emojiID, { diff --git a/src/gateway/handlers/messageReactionRemove.ts b/src/gateway/handlers/messageReactionRemove.ts index 9cf50fa..77ca7df 100644 --- a/src/gateway/handlers/messageReactionRemove.ts +++ b/src/gateway/handlers/messageReactionRemove.ts @@ -27,7 +27,7 @@ export const messageReactionRemove: GatewayEventHandler = async ( } else return } - const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name + const emojiID = (d.emoji.id !== null ? d.emoji.id : d.emoji.name) as string const reaction = await message.reactions.get(emojiID) if (reaction === undefined) return diff --git a/src/gateway/handlers/messageReactionRemoveEmoji.ts b/src/gateway/handlers/messageReactionRemoveEmoji.ts index 0d1cd05..273ebc1 100644 --- a/src/gateway/handlers/messageReactionRemoveEmoji.ts +++ b/src/gateway/handlers/messageReactionRemoveEmoji.ts @@ -19,7 +19,7 @@ export const messageReactionRemoveEmoji: GatewayEventHandler = async ( } else return } - const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name + const emojiID = (d.emoji.id !== null ? d.emoji.id : d.emoji.name) as string const reaction = await message.reactions.get(emojiID) if (reaction === undefined) return diff --git a/src/managers/base.ts b/src/managers/base.ts index 5fef9d2..9d5bd7e 100644 --- a/src/managers/base.ts +++ b/src/managers/base.ts @@ -37,10 +37,15 @@ export class BaseManager { } /** Deletes a key from Cache */ - async delete(key: string): Promise { + async _delete(key: string): Promise { return this.client.cache.delete(this.cacheName, key) } + /** Alias to _delete (cache) for compatibility purposes */ + async delete(key: string): Promise { + return await this._delete(key) + } + /** Gets an Array of values from Cache */ async array(): Promise { let arr = await (this.client.cache.array(this.cacheName) as T[]) diff --git a/src/managers/guildChannels.ts b/src/managers/guildChannels.ts index a6854a8..3bdaed8 100644 --- a/src/managers/guildChannels.ts +++ b/src/managers/guildChannels.ts @@ -36,6 +36,7 @@ export class GuildChannelsManager extends BaseChildManager< else return undefined } + /** Delete a Guild Channel */ async delete(id: string): Promise { return this.client.rest.delete(CHANNEL(id)) } diff --git a/src/managers/guildEmojis.ts b/src/managers/guildEmojis.ts index dde2225..bab24c9 100644 --- a/src/managers/guildEmojis.ts +++ b/src/managers/guildEmojis.ts @@ -88,7 +88,7 @@ export class GuildEmojisManager extends BaseChildManager { const arr = await this.array() for (const elem of arr) { const emojiID = elem.id !== null ? elem.id : elem.name - this.parent.delete(emojiID) + this.parent.delete(emojiID as string) } return true } diff --git a/src/managers/invites.ts b/src/managers/invites.ts index 6af3896..e5baf27 100644 --- a/src/managers/invites.ts +++ b/src/managers/invites.ts @@ -1,7 +1,7 @@ import { Client } from '../models/client.ts' import { Guild } from '../structures/guild.ts' import { Invite } from '../structures/invite.ts' -import { GUILD_INVITES } from '../types/endpoint.ts' +import { INVITE } from '../types/endpoint.ts' import { InvitePayload } from '../types/invite.ts' import { BaseManager } from './base.ts' @@ -19,14 +19,15 @@ export class InviteManager extends BaseManager { return new Invite(this.client, raw) } - async fetch(id: string): Promise { + /** Fetch an Invite */ + async fetch(id: string): Promise { return await new Promise((resolve, reject) => { this.client.rest - .get(GUILD_INVITES(this.guild.id)) + .get(INVITE(id)) .then(async (data) => { this.set(id, data as InvitePayload) const newInvite = await this.get(data.code) - resolve(newInvite) + resolve(newInvite as Invite) }) .catch((e) => reject(e)) }) diff --git a/src/managers/memberRoles.ts b/src/managers/memberRoles.ts index bb715e0..a405340 100644 --- a/src/managers/memberRoles.ts +++ b/src/managers/memberRoles.ts @@ -60,7 +60,7 @@ export class MemberRolesManager extends BaseChildManager { true ) - return res.status === 204 + return res.response.status === 204 } async remove(role: string | Role): Promise { @@ -76,6 +76,6 @@ export class MemberRolesManager extends BaseChildManager { true ) - return res.status === 204 + return res.response.status === 204 } } diff --git a/src/managers/members.ts b/src/managers/members.ts index b06bb06..200e890 100644 --- a/src/managers/members.ts +++ b/src/managers/members.ts @@ -58,6 +58,7 @@ export class MembersManager extends BaseManager { ) } + /** Fetch a Guild Member */ async fetch(id: string): Promise { return await new Promise((resolve, reject) => { this.client.rest diff --git a/src/managers/messageReactions.ts b/src/managers/messageReactions.ts index 7c43ae6..000c5f9 100644 --- a/src/managers/messageReactions.ts +++ b/src/managers/messageReactions.ts @@ -4,6 +4,7 @@ import { Guild } from '../structures/guild.ts' import { Message } from '../structures/message.ts' import { MessageReaction } from '../structures/messageReaction.ts' import { Reaction } from '../types/channel.ts' +import { MESSAGE_REACTION, MESSAGE_REACTIONS } from '../types/endpoint.ts' import { BaseManager } from './base.ts' export class MessageReactionsManager extends BaseManager< @@ -23,7 +24,7 @@ export class MessageReactionsManager extends BaseManager< const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name - let emoji = await this.client.emojis.get(emojiID) + let emoji = await this.client.emojis.get(emojiID as string) if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) const reaction = new MessageReaction(this.client, raw, this.message, emoji) @@ -46,7 +47,7 @@ export class MessageReactionsManager extends BaseManager< return await Promise.all( arr.map(async (raw) => { const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name - let emoji = await this.client.emojis.get(emojiID) + let emoji = await this.client.emojis.get(emojiID as string) if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) return new MessageReaction(this.client, raw, this.message, emoji) @@ -58,4 +59,22 @@ export class MessageReactionsManager extends BaseManager< await this.client.cache.deleteCache(`reaction_users:${this.message.id}`) return this.client.cache.deleteCache(this.cacheName) } + + /** Remove all Reactions from the Message */ + async removeAll(): Promise { + await this.client.rest.delete( + MESSAGE_REACTIONS(this.message.channel.id, this.message.id) + ) + } + + /** Remove a specific Emoji from Reactions */ + async removeEmoji(emoji: Emoji | string): Promise { + const val = encodeURIComponent( + (typeof emoji === 'object' ? emoji.id ?? emoji.name : emoji) as string + ) + await this.client.rest.delete( + MESSAGE_REACTION(this.message.channel.id, this.message.id, val) + ) + return this + } } diff --git a/src/managers/roles.ts b/src/managers/roles.ts index 437c6c0..0ad93ef 100644 --- a/src/managers/roles.ts +++ b/src/managers/roles.ts @@ -1,10 +1,19 @@ +import { Permissions } from '../../mod.ts' import { Client } from '../models/client.ts' import { Guild } from '../structures/guild.ts' import { Role } from '../structures/role.ts' -import { GUILD_ROLE } from '../types/endpoint.ts' +import { GUILD_ROLE, GUILD_ROLES } from '../types/endpoint.ts' import { RolePayload } from '../types/role.ts' import { BaseManager } from './base.ts' +export interface CreateGuildRoleOptions { + name?: string + permissions?: number | string | Permissions + color?: number | string + hoist?: boolean + mentionable?: boolean +} + export class RolesManager extends BaseManager { guild: Guild @@ -13,13 +22,14 @@ export class RolesManager extends BaseManager { this.guild = guild } + /** Fetch a Guild Role (from API) */ async fetch(id: string): Promise { return await new Promise((resolve, reject) => { this.client.rest .get(GUILD_ROLE(this.guild.id, id)) - .then((data) => { - this.set(id, data as RolePayload) - resolve(new Role(this.client, data as RolePayload)) + .then(async (data) => { + await this.set(id, data as RolePayload) + resolve(((await this.get(id)) as unknown) as Role) }) .catch((e) => reject(e)) }) @@ -31,4 +41,43 @@ export class RolesManager extends BaseManager { } return true } + + /** Create a Guild Role */ + async create(data?: CreateGuildRoleOptions): Promise { + if (typeof data?.color === 'string') { + if (data.color.startsWith('#')) data.color = data.color.slice(1) + } + + const roleRaw = ((await this.client.rest.post(GUILD_ROLES(this.guild.id), { + name: data?.name, + permissions: + data?.permissions === undefined + ? undefined + : (typeof data.permissions === 'object' + ? data.permissions.bitfield + : data.permissions + ).toString(), + color: + data?.color === undefined + ? undefined + : typeof data.color === 'string' + ? isNaN(parseInt(data.color, 16)) + ? 0 + : parseInt(data.color, 16) + : data.color, + hoist: data?.hoist ?? false, + mentionable: data?.mentionable ?? false + })) as unknown) as RolePayload + + await this.set(roleRaw.id, roleRaw) + return ((await this.get(roleRaw.id)) as unknown) as Role + } + + /** Delete a Guild Role */ + async delete(role: Role | string): Promise { + await this.client.rest.delete( + GUILD_ROLE(this.guild.id, typeof role === 'object' ? role.id : role) + ) + return true + } } diff --git a/src/models/client.ts b/src/models/client.ts index ab197ce..ee00401 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,15 @@ 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 { Invite } from '../structures/invite.ts' +import { INVITE } from '../types/endpoint.ts' +import { ClientEvents } from '../gateway/handlers/index.ts' /** OS related properties sent with Gateway Identify */ export interface ClientProperties { @@ -53,6 +56,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 +122,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 +137,6 @@ export class Client extends EventEmitter { _decoratedSlashModules?: SlashModule[] _id?: string - public on = (event: K, listener: ClientEvents[K]): this => - this._untypedOn(event, listener) - - public emit = ( - event: K, - ...args: Parameters - ): boolean => this._untypedEmit(event, ...args) - - private readonly _untypedOn = this.on - - private readonly _untypedEmit = this.emit - /** Shard on which this Client is */ shard: number = 0 /** Shard Manager of this Client if Sharded */ @@ -211,6 +231,18 @@ export class Client extends EventEmitter { return new Application(this, app) } + /** Fetch an Invite */ + async fetchInvite(id: string): Promise { + return await new Promise((resolve, reject) => { + this.rest + .get(INVITE(id)) + .then((data) => { + resolve(new Invite(this, data)) + }) + .catch((e) => reject(e)) + }) + } + /** * This function is used for connecting to discord. * @param token Your token. This is required. @@ -233,17 +265,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/structures/emoji.ts b/src/structures/emoji.ts index 0aa47f6..072a3e5 100644 --- a/src/structures/emoji.ts +++ b/src/structures/emoji.ts @@ -1,12 +1,14 @@ import { Client } from '../models/client.ts' import { EmojiPayload } from '../types/emoji.ts' +import { EMOJI } from '../types/endpoint.ts' import { Base } from './base.ts' import { Guild } from './guild.ts' +import { Role } from './role.ts' import { User } from './user.ts' export class Emoji extends Base { id: string | null - name: string + name: string | null roles?: string[] user?: User guild?: Guild @@ -17,7 +19,7 @@ export class Emoji extends Base { get getEmojiString(): string { if (this.id === null) { - return this.name + return this.name as string } else { if (this.animated === false) { return `<:${this.name}:${this.id}>` @@ -41,6 +43,28 @@ export class Emoji extends Base { this.available = data.available } + /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. Returns the updated emoji object on success. Fires a Guild Emojis Update Gateway event. */ + async edit(data: ModifyGuildEmojiParams): Promise { + if (this.id === null) throw new Error('Emoji ID is not valid.') + if (this.guild === undefined) throw new Error('Guild is undefined') + const roles = Array.isArray(data.roles) + ? data.roles.map(role => (role instanceof Role ? role.id : role)) + : [data.roles instanceof Role ? data.roles.id : data.roles] + const res = await this.client.rest.patch(EMOJI(this.guild.id, this.id), { + ...data, + roles + }) + return new Emoji(this.client, res) + } + + /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns `true` on success. Fires a Guild Emojis Update Gateway event. */ + async delete(): Promise { + if (this.id === null) return false + if (this.guild === undefined) return false + await this.client.rest.delete(EMOJI(this.guild.id, this.id)) + return true + } + readFromData(data: EmojiPayload): void { this.id = data.id ?? this.id this.name = data.name ?? this.name @@ -52,3 +76,11 @@ export class Emoji extends Base { if (data.user !== undefined) this.user = new User(this.client, data.user) } } + +/** https://discord.com/developers/docs/resources/emoji#modify-guild-emoji-json-params */ +export interface ModifyGuildEmojiParams { + /** Name of the emoji */ + name?: string + /** Roles to which this emoji will be whitelisted */ + roles?: string | Role | Array; +} diff --git a/src/structures/role.ts b/src/structures/role.ts index 01ada1d..ed2e821 100644 --- a/src/structures/role.ts +++ b/src/structures/role.ts @@ -12,14 +12,7 @@ export class Role extends Base { permissions: Permissions managed: boolean mentionable: boolean - - get mention(): string { - return `<@&${this.id}>` - } - - toString(): string { - return this.mention - } + tags?: RoleTags constructor(client: Client, data: RolePayload) { super(client, data) @@ -31,6 +24,14 @@ export class Role extends Base { this.permissions = new Permissions(data.permissions) this.managed = data.managed this.mentionable = data.mentionable + this.tags = + data.tags !== undefined + ? { + botID: data.tags?.bot_id, + integrationID: data.tags?.integration_id, + premiumSubscriber: 'premium_subscriber' in (data.tags ?? {}) + } + : undefined } readFromData(data: RolePayload): void { @@ -46,3 +47,12 @@ export class Role extends Base { this.mentionable = data.mentionable ?? this.mentionable } } + +export interface RoleTags { + /** The id of the bot who has this role */ + botID?: string + /** Whether this is the premium subscriber role for this guild */ + premiumSubscriber: boolean + /** The id of the integration this role belongs to */ + integrationID?: string +} 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}`) } }) diff --git a/src/types/emoji.ts b/src/types/emoji.ts index 65d6f39..c50f238 100644 --- a/src/types/emoji.ts +++ b/src/types/emoji.ts @@ -2,7 +2,7 @@ import { UserPayload } from './user.ts' export interface EmojiPayload { id: string | null - name: string + name: string | null roles?: string[] user?: UserPayload require_colons?: boolean diff --git a/src/types/role.ts b/src/types/role.ts index 78f43d6..5ddfee8 100644 --- a/src/types/role.ts +++ b/src/types/role.ts @@ -7,4 +7,14 @@ export interface RolePayload { permissions: string managed: boolean mentionable: boolean + tags?: RoleTagsPayload +} + +export interface RoleTagsPayload { + /** The id of the bot who has this role */ + bot_id?: string + /** Whether this is the premium subscriber role for this guild */ + premium_subscriber?: null + /** The id of the integration this role belongs to */ + integration_id?: string }