From a7336c19e32d48e563d79fb546b2d148e767bdf9 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 2 Dec 2020 13:32:00 +0530 Subject: [PATCH] feat(commands): add CommandCategory and fix messageUpdate --- src/consts/urlsAndVersions.ts | 2 +- src/gateway/handlers/channelDelete.ts | 26 +- src/gateway/handlers/channelPinsUpdate.ts | 6 +- src/gateway/handlers/guildBanAdd.ts | 36 +- src/gateway/handlers/guildBanRemove.ts | 35 +- src/gateway/handlers/guildCreate.ts | 62 +- src/gateway/handlers/guildDelete.ts | 42 +- src/gateway/handlers/guildEmojiUpdate.ts | 14 +- .../handlers/guildIntegrationsUpdate.ts | 4 +- src/gateway/handlers/guildMemberAdd.ts | 8 +- src/gateway/handlers/guildMemberUpdate.ts | 2 +- src/gateway/handlers/guildRoleDelete.ts | 36 +- src/gateway/handlers/guildUpdate.ts | 28 +- src/gateway/handlers/index.ts | 131 +++-- src/gateway/handlers/messageDelete.ts | 39 +- src/gateway/handlers/messageDeleteBulk.ts | 14 +- src/gateway/handlers/messageUpdate.ts | 25 +- src/gateway/handlers/ready.ts | 13 +- src/gateway/handlers/reconnect.ts | 13 +- src/gateway/handlers/voiceServerUpdate.ts | 6 +- src/gateway/handlers/voiceStateUpdate.ts | 34 +- src/gateway/handlers/webhooksUpdate.ts | 13 +- src/managers/guildVoiceStates.ts | 60 +- src/managers/messages.ts | 149 ++--- src/models/command.ts | 270 ++++++++- src/models/commandClient.ts | 530 +++++++++++------- src/structures/messageMentions.ts | 118 ++-- src/test/cmd.ts | 36 +- 28 files changed, 1097 insertions(+), 655 deletions(-) diff --git a/src/consts/urlsAndVersions.ts b/src/consts/urlsAndVersions.ts index 257c937..e368d32 100644 --- a/src/consts/urlsAndVersions.ts +++ b/src/consts/urlsAndVersions.ts @@ -6,4 +6,4 @@ export const DISCORD_CDN_URL: string = 'https://cdn.discordapp.com' export const DISCORD_API_VERSION: number = 8 -export const DISCORD_VOICE_VERSION: number = 4 \ No newline at end of file +export const DISCORD_VOICE_VERSION: number = 4 diff --git a/src/gateway/handlers/channelDelete.ts b/src/gateway/handlers/channelDelete.ts index 37cd2b4..f17beb8 100644 --- a/src/gateway/handlers/channelDelete.ts +++ b/src/gateway/handlers/channelDelete.ts @@ -1,13 +1,13 @@ -import { Gateway, GatewayEventHandler } from '../index.ts' -import { ChannelPayload } from '../../types/channel.ts' - -export const channelDelete: GatewayEventHandler = async ( - gateway: Gateway, - d: ChannelPayload -) => { - const channel = await gateway.client.channels.get(d.id) - if (channel !== undefined) { - await gateway.client.channels.delete(d.id) - gateway.client.emit('channelDelete', channel) - } -} +import { Gateway, GatewayEventHandler } from '../index.ts' +import { ChannelPayload } from '../../types/channel.ts' + +export const channelDelete: GatewayEventHandler = async ( + gateway: Gateway, + d: ChannelPayload +) => { + const channel = await gateway.client.channels.get(d.id) + if (channel !== undefined) { + await gateway.client.channels.delete(d.id) + gateway.client.emit('channelDelete', channel) + } +} diff --git a/src/gateway/handlers/channelPinsUpdate.ts b/src/gateway/handlers/channelPinsUpdate.ts index a05dd11..0394a08 100644 --- a/src/gateway/handlers/channelPinsUpdate.ts +++ b/src/gateway/handlers/channelPinsUpdate.ts @@ -6,10 +6,12 @@ export const channelPinsUpdate: GatewayEventHandler = async ( gateway: Gateway, d: ChannelPinsUpdatePayload ) => { - const after: TextChannel | undefined = await gateway.client.channels.get(d.channel_id) + const after: + | TextChannel + | undefined = await gateway.client.channels.get(d.channel_id) if (after !== undefined) { const before = after.refreshFromData({ - last_pin_timestamp: d.last_pin_timestamp + last_pin_timestamp: d.last_pin_timestamp, }) const raw = await gateway.client.channels._get(d.channel_id) await gateway.client.channels.set( diff --git a/src/gateway/handlers/guildBanAdd.ts b/src/gateway/handlers/guildBanAdd.ts index 50a7360..17901e9 100644 --- a/src/gateway/handlers/guildBanAdd.ts +++ b/src/gateway/handlers/guildBanAdd.ts @@ -1,17 +1,19 @@ -import { Gateway, GatewayEventHandler } from '../index.ts' -import { Guild } from '../../structures/guild.ts' -import { User } from '../../structures/user.ts' -import { GuildBanAddPayload } from '../../types/gateway.ts' - -export const guildBanAdd: GatewayEventHandler = async ( - gateway: Gateway, - d: GuildBanAddPayload -) => { - const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) - const user: User = await gateway.client.users.get(d.user.id) ?? new User(gateway.client, d.user) - - if (guild !== undefined) { - // We don't have to delete member, already done with guildMemberRemove event - gateway.client.emit('guildBanAdd', guild, user) - } -} +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' +import { GuildBanAddPayload } from '../../types/gateway.ts' + +export const guildBanAdd: GatewayEventHandler = async ( + gateway: Gateway, + d: GuildBanAddPayload +) => { + const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) + const user: User = + (await gateway.client.users.get(d.user.id)) ?? + new User(gateway.client, d.user) + + if (guild !== undefined) { + // We don't have to delete member, already done with guildMemberRemove event + gateway.client.emit('guildBanAdd', guild, user) + } +} diff --git a/src/gateway/handlers/guildBanRemove.ts b/src/gateway/handlers/guildBanRemove.ts index d95a6d9..1b824fd 100644 --- a/src/gateway/handlers/guildBanRemove.ts +++ b/src/gateway/handlers/guildBanRemove.ts @@ -1,17 +1,18 @@ -import { Gateway, GatewayEventHandler } from '../index.ts' -import { Guild } from '../../structures/guild.ts' -import { User } from '../../structures/user.ts' -import { GuildBanRemovePayload } from '../../types/gateway.ts' - -export const guildBanRemove: GatewayEventHandler = async ( - gateway: Gateway, - d: GuildBanRemovePayload -) => { - const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) - const user: User = - await gateway.client.users.get(d.user.id) ?? new User(gateway.client, d.user) - - if (guild !== undefined) { - gateway.client.emit('guildBanRemove', guild, user) - } -} \ No newline at end of file +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' +import { GuildBanRemovePayload } from '../../types/gateway.ts' + +export const guildBanRemove: GatewayEventHandler = async ( + gateway: Gateway, + d: GuildBanRemovePayload +) => { + const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) + const user: User = + (await gateway.client.users.get(d.user.id)) ?? + new User(gateway.client, d.user) + + if (guild !== undefined) { + gateway.client.emit('guildBanRemove', guild, user) + } +} diff --git a/src/gateway/handlers/guildCreate.ts b/src/gateway/handlers/guildCreate.ts index dfcb532..a40d0ac 100644 --- a/src/gateway/handlers/guildCreate.ts +++ b/src/gateway/handlers/guildCreate.ts @@ -1,64 +1,32 @@ import { Gateway, GatewayEventHandler } from '../index.ts' import { Guild } from '../../structures/guild.ts' import { GuildPayload } from '../../types/guild.ts' -import { MembersManager } from '../../managers/members.ts' import { GuildChannelPayload } from '../../types/channel.ts' -import { RolesManager } from '../../managers/roles.ts' export const guildCreate: GatewayEventHandler = async ( gateway: Gateway, d: GuildPayload ) => { - let guild: Guild | undefined = await gateway.client.guilds.get(d.id) - if (guild !== undefined) { - // It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in - await gateway.client.guilds.set(d.id, d) + const hasGuild: Guild | undefined = await gateway.client.guilds.get(d.id) + await gateway.client.guilds.set(d.id, d) + const guild = ((await gateway.client.guilds.get(d.id)) as unknown) as Guild - if (d.members !== undefined) { - const members = new MembersManager(gateway.client, guild) - await members.fromPayload(d.members) - guild.members = members + if (d.members !== undefined) await guild.members.fromPayload(d.members) + + if (d.channels !== undefined) { + for (const ch of d.channels as GuildChannelPayload[]) { + ch.guild_id = d.id + await gateway.client.channels.set(ch.id, ch) } + } - if (d.channels !== undefined) { - for (const ch of d.channels as GuildChannelPayload[]) { - ch.guild_id = d.id - await gateway.client.channels.set(ch.id, ch) - } - } + await guild.roles.fromPayload(d.roles) - if (d.roles !== undefined) { - const roles = new RolesManager(gateway.client, guild) - await roles.fromPayload(d.roles) - guild.roles = roles - } + if (d.voice_states !== undefined) + await guild.voiceStates.fromPayload(d.voice_states) - guild.refreshFromData(d) - } else { - await gateway.client.guilds.set(d.id, d) - guild = new Guild(gateway.client, d) - - if (d.members !== undefined) { - const members = new MembersManager(gateway.client, guild) - await members.fromPayload(d.members) - guild.members = members - } - - if (d.channels !== undefined) { - for (const ch of d.channels as GuildChannelPayload[]) { - ch.guild_id = d.id - await gateway.client.channels.set(ch.id, ch) - } - } - - if (d.roles !== undefined) { - const roles = new RolesManager(gateway.client, guild) - await roles.fromPayload(d.roles) - guild.roles = roles - } - - await guild.roles.fromPayload(d.roles) - guild = new Guild(gateway.client, d) + if (hasGuild === undefined) { + // It wasn't lazy load, so emit event gateway.client.emit('guildCreate', guild) } } diff --git a/src/gateway/handlers/guildDelete.ts b/src/gateway/handlers/guildDelete.ts index 2270e38..369181d 100644 --- a/src/gateway/handlers/guildDelete.ts +++ b/src/gateway/handlers/guildDelete.ts @@ -1,21 +1,21 @@ -import { Guild } from '../../structures/guild.ts' -import { GuildPayload } from '../../types/guild.ts' -import { Gateway, GatewayEventHandler } from '../index.ts' - -export const guildDelte: GatewayEventHandler = async ( - gateway: Gateway, - d: GuildPayload -) => { - const guild: Guild | undefined = await gateway.client.guilds.get(d.id) - - if (guild !== undefined) { - guild.refreshFromData(d) - - await guild.members.flush() - await guild.channels.flush() - await guild.roles.flush() - await gateway.client.guilds.delete(d.id) - - gateway.client.emit('guildDelete', guild) - } -} +import { Guild } from '../../structures/guild.ts' +import { GuildPayload } from '../../types/guild.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const guildDelte: GatewayEventHandler = async ( + gateway: Gateway, + d: GuildPayload +) => { + const guild: Guild | undefined = await gateway.client.guilds.get(d.id) + + if (guild !== undefined) { + guild.refreshFromData(d) + + await guild.members.flush() + await guild.channels.flush() + await guild.roles.flush() + await gateway.client.guilds.delete(d.id) + + gateway.client.emit('guildDelete', guild) + } +} diff --git a/src/gateway/handlers/guildEmojiUpdate.ts b/src/gateway/handlers/guildEmojiUpdate.ts index 53b6502..1e9eba0 100644 --- a/src/gateway/handlers/guildEmojiUpdate.ts +++ b/src/gateway/handlers/guildEmojiUpdate.ts @@ -1,6 +1,6 @@ -import { Emoji } from "../../structures/emoji.ts" +import { Emoji } from '../../structures/emoji.ts' import { Guild } from '../../structures/guild.ts' -import { EmojiPayload } from "../../types/emoji.ts" +import { EmojiPayload } from '../../types/emoji.ts' import { GuildEmojiUpdatePayload } from '../../types/gateway.ts' import { Gateway, GatewayEventHandler } from '../index.ts' @@ -13,27 +13,27 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( const emojis = await guild.emojis.collection() const deleted: Emoji[] = [] const added: Emoji[] = [] - const updated: Array<{ before: Emoji, after: Emoji }> = [] + const updated: Array<{ before: Emoji; after: Emoji }> = [] const _updated: EmojiPayload[] = [] for (const raw of d.emojis) { const has = emojis.get(raw.id) if (has === undefined) { await guild.emojis.set(raw.id, raw) - const emoji = await guild.emojis.get(raw.id) as Emoji + const emoji = (await guild.emojis.get(raw.id)) as Emoji added.push(emoji) } else _updated.push(raw) } for (const emoji of emojis.values()) { - const find = _updated.find(e => emoji.id === e.id) + const find = _updated.find((e) => emoji.id === e.id) if (find === undefined) { await guild.emojis.delete(emoji.id) deleted.push(emoji) } else { - const before = await guild.emojis.get(find.id) as Emoji + const before = (await guild.emojis.get(find.id)) as Emoji await guild.emojis.set(find.id, find) - const after = await guild.emojis.get(find.id) as Emoji + const after = (await guild.emojis.get(find.id)) as Emoji updated.push({ before, after }) } } diff --git a/src/gateway/handlers/guildIntegrationsUpdate.ts b/src/gateway/handlers/guildIntegrationsUpdate.ts index a115efc..13b7e37 100644 --- a/src/gateway/handlers/guildIntegrationsUpdate.ts +++ b/src/gateway/handlers/guildIntegrationsUpdate.ts @@ -1,6 +1,6 @@ import { Gateway, GatewayEventHandler } from '../index.ts' import { Guild } from '../../structures/guild.ts' -import { GuildIntegrationsUpdatePayload } from "../../types/gateway.ts" +import { GuildIntegrationsUpdatePayload } from '../../types/gateway.ts' export const guildIntegrationsUpdate: GatewayEventHandler = async ( gateway: Gateway, @@ -11,4 +11,4 @@ export const guildIntegrationsUpdate: GatewayEventHandler = async ( if (guild === undefined) return gateway.client.emit('guildIntegrationsUpdate', guild) -} \ No newline at end of file +} diff --git a/src/gateway/handlers/guildMemberAdd.ts b/src/gateway/handlers/guildMemberAdd.ts index 9407ace..c14bbf0 100644 --- a/src/gateway/handlers/guildMemberAdd.ts +++ b/src/gateway/handlers/guildMemberAdd.ts @@ -1,7 +1,7 @@ import { Gateway, GatewayEventHandler } from '../index.ts' import { Guild } from '../../structures/guild.ts' -import { GuildMemberAddPayload } from "../../types/gateway.ts" -import { Member } from "../../structures/member.ts" +import { GuildMemberAddPayload } from '../../types/gateway.ts' +import { Member } from '../../structures/member.ts' export const guildMemberAdd: GatewayEventHandler = async ( gateway: Gateway, @@ -12,6 +12,6 @@ export const guildMemberAdd: GatewayEventHandler = async ( if (guild === undefined) return await guild.members.set(d.user.id, d) - const member = await guild.members.get(d.user.id) as Member + const member = (await guild.members.get(d.user.id)) as Member gateway.client.emit('guildMemberAdd', member) -} \ No newline at end of file +} diff --git a/src/gateway/handlers/guildMemberUpdate.ts b/src/gateway/handlers/guildMemberUpdate.ts index 6267c0c..29843ac 100644 --- a/src/gateway/handlers/guildMemberUpdate.ts +++ b/src/gateway/handlers/guildMemberUpdate.ts @@ -20,7 +20,7 @@ export const guildMemberUpdate: GatewayEventHandler = async ( nick: d.nick, premium_since: d.premium_since, deaf: member?.deaf ?? false, - mute: member?.mute ?? false + mute: member?.mute ?? false, } await guild.members.set(d.user.id, newMemberPayload) const newMember = await guild.members.get(d.user.id) diff --git a/src/gateway/handlers/guildRoleDelete.ts b/src/gateway/handlers/guildRoleDelete.ts index 5105135..711833d 100644 --- a/src/gateway/handlers/guildRoleDelete.ts +++ b/src/gateway/handlers/guildRoleDelete.ts @@ -1,18 +1,18 @@ -import { Gateway, GatewayEventHandler } from '../index.ts' -import { Guild } from '../../structures/guild.ts' -import { GuildRoleDeletePayload } from "../../types/gateway.ts" - -export const guildRoleDelete: GatewayEventHandler = async ( - gateway: Gateway, - d: GuildRoleDeletePayload -) => { - const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) - // Weird case, shouldn't happen - if (guild === undefined) return - - const role = await guild.roles.get(d.role_id) - // Shouldn't happen either - if(role === undefined) return - - gateway.client.emit('guildRoleDelete', role) -} \ No newline at end of file +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { GuildRoleDeletePayload } from '../../types/gateway.ts' + +export const guildRoleDelete: GatewayEventHandler = async ( + gateway: Gateway, + d: GuildRoleDeletePayload +) => { + const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) + // Weird case, shouldn't happen + if (guild === undefined) return + + const role = await guild.roles.get(d.role_id) + // Shouldn't happen either + if (role === undefined) return + + gateway.client.emit('guildRoleDelete', role) +} diff --git a/src/gateway/handlers/guildUpdate.ts b/src/gateway/handlers/guildUpdate.ts index c7e7cc4..3bb2391 100644 --- a/src/gateway/handlers/guildUpdate.ts +++ b/src/gateway/handlers/guildUpdate.ts @@ -1,14 +1,14 @@ -import { Gateway, GatewayEventHandler } from '../index.ts' -import { Guild } from '../../structures/guild.ts' -import { GuildPayload } from '../../types/guild.ts' - -export const guildUpdate: GatewayEventHandler = async ( - gateway: Gateway, - d: GuildPayload -) => { - const after: Guild | undefined = await gateway.client.guilds.get(d.id) - if (after === undefined) return - const before = after.refreshFromData(d) - await gateway.client.guilds.set(d.id, d) - gateway.client.emit('guildUpdate', before, after) -} +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { GuildPayload } from '../../types/guild.ts' + +export const guildUpdate: GatewayEventHandler = async ( + gateway: Gateway, + d: GuildPayload +) => { + const after: Guild | undefined = await gateway.client.guilds.get(d.id) + if (after === undefined) return + const before = after.refreshFromData(d) + await gateway.client.guilds.set(d.id, d) + gateway.client.emit('guildUpdate', before, after) +} diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 9974fff..9bfecf6 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -13,32 +13,32 @@ import { guildBanRemove } from './guildBanRemove.ts' import { messageCreate } from './messageCreate.ts' import { resume } from './resume.ts' import { reconnect } from './reconnect.ts' -import { messageDelete } from "./messageDelete.ts" -import { messageUpdate } from "./messageUpdate.ts" -import { guildEmojiUpdate } from "./guildEmojiUpdate.ts" -import { guildMemberAdd } from "./guildMemberAdd.ts" -import { guildMemberRemove } from "./guildMemberRemove.ts" -import { guildMemberUpdate } from "./guildMemberUpdate.ts" -import { guildRoleCreate } from "./guildRoleCreate.ts" -import { guildRoleDelete } from "./guildRoleDelete.ts" -import { guildRoleUpdate } from "./guildRoleUpdate.ts" -import { guildIntegrationsUpdate } from "./guildIntegrationsUpdate.ts" -import { webhooksUpdate } from "./webhooksUpdate.ts" -import { messageDeleteBulk } from "./messageDeleteBulk.ts" -import { userUpdate } from "./userUpdate.ts" -import { typingStart } from "./typingStart.ts" -import { Channel } from "../../structures/channel.ts" -import { GuildTextChannel, TextChannel } from "../../structures/textChannel.ts" -import { Guild } from "../../structures/guild.ts" -import { User } from "../../structures/user.ts" -import { Emoji } from "../../structures/emoji.ts" -import { Member } from "../../structures/member.ts" -import { Role } from "../../structures/role.ts" -import { Message } from "../../structures/message.ts" -import { Collection } from "../../utils/collection.ts" -import { voiceServerUpdate } from "./voiceServerUpdate.ts" -import { voiceStateUpdate } from "./voiceStateUpdate.ts" -import { VoiceState } from "../../structures/voiceState.ts" +import { messageDelete } from './messageDelete.ts' +import { messageUpdate } from './messageUpdate.ts' +import { guildEmojiUpdate } from './guildEmojiUpdate.ts' +import { guildMemberAdd } from './guildMemberAdd.ts' +import { guildMemberRemove } from './guildMemberRemove.ts' +import { guildMemberUpdate } from './guildMemberUpdate.ts' +import { guildRoleCreate } from './guildRoleCreate.ts' +import { guildRoleDelete } from './guildRoleDelete.ts' +import { guildRoleUpdate } from './guildRoleUpdate.ts' +import { guildIntegrationsUpdate } from './guildIntegrationsUpdate.ts' +import { webhooksUpdate } from './webhooksUpdate.ts' +import { messageDeleteBulk } from './messageDeleteBulk.ts' +import { userUpdate } from './userUpdate.ts' +import { typingStart } from './typingStart.ts' +import { Channel } from '../../structures/channel.ts' +import { GuildTextChannel, TextChannel } from '../../structures/textChannel.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' +import { Emoji } from '../../structures/emoji.ts' +import { Member } from '../../structures/member.ts' +import { Role } from '../../structures/role.ts' +import { Message } from '../../structures/message.ts' +import { Collection } from '../../utils/collection.ts' +import { voiceServerUpdate } from './voiceServerUpdate.ts' +import { voiceStateUpdate } from './voiceStateUpdate.ts' +import { VoiceState } from '../../structures/voiceState.ts' export const gatewayHandlers: { [eventCode in GatewayEvents]: GatewayEventHandler | undefined @@ -79,7 +79,7 @@ export const gatewayHandlers: { USER_UPDATE: userUpdate, VOICE_STATE_UPDATE: voiceStateUpdate, VOICE_SERVER_UPDATE: voiceServerUpdate, - WEBHOOKS_UPDATE: webhooksUpdate + WEBHOOKS_UPDATE: webhooksUpdate, } export interface EventTypes { @@ -93,37 +93,46 @@ export interface VoiceServerUpdateData { } export interface ClientEvents extends EventTypes { - 'ready': () => void - 'reconnect': () => void - 'resumed': () => void - 'channelCreate': (channel: Channel) => void - 'channelDelete': (channel: Channel) => void - 'channelPinsUpdate': (before: TextChannel, after: TextChannel) => void - 'channelUpdate': (before: Channel, after: Channel) => void - 'guildBanAdd': (guild: Guild, user: User) => void - 'guildBanRemove': (guild: Guild, user: User) => void - 'guildCreate': (guild: Guild) => void - 'guildDelete': (guild: Guild) => void - 'guildEmojiAdd': (guild: Guild, emoji: Emoji) => void - 'guildEmojiDelete': (guild: Guild, emoji: Emoji) => void - 'guildEmojiUpdate': (guild: Guild, before: Emoji, after: Emoji) => void - 'guildIntegrationsUpdate': (guild: Guild) => void - 'guildMemberAdd': (member: Member) => void - 'guildMemberRemove': (member: Member) => void - 'guildMemberUpdate': (before: Member, after: Member) => void - 'guildRoleCreate': (role: Role) => void - 'guildRoleDelete': (role: Role) => void - 'guildRoleUpdate': (before: Role, after: Role) => void - 'guildUpdate': (before: Guild, after: Guild) => void - 'messageCreate': (message: Message) => void - 'messageDelete': (message: Message) => void - 'messageDeleteBulk': (channel: GuildTextChannel, messages: Collection, uncached: Set) => void - 'messageUpdate': (before: Message, after: Message) => void - 'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void - 'userUpdate': (before: User, after: User) => void - 'voiceServerUpdate': (data: VoiceServerUpdateData) => void - 'voiceStateAdd': (state: VoiceState) => void - 'voiceStateRemove': (state: VoiceState) => void - 'voiceStateUpdate': (state: VoiceState, after: VoiceState) => void - 'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void -} \ No newline at end of file + ready: () => void + reconnect: () => void + resumed: () => void + channelCreate: (channel: Channel) => void + channelDelete: (channel: Channel) => void + channelPinsUpdate: (before: TextChannel, after: TextChannel) => void + channelUpdate: (before: Channel, after: Channel) => void + guildBanAdd: (guild: Guild, user: User) => void + guildBanRemove: (guild: Guild, user: User) => void + guildCreate: (guild: Guild) => void + guildDelete: (guild: Guild) => void + guildEmojiAdd: (guild: Guild, emoji: Emoji) => void + guildEmojiDelete: (guild: Guild, emoji: Emoji) => void + guildEmojiUpdate: (guild: Guild, before: Emoji, after: Emoji) => void + guildIntegrationsUpdate: (guild: Guild) => void + guildMemberAdd: (member: Member) => void + guildMemberRemove: (member: Member) => void + guildMemberUpdate: (before: Member, after: Member) => void + guildRoleCreate: (role: Role) => void + guildRoleDelete: (role: Role) => void + guildRoleUpdate: (before: Role, after: Role) => void + guildUpdate: (before: Guild, after: Guild) => void + messageCreate: (message: Message) => void + messageDelete: (message: Message) => void + messageDeleteBulk: ( + channel: GuildTextChannel, + messages: Collection, + uncached: Set + ) => void + messageUpdate: (before: Message, after: Message) => void + typingStart: ( + user: User, + channel: TextChannel, + at: Date, + guildData?: TypingStartGuildData + ) => void + userUpdate: (before: User, after: User) => void + voiceServerUpdate: (data: VoiceServerUpdateData) => void + voiceStateAdd: (state: VoiceState) => void + voiceStateRemove: (state: VoiceState) => void + voiceStateUpdate: (state: VoiceState, after: VoiceState) => void + webhooksUpdate: (guild: Guild, channel: GuildTextChannel) => void +} diff --git a/src/gateway/handlers/messageDelete.ts b/src/gateway/handlers/messageDelete.ts index ccad9b1..74fcf66 100644 --- a/src/gateway/handlers/messageDelete.ts +++ b/src/gateway/handlers/messageDelete.ts @@ -1,19 +1,20 @@ -import { TextChannel } from '../../structures/textChannel.ts' -import { MessageDeletePayload } from "../../types/gateway.ts" -import { Gateway, GatewayEventHandler } from '../index.ts' - -export const messageDelete: GatewayEventHandler = async ( - gateway: Gateway, - d: MessageDeletePayload -) => { - let channel = await gateway.client.channels.get(d.channel_id) - // Fetch the channel if not cached - if (channel === undefined) - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel - - const message = await channel.messages.get(d.id) - if (message === undefined) return gateway.client.emit('messageDeleteUncached', d) - await channel.messages.delete(d.id) - gateway.client.emit('messageDelete', message) -} +import { TextChannel } from '../../structures/textChannel.ts' +import { MessageDeletePayload } from '../../types/gateway.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const messageDelete: GatewayEventHandler = async ( + gateway: Gateway, + d: MessageDeletePayload +) => { + let channel = await gateway.client.channels.get(d.channel_id) + // Fetch the channel if not cached + if (channel === undefined) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel + + const message = await channel.messages.get(d.id) + if (message === undefined) + return gateway.client.emit('messageDeleteUncached', d) + await channel.messages.delete(d.id) + gateway.client.emit('messageDelete', message) +} diff --git a/src/gateway/handlers/messageDeleteBulk.ts b/src/gateway/handlers/messageDeleteBulk.ts index 8f15718..a98fdfc 100644 --- a/src/gateway/handlers/messageDeleteBulk.ts +++ b/src/gateway/handlers/messageDeleteBulk.ts @@ -1,18 +1,22 @@ -import { Message } from "../../structures/message.ts" +import { Message } from '../../structures/message.ts' import { GuildTextChannel } from '../../structures/textChannel.ts' -import { MessageDeleteBulkPayload } from "../../types/gateway.ts" -import { Collection } from "../../utils/collection.ts" +import { MessageDeleteBulkPayload } from '../../types/gateway.ts' +import { Collection } from '../../utils/collection.ts' import { Gateway, GatewayEventHandler } from '../index.ts' export const messageDeleteBulk: GatewayEventHandler = async ( gateway: Gateway, d: MessageDeleteBulkPayload ) => { - let channel = await gateway.client.channels.get(d.channel_id) + let channel = await gateway.client.channels.get( + d.channel_id + ) // Fetch the channel if not cached if (channel === undefined) // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - channel = (await gateway.client.channels.fetch(d.channel_id)) as GuildTextChannel + channel = (await gateway.client.channels.fetch( + d.channel_id + )) as GuildTextChannel const messages = new Collection() const uncached = new Set() diff --git a/src/gateway/handlers/messageUpdate.ts b/src/gateway/handlers/messageUpdate.ts index b23b0fc..cbe2d88 100644 --- a/src/gateway/handlers/messageUpdate.ts +++ b/src/gateway/handlers/messageUpdate.ts @@ -1,6 +1,5 @@ import { Message } from '../../structures/message.ts' import { TextChannel } from '../../structures/textChannel.ts' -import { User } from '../../structures/user.ts' import { MessagePayload } from '../../types/channel.ts' import { Gateway, GatewayEventHandler } from '../index.ts' @@ -11,19 +10,19 @@ export const messageUpdate: GatewayEventHandler = async ( let channel = await gateway.client.channels.get(d.channel_id) // Fetch the channel if not cached if (channel === undefined) - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel + channel = await gateway.client.channels.fetch(d.channel_id) + + if (channel === undefined) return const message = await channel.messages.get(d.id) - const author = - message?.author !== undefined - ? message.author - : new User(gateway.client, d.author) - const newMsg = new Message(gateway.client, d, channel, author) - if (message === undefined) { - await channel.messages.set(d.id, d) - return gateway.client.emit('messageUpdateUncached', newMsg) - } - await channel.messages.set(d.id, d) + if (message === undefined) return + const raw = await channel.messages._get(d.id) + if (raw === undefined) return + + const newRaw = raw + Object.assign(newRaw, d) + await channel.messages.set(d.id, newRaw) + + const newMsg = ((await channel.messages.get(d.id)) as unknown) as Message gateway.client.emit('messageUpdate', message, newMsg) } diff --git a/src/gateway/handlers/ready.ts b/src/gateway/handlers/ready.ts index 385c3b6..d3da28d 100644 --- a/src/gateway/handlers/ready.ts +++ b/src/gateway/handlers/ready.ts @@ -1,19 +1,22 @@ import { User } from '../../structures/user.ts' -import { Ready } from "../../types/gateway.ts" +import { Ready } from '../../types/gateway.ts' import { GuildPayload } from '../../types/guild.ts' import { Gateway, GatewayEventHandler } from '../index.ts' -export const ready: GatewayEventHandler = async (gateway: Gateway, d: Ready) => { +export const ready: GatewayEventHandler = async ( + gateway: Gateway, + d: Ready +) => { await gateway.client.guilds.flush() gateway.client.user = new User(gateway.client, d.user) gateway.sessionID = d.session_id gateway.debug(`Received READY. Session: ${gateway.sessionID}`) - await gateway.cache.set("session_id", gateway.sessionID) + await gateway.cache.set('session_id', gateway.sessionID) d.guilds.forEach((guild: GuildPayload) => { gateway.client.guilds.set(guild.id, guild) }) - + gateway.client.emit('ready') -} \ No newline at end of file +} diff --git a/src/gateway/handlers/reconnect.ts b/src/gateway/handlers/reconnect.ts index 1dbcbf6..75f7b48 100644 --- a/src/gateway/handlers/reconnect.ts +++ b/src/gateway/handlers/reconnect.ts @@ -1,5 +1,8 @@ -import { Gateway , GatewayEventHandler } from '../index.ts' - -export const reconnect: GatewayEventHandler = async (gateway: Gateway, d: any) => { - gateway.reconnect() -} \ No newline at end of file +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const reconnect: GatewayEventHandler = async ( + gateway: Gateway, + d: any +) => { + gateway.reconnect() +} diff --git a/src/gateway/handlers/voiceServerUpdate.ts b/src/gateway/handlers/voiceServerUpdate.ts index 6a17138..5fc299d 100644 --- a/src/gateway/handlers/voiceServerUpdate.ts +++ b/src/gateway/handlers/voiceServerUpdate.ts @@ -1,5 +1,5 @@ -import { Guild } from "../../structures/guild.ts" -import { VoiceServerUpdatePayload } from "../../types/gateway.ts" +import { Guild } from '../../structures/guild.ts' +import { VoiceServerUpdatePayload } from '../../types/gateway.ts' import { Gateway, GatewayEventHandler } from '../index.ts' export const voiceServerUpdate: GatewayEventHandler = async ( @@ -9,6 +9,6 @@ export const voiceServerUpdate: GatewayEventHandler = async ( gateway.client.emit('voiceServerUpdate', { token: d.token, endpoint: d.endpoint, - guild: (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild + guild: ((await gateway.client.guilds.get(d.guild_id)) as unknown) as Guild, }) } diff --git a/src/gateway/handlers/voiceStateUpdate.ts b/src/gateway/handlers/voiceStateUpdate.ts index fb8b95a..9ea7954 100644 --- a/src/gateway/handlers/voiceStateUpdate.ts +++ b/src/gateway/handlers/voiceStateUpdate.ts @@ -1,6 +1,7 @@ -import { Guild } from "../../structures/guild.ts" -import { VoiceState } from "../../structures/voiceState.ts" -import { VoiceStatePayload } from "../../types/voice.ts" +import { Guild } from '../../structures/guild.ts' +import { VoiceState } from '../../structures/voiceState.ts' +import { MemberPayload } from '../../types/guild.ts' +import { VoiceStatePayload } from '../../types/voice.ts' import { Gateway, GatewayEventHandler } from '../index.ts' export const voiceStateUpdate: GatewayEventHandler = async ( @@ -9,22 +10,41 @@ export const voiceStateUpdate: GatewayEventHandler = async ( ) => { // TODO(DjDeveloperr): Support self-bot here; they can be in DMs (Call) if (d.guild_id === undefined) return - const guild = (await gateway.client.guilds.get(d.guild_id) as unknown) as Guild + const guild = ((await gateway.client.guilds.get( + d.guild_id + )) as unknown) as Guild const voiceState = await guild.voiceStates.get(d.user_id) if (d.channel_id === null) { + if (voiceState === undefined) { + await guild.members.set(d.user_id, (d.member as unknown) as MemberPayload) + const member = ((await guild.members.get( + d.user_id + )) as unknown) as MemberPayload + return gateway.client.emit('voiceStateRemoveUncached', { guild, member }) + } // No longer in the channel, so delete await guild.voiceStates.delete(d.user_id) - gateway.client.emit('voiceStateRemove', (voiceState as unknown) as VoiceState) + gateway.client.emit( + 'voiceStateRemove', + (voiceState as unknown) as VoiceState + ) return } await guild.voiceStates.set(d.user_id, d) const newVoiceState = await guild.voiceStates.get(d.user_id) if (voiceState === undefined) { - gateway.client.emit('voiceStateAdd', (newVoiceState as unknown) as VoiceState) + gateway.client.emit( + 'voiceStateAdd', + (newVoiceState as unknown) as VoiceState + ) } else { - gateway.client.emit('voiceStateUpdate', voiceState, (newVoiceState as unknown) as VoiceState) + gateway.client.emit( + 'voiceStateUpdate', + voiceState, + (newVoiceState as unknown) as VoiceState + ) } } diff --git a/src/gateway/handlers/webhooksUpdate.ts b/src/gateway/handlers/webhooksUpdate.ts index 876fb7f..99aa8ed 100644 --- a/src/gateway/handlers/webhooksUpdate.ts +++ b/src/gateway/handlers/webhooksUpdate.ts @@ -1,7 +1,7 @@ import { Gateway, GatewayEventHandler } from '../index.ts' import { Guild } from '../../structures/guild.ts' -import { WebhooksUpdatePayload } from "../../types/gateway.ts" -import { GuildTextChannel } from "../../structures/textChannel.ts" +import { WebhooksUpdatePayload } from '../../types/gateway.ts' +import { GuildTextChannel } from '../../structures/textChannel.ts' export const webhooksUpdate: GatewayEventHandler = async ( gateway: Gateway, @@ -10,7 +10,10 @@ export const webhooksUpdate: GatewayEventHandler = async ( const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) if (guild === undefined) return - const channel: GuildTextChannel | undefined = await guild.channels.get(d.channel_id) as GuildTextChannel - if (channel === undefined) gateway.client.emit('webhooksUpdateUncached', guild, d.channel_id) + const channel: GuildTextChannel | undefined = (await guild.channels.get( + d.channel_id + )) as GuildTextChannel + if (channel === undefined) + gateway.client.emit('webhooksUpdateUncached', guild, d.channel_id) else gateway.client.emit('webhooksUpdate', guild, channel) -} \ No newline at end of file +} diff --git a/src/managers/guildVoiceStates.ts b/src/managers/guildVoiceStates.ts index 70d0a07..63d00c1 100644 --- a/src/managers/guildVoiceStates.ts +++ b/src/managers/guildVoiceStates.ts @@ -1,30 +1,48 @@ import { Client } from '../models/client.ts' -import { Guild } from "../structures/guild.ts" -import { VoiceChannel } from "../structures/guildVoiceChannel.ts" -import { User } from "../structures/user.ts" -import { VoiceState } from "../structures/voiceState.ts" -import { VoiceStatePayload } from "../types/voice.ts" +import { Guild } from '../structures/guild.ts' +import { VoiceChannel } from '../structures/guildVoiceChannel.ts' +import { User } from '../structures/user.ts' +import { VoiceState } from '../structures/voiceState.ts' +import { VoiceStatePayload } from '../types/voice.ts' import { BaseManager } from './base.ts' -export class GuildVoiceStatesManager extends BaseManager { +export class GuildVoiceStatesManager extends BaseManager< + VoiceStatePayload, + VoiceState +> { guild: Guild - async get (key: string): Promise { - const raw = await this._get(key) - if (raw === undefined) return - - const guild = raw.guild_id === undefined ? undefined : await this.client.guilds.get(raw.guild_id) - - return new VoiceState(this.client, raw, { - user: (await this.client.users.get(raw.user_id) as unknown) as User, - channel: raw.channel_id == null ? null : (await this.client.channels.get(raw.channel_id) as unknown) as VoiceChannel, - guild, - member: guild === undefined ? undefined : await guild.members.get(raw.user_id) - }) - } - - constructor (client: Client, guild: Guild) { + constructor(client: Client, guild: Guild) { super(client, `vs:${guild.id}`, VoiceState) this.guild = guild } + + async get(key: string): Promise { + const raw = await this._get(key) + if (raw === undefined) return + + const guild = + raw.guild_id === undefined + ? undefined + : await this.client.guilds.get(raw.guild_id) + + return new VoiceState(this.client, raw, { + user: ((await this.client.users.get(raw.user_id)) as unknown) as User, + channel: + raw.channel_id == null + ? null + : (((await this.client.channels.get( + raw.channel_id + )) as unknown) as VoiceChannel), + guild, + member: + guild === undefined ? undefined : await guild.members.get(raw.user_id), + }) + } + + async fromPayload(d: VoiceStatePayload[]): Promise { + for (const data of d) { + await this.set(data.user_id, data) + } + } } diff --git a/src/managers/messages.ts b/src/managers/messages.ts index b7cf181..b1d6eee 100644 --- a/src/managers/messages.ts +++ b/src/managers/messages.ts @@ -1,69 +1,80 @@ -import { Client } from '../models/client.ts' -import { Message } from '../structures/message.ts' -import { TextChannel } from '../structures/textChannel.ts' -import { User } from '../structures/user.ts' -import { MessagePayload } from '../types/channel.ts' -import { CHANNEL_MESSAGE } from '../types/endpoint.ts' -import { BaseManager } from './base.ts' - -export class MessagesManager extends BaseManager { - channel: TextChannel - - constructor (client: Client, channel: TextChannel) { - super(client, 'messages', Message) - this.channel = channel - } - - async get (key: string): Promise { - const raw = await this._get(key) - if (raw === undefined) return - - let channel = await this.client.channels.get(raw.channel_id) - if (channel === undefined) - channel = await this.client.channels.fetch(raw.channel_id) - - const author = new User(this.client, raw.author) - - const res = new this.DataType(this.client, raw, channel, author) as any - await res.mentions.fromPayload(raw) - return res - } - - async set (key: string, value: MessagePayload): Promise { - return this.client.cache.set(this.cacheName, key, value, this.client.messageCacheLifetime) - } - - async fetch (id: string): Promise { - return await new Promise((resolve, reject) => { - this.client.rest - .get(CHANNEL_MESSAGE(this.channel.id, id)) - .then(async data => { - await this.set(id, data as MessagePayload) - - let channel: any = await this.client.channels.get( - this.channel.id - ) - if (channel === undefined) - channel = await this.client.channels.fetch(this.channel.id) - - const author = new User(this.client, (data as MessagePayload).author) - await this.client.users.set( - author.id, - (data as MessagePayload).author - ) - - const res = new Message( - this.client, - data as MessagePayload, - channel as TextChannel, - author - ) - - await res.mentions.fromPayload(data) - - resolve(res) - }) - .catch(e => reject(e)) - }) - } -} +import { Client } from '../models/client.ts' +import { Message } from '../structures/message.ts' +import { TextChannel } from '../structures/textChannel.ts' +import { User } from '../structures/user.ts' +import { MessagePayload } from '../types/channel.ts' +import { CHANNEL_MESSAGE } from '../types/endpoint.ts' +import { BaseManager } from './base.ts' + +export class MessagesManager extends BaseManager { + channel: TextChannel + + constructor(client: Client, channel: TextChannel) { + super(client, 'messages', Message) + this.channel = channel + } + + async get(key: string): Promise { + const raw = await this._get(key) + if (raw === undefined) return + + if (raw.author === undefined) return + + let channel = await this.client.channels.get(raw.channel_id) + if (channel === undefined) + channel = await this.client.channels.fetch(raw.channel_id) + + let author = ((await this.client.users.get( + raw.author.id + )) as unknown) as User + + if (author === undefined) author = new User(this.client, raw.author) + + const res = new this.DataType(this.client, raw, channel, author) as any + await res.mentions.fromPayload(raw) + return res + } + + async set(key: string, value: MessagePayload): Promise { + return this.client.cache.set( + this.cacheName, + key, + value, + this.client.messageCacheLifetime + ) + } + + async fetch(id: string): Promise { + return await new Promise((resolve, reject) => { + this.client.rest + .get(CHANNEL_MESSAGE(this.channel.id, id)) + .then(async (data) => { + await this.set(id, data as MessagePayload) + + let channel: any = await this.client.channels.get( + this.channel.id + ) + if (channel === undefined) + channel = await this.client.channels.fetch(this.channel.id) + + const author = new User(this.client, (data as MessagePayload).author) + await this.client.users.set( + author.id, + (data as MessagePayload).author + ) + + const res = new Message( + this.client, + data as MessagePayload, + channel as TextChannel, + author + ) + + await res.mentions.fromPayload(data) + + resolve(res) + }) + .catch((e) => reject(e)) + }) + } +} diff --git a/src/models/command.ts b/src/models/command.ts index 1c99110..48b2a0a 100644 --- a/src/models/command.ts +++ b/src/models/command.ts @@ -4,7 +4,7 @@ import { TextChannel } from '../structures/textChannel.ts' import { User } from '../structures/user.ts' import { Collection } from '../utils/collection.ts' import { CommandClient } from './commandClient.ts' -import { Extension } from "./extensions.ts" +import { Extension } from './extensions.ts' export interface CommandContext { /** The Client object */ @@ -34,6 +34,8 @@ export class Command { name: string = '' /** Description of the Command */ description?: string + /** Category of the Command */ + category?: string /** Array of Aliases of Command, or only string */ aliases?: string | string[] /** Extension (Parent) of the Command */ @@ -42,10 +44,12 @@ export class Command { usage?: string | string[] /** Usage Example of Command, only Arguments (without Prefix and Name) */ examples?: string | string[] - /** Does the Command take Arguments? Maybe number of required arguments? */ - args?: number | boolean - /** Permission(s) required for using Command */ + /** Does the Command take Arguments? Maybe number of required arguments? Or list of arguments? */ + args?: number | boolean | string[] + /** Permissions(s) required by both User and Bot in order to use Command */ permissions?: string | string[] + /** Permission(s) required for using Command */ + userPermissions?: string | string[] /** Permission(s) bot will need in order to execute Command */ botPermissions?: string | string[] /** Role(s) user will require in order to use Command. List or one of ID or name */ @@ -64,14 +68,189 @@ export class Command { ownerOnly?: boolean /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ - beforeExecute(ctx: CommandContext): boolean | Promise { return true } + beforeExecute(ctx: CommandContext): boolean | Promise { + return true + } + /** Actual command code, which is executed when all checks have passed. */ - execute(ctx: CommandContext): any { } + execute(ctx: CommandContext): any {} /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ - afterExecute(ctx: CommandContext, executeResult: any): any { } + afterExecute(ctx: CommandContext, executeResult: any): any {} toString(): string { - return `Command: ${this.name}${this.extension !== undefined ? ` [${this.extension.name}]` : ''}` + return `Command: ${this.name}${ + this.extension !== undefined + ? ` [${this.extension.name}]` + : this.category !== undefined + ? ` [${this.category}]` + : '' + }` + } +} + +export class CommandCategory { + /** Name of the Category. */ + name: string = '' + /** Permissions(s) required by both User and Bot in order to use Category Commands */ + permissions?: string | string[] + /** Permission(s) required for using Category Commands */ + userPermissions?: string | string[] + /** Permission(s) bot will need in order to execute Category Commands */ + botPermissions?: string | string[] + /** Role(s) user will require in order to use Category Commands. List or one of ID or name */ + roles?: string | string[] + /** Whitelisted Guilds. Only these Guild(s) can execute Category Commands. (List or one of IDs) */ + whitelistedGuilds?: string | string[] + /** Whitelisted Channels. Category Commands can be executed only in these channels. (List or one of IDs) */ + whitelistedChannels?: string | string[] + /** Whitelisted Users. Category Commands can be executed only by these Users (List or one of IDs) */ + whitelistedUsers?: string | string[] + /** Whether the Category Commands can only be used in Guild (if allowed in DMs) */ + guildOnly?: boolean + /** Whether the Category Commands can only be used in Bot's DMs (if allowed) */ + dmOnly?: boolean + /** Whether the Category Commands can only be used by Bot Owners */ + ownerOnly?: boolean +} + +export class CommandBuilder extends Command { + setName(name: string): CommandBuilder { + this.name = name + return this + } + + setDescription(description?: string): CommandBuilder { + this.description = description + return this + } + + setCategory(category?: string): CommandBuilder { + this.category = category + return this + } + + setAlias(alias: string | string[]): CommandBuilder { + this.aliases = alias + return this + } + + addAlias(alias: string | string[]): CommandBuilder { + if (this.aliases === undefined) this.aliases = [] + if (typeof this.aliases === 'string') this.aliases = [this.aliases] + + this.aliases = [ + ...new Set( + ...this.aliases, + ...(typeof alias === 'string' ? [alias] : alias) + ), + ] + + return this + } + + setExtension(extension?: Extension): CommandBuilder { + this.extension = extension + return this + } + + setUsage(usage: string | string[]): CommandBuilder { + this.usage = usage + return this + } + + addUsage(usage: string | string[]): CommandBuilder { + if (this.usage === undefined) this.usage = [] + if (typeof this.usage === 'string') this.usage = [this.usage] + + this.aliases = [ + ...new Set( + ...this.usage, + ...(typeof usage === 'string' ? [usage] : usage) + ), + ] + + return this + } + + setExample(examples: string | string[]): CommandBuilder { + this.examples = examples + return this + } + + addExample(examples: string | string[]): CommandBuilder { + if (this.examples === undefined) this.examples = [] + if (typeof this.examples === 'string') this.examples = [this.examples] + + this.examples = [ + ...new Set( + ...this.examples, + ...(typeof examples === 'string' ? [examples] : examples) + ), + ] + + return this + } + + setPermissions(perms?: string | string[]): CommandBuilder { + this.permissions = perms + return this + } + + setUserPermissions(perms?: string | string[]): CommandBuilder { + this.userPermissions = perms + return this + } + + setBotPermissions(perms?: string | string[]): CommandBuilder { + this.botPermissions = perms + return this + } + + setRoles(roles: string | string[]): CommandBuilder { + this.roles = roles + return this + } + + setWhitelistedGuilds(list: string | string[]): CommandBuilder { + this.whitelistedGuilds = list + return this + } + + setWhitelistedUsers(list: string | string[]): CommandBuilder { + this.whitelistedUsers = list + return this + } + + setWhitelistedChannels(list: string | string[]): CommandBuilder { + this.whitelistedChannels = list + return this + } + + setGuildOnly(value: boolean = true): CommandBuilder { + this.guildOnly = value + return this + } + + setOwnerOnly(value: boolean = true): CommandBuilder { + this.ownerOnly = value + return this + } + + onBeforeExecute(fn: (ctx: CommandContext) => boolean | any): CommandBuilder { + this.beforeExecute = fn + return this + } + + onExecute(fn: (ctx: CommandContext) => any): CommandBuilder { + this.execute = fn + return this + } + + onAfterExecute( + fn: (ctx: CommandContext, executeResult?: any) => any + ): CommandBuilder { + this.afterExecute = fn + return this } } @@ -85,7 +264,9 @@ export class CommandsManager { } /** Number of loaded Commands */ - get count(): number { return this.list.size } + get count(): number { + return this.list.size + } /** Find a Command by name/alias */ find(search: string): Command | undefined { @@ -99,7 +280,7 @@ export class CommandsManager { if (typeof cmd.aliases === 'string') aliases = [cmd.aliases] else aliases = cmd.aliases if (this.client.caseSensitive === false) - aliases = aliases.map(e => e.toLowerCase()) + aliases = aliases.map((e) => e.toLowerCase()) return aliases.includes(search) } else return false }) @@ -123,8 +304,9 @@ export class CommandsManager { const aliases: string[] = typeof search.aliases === 'string' ? [search.aliases] : search.aliases exists = - aliases.map(alias => this.find(alias) !== undefined).find(e => e) ?? - false + aliases + .map((alias) => this.find(alias) !== undefined) + .find((e) => e) ?? false } return exists } @@ -134,7 +316,10 @@ export class CommandsManager { add(cmd: Command | typeof Command): boolean { // eslint-disable-next-line new-cap if (!(cmd instanceof Command)) cmd = new cmd() - if (this.exists(cmd)) throw new Error(`Failed to add Command '${cmd.toString()}' with name/alias already exists.`) + if (this.exists(cmd)) + throw new Error( + `Failed to add Command '${cmd.toString()}' with name/alias already exists.` + ) this.list.set(cmd.name, cmd) return true } @@ -148,7 +333,7 @@ export class CommandsManager { /** Check whether a Command is disabled or not */ isDisabled(name: string | Command): boolean { - const cmd = typeof name === "string" ? this.find(name) : name + const cmd = typeof name === 'string' ? this.find(name) : name if (cmd === undefined) return false const exists = this.exists(name) if (!exists) return false @@ -157,12 +342,65 @@ export class CommandsManager { /** Disable a Command */ disable(name: string | Command): boolean { - const cmd = typeof name === "string" ? this.find(name) : name + const cmd = typeof name === 'string' ? this.find(name) : name if (cmd === undefined) return false if (this.isDisabled(cmd)) return false this.disabled.add(cmd.name) return true } + + /** Get all commands of a Category */ + category(category: string): Collection { + return this.list.filter( + (cmd) => cmd.category !== undefined && cmd.category === category + ) + } +} + +export class CategoriesManager { + client: CommandClient + list: Collection = new Collection() + + constructor(client: CommandClient) { + this.client = client + } + + /** Get a Collection of Categories */ + all(): Collection { + return this.list + } + + /** Get a list of names of Categories added */ + names(): string[] { + return [...this.list.keys()] + } + + /** Check if a Category exists or not */ + has(category: string | CommandCategory): boolean { + return this.list.has( + typeof category === 'string' ? category : category.name + ) + } + + /** Get a Category by name */ + get(name: string): CommandCategory | undefined { + return this.list.get(name) + } + + /** Add a Category to the Manager */ + add(category: CommandCategory): CategoriesManager { + if (this.has(category)) + throw new Error(`Category ${category.name} already exists`) + this.list.set(category.name, category) + return this + } + + /** Remove a Category from the Manager */ + remove(category: string | CommandCategory): boolean { + if (!this.has(category)) return false + this.list.delete(typeof category === 'string' ? category : category.name) + return true + } } export interface ParsedCommand { @@ -185,6 +423,6 @@ export const parseCommand = ( return { name, args, - argString + argString, } } diff --git a/src/models/commandClient.ts b/src/models/commandClient.ts index 2ab33fb..e3b3b66 100644 --- a/src/models/commandClient.ts +++ b/src/models/commandClient.ts @@ -1,198 +1,332 @@ -import { Message } from "../../mod.ts" -import { awaitSync } from "../utils/mixedPromise.ts" -import { Client, ClientOptions } from './client.ts' -import { CommandContext, CommandsManager, parseCommand } from './command.ts' -import { ExtensionsManager } from "./extensions.ts" - -type PrefixReturnType = string | string[] | Promise - -export interface CommandClientOptions extends ClientOptions { - prefix: string | string[] - mentionPrefix?: boolean - getGuildPrefix?: (guildID: string) => PrefixReturnType - getUserPrefix?: (userID: string) => PrefixReturnType - isGuildBlacklisted?: (guildID: string) => boolean | Promise - isUserBlacklisted?: (guildID: string) => boolean | Promise - isChannelBlacklisted?: (guildID: string) => boolean | Promise - spacesAfterPrefix?: boolean - betterArgs?: boolean - owners?: string[] - allowBots?: boolean - allowDMs?: boolean - caseSensitive?: boolean -} - -export class CommandClient extends Client implements CommandClientOptions { - prefix: string | string[] - mentionPrefix: boolean - getGuildPrefix: (guildID: string) => PrefixReturnType - getUserPrefix: (userID: string) => PrefixReturnType - isGuildBlacklisted: (guildID: string) => boolean | Promise - isUserBlacklisted: (guildID: string) => boolean | Promise - isChannelBlacklisted: (guildID: string) => boolean | Promise - spacesAfterPrefix: boolean - betterArgs: boolean - owners: string[] - allowBots: boolean - allowDMs: boolean - caseSensitive: boolean - extensions: ExtensionsManager = new ExtensionsManager(this) - commands: CommandsManager = new CommandsManager(this) - - constructor(options: CommandClientOptions) { - super(options) - this.prefix = options.prefix - this.mentionPrefix = - options.mentionPrefix === undefined ? false : options.mentionPrefix - this.getGuildPrefix = - options.getGuildPrefix === undefined - ? (id: string) => this.prefix - : options.getGuildPrefix - this.getUserPrefix = - options.getUserPrefix === undefined - ? (id: string) => this.prefix - : options.getUserPrefix - this.isUserBlacklisted = - options.isUserBlacklisted === undefined - ? (id: string) => false - : options.isUserBlacklisted - this.isGuildBlacklisted = - options.isGuildBlacklisted === undefined - ? (id: string) => false - : options.isGuildBlacklisted - this.isChannelBlacklisted = - options.isChannelBlacklisted === undefined - ? (id: string) => false - : options.isChannelBlacklisted - this.spacesAfterPrefix = - options.spacesAfterPrefix === undefined - ? false - : options.spacesAfterPrefix - this.betterArgs = - options.betterArgs === undefined ? false : options.betterArgs - this.owners = options.owners === undefined ? [] : options.owners - this.allowBots = options.allowBots === undefined ? false : options.allowBots - this.allowDMs = options.allowDMs === undefined ? true : options.allowDMs - this.caseSensitive = - options.caseSensitive === undefined ? false : options.caseSensitive - - this.on( - 'messageCreate', - async (msg: Message) => await this.processMessage(msg) - ) - } - - async processMessage(msg: Message): Promise { - if (!this.allowBots && msg.author.bot === true) return - - const isUserBlacklisted = await awaitSync(this.isUserBlacklisted(msg.author.id)) - if (isUserBlacklisted === true) return - - const isChannelBlacklisted = await awaitSync(this.isChannelBlacklisted(msg.channel.id)) - if (isChannelBlacklisted === true) return - - if (msg.guild !== undefined) { - const isGuildBlacklisted = await awaitSync(this.isGuildBlacklisted(msg.guild.id)) - if (isGuildBlacklisted === true) return - } - - let prefix: string | string[] = this.prefix - - if (msg.guild !== undefined) { - prefix = await awaitSync(this.getGuildPrefix(msg.guild.id)) - } else { - prefix = await awaitSync(this.getUserPrefix(msg.author.id)) - } - - let mentionPrefix = false - - if (typeof prefix === 'string') { - if (msg.content.startsWith(prefix) === false) { - if (this.mentionPrefix) mentionPrefix = true - else return - } - } else { - const usedPrefix = prefix.find(v => msg.content.startsWith(v)) - if (usedPrefix === undefined) { - if (this.mentionPrefix) mentionPrefix = true - else return - } - else prefix = usedPrefix - } - - if (mentionPrefix) { - if (msg.content.startsWith(this.user?.mention as string) === true) prefix = this.user?.mention as string - else if (msg.content.startsWith(this.user?.nickMention as string) === true) prefix = this.user?.nickMention as string - else return - } - - if (typeof prefix !== 'string') return - - const parsed = parseCommand(this, msg, prefix) - const command = this.commands.find(parsed.name) - - if (command === undefined) return - - if (command.whitelistedGuilds !== undefined && msg.guild !== undefined && command.whitelistedGuilds.includes(msg.guild.id) === false) return; - if (command.whitelistedChannels !== undefined && command.whitelistedChannels.includes(msg.channel.id) === false) return; - if (command.whitelistedUsers !== undefined && command.whitelistedUsers.includes(msg.author.id) === false) return; - - const ctx: CommandContext = { - client: this, - name: parsed.name, - prefix, - args: parsed.args, - argString: parsed.argString, - message: msg, - author: msg.author, - command, - channel: msg.channel, - guild: msg.guild - } - - if (command.ownerOnly === true && !this.owners.includes(msg.author.id)) return this.emit('commandOwnerOnly', ctx, command) - if (command.guildOnly === true && msg.guild === undefined) return this.emit('commandGuildOnly', ctx, command) - if (command.dmOnly === true && msg.guild !== undefined) return this.emit('commandDmOnly', ctx, command) - - if (command.botPermissions !== undefined && msg.guild !== undefined) { - // TODO: Check Overwrites too - const me = await msg.guild.me() - const missing: string[] = [] - - for (const perm of command.botPermissions) { - if (me.permissions.has(perm) === false) missing.push(perm) - } - - if (missing.length !== 0) return this.emit('commandBotMissingPermissions', ctx, command, missing) - } - - if (command.permissions !== undefined && msg.guild !== undefined) { - const missing: string[] = [] - let perms: string[] = [] - if (typeof command.permissions === 'string') perms = [command.permissions] - else perms = command.permissions - for (const perm of perms) { - const has = msg.member?.permissions.has(perm) - if (has !== true) missing.push(perm) - } - if (missing.length !== 0) return this.emit('commandMissingPermissions', command, missing, ctx) - } - - if (command.args !== undefined) { - if (typeof command.args === 'boolean' && parsed.args.length === 0) return this.emit('commandMissingArgs', ctx, command) - else if (typeof command.args === 'number' && parsed.args.length < command.args) this.emit('commandMissingArgs', ctx, command) - } - - try { - this.emit('commandUsed', ctx, command) - - const beforeExecute = await awaitSync(command.beforeExecute(ctx)) - if (beforeExecute === false) return - - const result = await awaitSync(command.execute(ctx)) - command.afterExecute(ctx, result) - } catch (e) { - this.emit('commandError', command, ctx, e) - } - } -} +import { Message } from '../../mod.ts' +import { awaitSync } from '../utils/mixedPromise.ts' +import { Client, ClientOptions } from './client.ts' +import { + CategoriesManager, + CommandContext, + CommandsManager, + parseCommand, +} from './command.ts' +import { ExtensionsManager } from './extensions.ts' + +type PrefixReturnType = string | string[] | Promise + +export interface CommandClientOptions extends ClientOptions { + prefix: string | string[] + mentionPrefix?: boolean + getGuildPrefix?: (guildID: string) => PrefixReturnType + getUserPrefix?: (userID: string) => PrefixReturnType + isGuildBlacklisted?: (guildID: string) => boolean | Promise + isUserBlacklisted?: (guildID: string) => boolean | Promise + isChannelBlacklisted?: (guildID: string) => boolean | Promise + spacesAfterPrefix?: boolean + betterArgs?: boolean + owners?: string[] + allowBots?: boolean + allowDMs?: boolean + caseSensitive?: boolean +} + +export class CommandClient extends Client implements CommandClientOptions { + prefix: string | string[] + mentionPrefix: boolean + getGuildPrefix: (guildID: string) => PrefixReturnType + getUserPrefix: (userID: string) => PrefixReturnType + isGuildBlacklisted: (guildID: string) => boolean | Promise + isUserBlacklisted: (guildID: string) => boolean | Promise + isChannelBlacklisted: (guildID: string) => boolean | Promise + spacesAfterPrefix: boolean + betterArgs: boolean + owners: string[] + allowBots: boolean + allowDMs: boolean + caseSensitive: boolean + extensions: ExtensionsManager = new ExtensionsManager(this) + commands: CommandsManager = new CommandsManager(this) + categories: CategoriesManager = new CategoriesManager(this) + + constructor(options: CommandClientOptions) { + super(options) + this.prefix = options.prefix + this.mentionPrefix = + options.mentionPrefix === undefined ? false : options.mentionPrefix + this.getGuildPrefix = + options.getGuildPrefix === undefined + ? (id: string) => this.prefix + : options.getGuildPrefix + this.getUserPrefix = + options.getUserPrefix === undefined + ? (id: string) => this.prefix + : options.getUserPrefix + this.isUserBlacklisted = + options.isUserBlacklisted === undefined + ? (id: string) => false + : options.isUserBlacklisted + this.isGuildBlacklisted = + options.isGuildBlacklisted === undefined + ? (id: string) => false + : options.isGuildBlacklisted + this.isChannelBlacklisted = + options.isChannelBlacklisted === undefined + ? (id: string) => false + : options.isChannelBlacklisted + this.spacesAfterPrefix = + options.spacesAfterPrefix === undefined + ? false + : options.spacesAfterPrefix + this.betterArgs = + options.betterArgs === undefined ? false : options.betterArgs + this.owners = options.owners === undefined ? [] : options.owners + this.allowBots = options.allowBots === undefined ? false : options.allowBots + this.allowDMs = options.allowDMs === undefined ? true : options.allowDMs + this.caseSensitive = + options.caseSensitive === undefined ? false : options.caseSensitive + + this.on( + 'messageCreate', + async (msg: Message) => await this.processMessage(msg) + ) + } + + async processMessage(msg: Message): Promise { + if (!this.allowBots && msg.author.bot === true) return + + const isUserBlacklisted = await awaitSync( + this.isUserBlacklisted(msg.author.id) + ) + if (isUserBlacklisted === true) return + + const isChannelBlacklisted = await awaitSync( + this.isChannelBlacklisted(msg.channel.id) + ) + if (isChannelBlacklisted === true) return + + if (msg.guild !== undefined) { + const isGuildBlacklisted = await awaitSync( + this.isGuildBlacklisted(msg.guild.id) + ) + if (isGuildBlacklisted === true) return + } + + let prefix: string | string[] = this.prefix + + if (msg.guild !== undefined) { + prefix = await awaitSync(this.getGuildPrefix(msg.guild.id)) + } else { + prefix = await awaitSync(this.getUserPrefix(msg.author.id)) + } + + let mentionPrefix = false + + if (typeof prefix === 'string') { + if (msg.content.startsWith(prefix) === false) { + if (this.mentionPrefix) mentionPrefix = true + else return + } + } else { + const usedPrefix = prefix.find((v) => msg.content.startsWith(v)) + if (usedPrefix === undefined) { + if (this.mentionPrefix) mentionPrefix = true + else return + } else prefix = usedPrefix + } + + if (mentionPrefix) { + if (msg.content.startsWith(this.user?.mention as string) === true) + prefix = this.user?.mention as string + else if ( + msg.content.startsWith(this.user?.nickMention as string) === true + ) + prefix = this.user?.nickMention as string + else return + } + + if (typeof prefix !== 'string') return + + const parsed = parseCommand(this, msg, prefix) + const command = this.commands.find(parsed.name) + + if (command === undefined) return + const category = + command.category !== undefined + ? this.categories.get(command.category) + : undefined + + // Guild whitelist exists, and if does and Command used in a Guild, is this Guild allowed? + // This is a bit confusing here, if these settings on a Command exist, and also do on Category, Command overrides them + if ( + command.whitelistedGuilds === undefined && + category?.whitelistedGuilds !== undefined && + msg.guild !== undefined && + category.whitelistedGuilds.includes(msg.guild.id) === false + ) + return + if ( + command.whitelistedGuilds !== undefined && + msg.guild !== undefined && + command.whitelistedGuilds.includes(msg.guild.id) === false + ) + return + + // Checks for Channel Whitelist + if ( + command.whitelistedChannels === undefined && + category?.whitelistedChannels !== undefined && + category.whitelistedChannels.includes(msg.channel.id) === false + ) + return + if ( + command.whitelistedChannels !== undefined && + command.whitelistedChannels.includes(msg.channel.id) === false + ) + return + + // Checks for Users Whitelist + if ( + command.whitelistedUsers === undefined && + category?.whitelistedUsers !== undefined && + category.whitelistedUsers.includes(msg.author.id) === false + ) + return + if ( + command.whitelistedUsers !== undefined && + command.whitelistedUsers.includes(msg.author.id) === false + ) + return + + const ctx: CommandContext = { + client: this, + name: parsed.name, + prefix, + args: parsed.args, + argString: parsed.argString, + message: msg, + author: msg.author, + command, + channel: msg.channel, + guild: msg.guild, + } + + // In these checks too, Command overrides Category if present + // Check if Command is only for Owners + if ( + (command.ownerOnly !== undefined || category === undefined + ? command.ownerOnly + : category.ownerOnly) === true && + !this.owners.includes(msg.author.id) + ) + return this.emit('commandOwnerOnly', ctx, command) + + // Check if Command is only for Guild + if ( + (command.guildOnly !== undefined || category === undefined + ? command.guildOnly + : category.guildOnly) === true && + msg.guild === undefined + ) + return this.emit('commandGuildOnly', ctx, command) + + // Check if Command is only for DMs + if ( + (command.dmOnly !== undefined || category === undefined + ? command.dmOnly + : category.dmOnly) === true && + msg.guild !== undefined + ) + return this.emit('commandDmOnly', ctx, command) + + const allPermissions = + command.permissions !== undefined + ? command.permissions + : category?.permissions + + if ( + (command.botPermissions !== undefined || + category?.permissions !== undefined) && + msg.guild !== undefined + ) { + // TODO: Check Overwrites too + const me = await msg.guild.me() + const missing: string[] = [] + + let permissions = + command.botPermissions === undefined + ? category?.permissions + : command.botPermissions + + if (permissions !== undefined) { + if (typeof permissions === 'string') permissions = [permissions] + + if (allPermissions !== undefined) + permissions = [...new Set(...permissions, ...allPermissions)] + + for (const perm of permissions) { + if (me.permissions.has(perm) === false) missing.push(perm) + } + + if (missing.length !== 0) + return this.emit( + 'commandBotMissingPermissions', + ctx, + command, + missing + ) + } + } + + if ( + (command.userPermissions !== undefined || + category?.userPermissions !== undefined) && + msg.guild !== undefined + ) { + let permissions = + command.userPermissions !== undefined + ? command.userPermissions + : category?.userPermissions + + if (permissions !== undefined) { + if (typeof permissions === 'string') permissions = [permissions] + + if (allPermissions !== undefined) + permissions = [...new Set(...permissions, ...allPermissions)] + + const missing: string[] = [] + + for (const perm of permissions) { + const has = msg.member?.permissions.has(perm) + if (has !== true) missing.push(perm) + } + + if (missing.length !== 0) + return this.emit( + 'commandUserMissingPermissions', + command, + missing, + ctx + ) + } + } + + if (command.args !== undefined) { + if (typeof command.args === 'boolean' && parsed.args.length === 0) + return this.emit('commandMissingArgs', ctx, command) + else if ( + typeof command.args === 'number' && + parsed.args.length < command.args + ) + this.emit('commandMissingArgs', ctx, command) + } + + try { + this.emit('commandUsed', ctx, command) + + const beforeExecute = await awaitSync(command.beforeExecute(ctx)) + if (beforeExecute === false) return + + const result = await awaitSync(command.execute(ctx)) + command.afterExecute(ctx, result) + } catch (e) { + this.emit('commandError', command, ctx, e) + } + } +} diff --git a/src/structures/messageMentions.ts b/src/structures/messageMentions.ts index bf2e252..56e1197 100644 --- a/src/structures/messageMentions.ts +++ b/src/structures/messageMentions.ts @@ -1,55 +1,63 @@ -import { Client } from "../models/client.ts"; -import { MessagePayload } from "../types/channel.ts"; -import { Collection } from "../utils/collection.ts"; -import { GuildTextChannel } from "./textChannel.ts"; -import { Message } from "./message.ts"; -import { Role } from "./role.ts"; -import { User } from "./user.ts"; - -export class MessageMentions { - client: Client - message: Message - users: Collection = new Collection() - roles: Collection = new Collection() - channels: Collection = new Collection() - everyone: boolean = false - - static EVERYONE_MENTION = /@(everyone|here)/g - static USER_MENTION = /<@!?(\d{17,19})>/g - static ROLE_MENTION = /<@&(\d{17,19})>/g - static CHANNEL_MENTION = /<#(\d{17,19})>/g - - constructor(client: Client, message: Message) { - this.client = client - this.message = message - } - - async fromPayload(payload: MessagePayload): Promise { - payload.mentions.forEach(rawUser => { - this.users.set(rawUser.id, new User(this.client, rawUser)) - }) - - if (this.message.guild !== undefined) { - for (const id of payload.mention_roles) { - const role = await this.message.guild.roles.get(id) - if(role !== undefined) this.roles.set(role.id, role) - } - } - if (payload.mention_channels !== undefined) { - for (const mentionChannel of payload.mention_channels) { - const channel = await this.client.channels.get(mentionChannel.id) - if (channel !== undefined) this.channels.set(channel.id, channel) - } - } - const matchChannels = this.message.content.match(MessageMentions.CHANNEL_MENTION) - if (matchChannels !== null) { - for (const id of matchChannels) { - const parsedID = id.substr(2, id.length - 3) - const channel = await this.client.channels.get(parsedID) - if (channel !== undefined) this.channels.set(channel.id, channel) - } - } - this.everyone = payload.mention_everyone - return this - } -} +import { Client } from '../models/client.ts' +import { MessagePayload } from '../types/channel.ts' +import { Collection } from '../utils/collection.ts' +import { GuildTextChannel } from './textChannel.ts' +import { Message } from './message.ts' +import { Role } from './role.ts' +import { User } from './user.ts' + +export class MessageMentions { + client: Client + message: Message + users: Collection = new Collection() + roles: Collection = new Collection() + channels: Collection = new Collection() + everyone: boolean = false + + static EVERYONE_MENTION = /@(everyone|here)/g + static USER_MENTION = /<@!?(\d{17,19})>/g + static ROLE_MENTION = /<@&(\d{17,19})>/g + static CHANNEL_MENTION = /<#(\d{17,19})>/g + + constructor(client: Client, message: Message) { + this.client = client + this.message = message + } + + async fromPayload(payload: MessagePayload): Promise { + if (this.message === undefined) return this + if (payload.mentions !== undefined) + payload.mentions.forEach((rawUser) => { + this.users.set(rawUser.id, new User(this.client, rawUser)) + }) + + if (this.message.guild !== undefined) { + for (const id of payload.mention_roles) { + const role = await this.message.guild.roles.get(id) + if (role !== undefined) this.roles.set(role.id, role) + } + } + if (payload.mention_channels !== undefined) { + for (const mentionChannel of payload.mention_channels) { + const channel = await this.client.channels.get( + mentionChannel.id + ) + if (channel !== undefined) this.channels.set(channel.id, channel) + } + } + const matchChannels = this.message.content.match( + MessageMentions.CHANNEL_MENTION + ) + if (matchChannels !== null) { + for (const id of matchChannels) { + const parsedID = id.substr(2, id.length - 3) + const channel = await this.client.channels.get( + parsedID + ) + if (channel !== undefined) this.channels.set(channel.id, channel) + } + } + this.everyone = payload.mention_everyone + return this + } +} diff --git a/src/test/cmd.ts b/src/test/cmd.ts index 68f0052..00a2862 100644 --- a/src/test/cmd.ts +++ b/src/test/cmd.ts @@ -1,10 +1,17 @@ -import { Command, CommandClient, Intents, GuildChannel, CommandContext, Extension } from '../../mod.ts' +import { + Command, + CommandClient, + Intents, + GuildChannel, + CommandContext, + Extension, +} from '../../mod.ts' import { TOKEN } from './config.ts' const client = new CommandClient({ - prefix: ["pls", "!"], + prefix: ['pls', '!'], spacesAfterPrefix: true, - mentionPrefix: true + mentionPrefix: true, }) client.on('debug', console.log) @@ -55,10 +62,9 @@ client.on('webhooksUpdate', (guild, channel) => { console.log(`Webhooks Updated in #${channel.name} from ${guild.name}`) }) -client.on("commandError", console.error) +client.on('commandError', console.error) class ChannelLog extends Extension { - onChannelCreate(ext: Extension, channel: GuildChannel): void { console.log(`Channel Created: ${channel.name}`) } @@ -81,15 +87,27 @@ class ChannelLog extends Extension { client.extensions.load(ChannelLog) client.on('messageDeleteBulk', (channel, messages, uncached) => { - console.log(`=== Message Delete Bulk ===\nMessages: ${messages.map(m => m.id).join(', ')}\nUncached: ${[...uncached.values()].join(', ')}`) + console.log( + `=== Message Delete Bulk ===\nMessages: ${messages + .map((m) => m.id) + .join(', ')}\nUncached: ${[...uncached.values()].join(', ')}` + ) }) client.on('channelUpdate', (before, after) => { - console.log(`Channel Update: ${(before as GuildChannel).name}, ${(after as GuildChannel).name}`) + console.log( + `Channel Update: ${(before as GuildChannel).name}, ${ + (after as GuildChannel).name + }` + ) }) client.on('typingStart', (user, channel, at, guildData) => { - console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`) + console.log( + `${user.tag} started typing in ${channel.id} at ${at}${ + guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : '' + }` + ) }) client.on('voiceStateAdd', (state) => { @@ -114,4 +132,4 @@ for (const file of files) { console.log(`Loaded ${client.commands.count} commands!`) -client.connect(TOKEN, Intents.create(['GUILD_MEMBERS', 'GUILD_PRESENCES'])) \ No newline at end of file +client.connect(TOKEN, Intents.create(['GUILD_MEMBERS', 'GUILD_PRESENCES']))