Merge pull request #43 from DjDeveloperr/main

feat(reactions): added new sructures, managers and events
This commit is contained in:
Helloyunho 2020-12-02 19:17:44 +09:00 committed by GitHub
commit c277f67b75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1475 additions and 772 deletions

View file

@ -6,10 +6,12 @@ export const channelPinsUpdate: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
d: ChannelPinsUpdatePayload d: ChannelPinsUpdatePayload
) => { ) => {
const after: TextChannel | undefined = await gateway.client.channels.get<TextChannel>(d.channel_id) const after:
| TextChannel
| undefined = await gateway.client.channels.get<TextChannel>(d.channel_id)
if (after !== undefined) { if (after !== undefined) {
const before = after.refreshFromData({ 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) const raw = await gateway.client.channels._get(d.channel_id)
await gateway.client.channels.set( await gateway.client.channels.set(

View file

@ -8,7 +8,9 @@ export const guildBanAdd: GatewayEventHandler = async (
d: GuildBanAddPayload d: GuildBanAddPayload
) => { ) => {
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) 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) const user: User =
(await gateway.client.users.get(d.user.id)) ??
new User(gateway.client, d.user)
if (guild !== undefined) { if (guild !== undefined) {
// We don't have to delete member, already done with guildMemberRemove event // We don't have to delete member, already done with guildMemberRemove event

View file

@ -9,7 +9,8 @@ export const guildBanRemove: GatewayEventHandler = async (
) => { ) => {
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
const user: User = const user: User =
await gateway.client.users.get(d.user.id) ?? new User(gateway.client, d.user) (await gateway.client.users.get(d.user.id)) ??
new User(gateway.client, d.user)
if (guild !== undefined) { if (guild !== undefined) {
gateway.client.emit('guildBanRemove', guild, user) gateway.client.emit('guildBanRemove', guild, user)

View file

@ -1,64 +1,32 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { GuildPayload } from '../../types/guild.ts' import { GuildPayload } from '../../types/guild.ts'
import { MembersManager } from '../../managers/members.ts'
import { GuildChannelPayload } from '../../types/channel.ts' import { GuildChannelPayload } from '../../types/channel.ts'
import { RolesManager } from '../../managers/roles.ts'
export const guildCreate: GatewayEventHandler = async ( export const guildCreate: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
d: GuildPayload d: GuildPayload
) => { ) => {
let guild: Guild | undefined = await gateway.client.guilds.get(d.id) const hasGuild: Guild | undefined = await gateway.client.guilds.get(d.id)
if (guild !== undefined) { await gateway.client.guilds.set(d.id, d)
// It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in const guild = ((await gateway.client.guilds.get(d.id)) as unknown) as Guild
await gateway.client.guilds.set(d.id, d)
if (d.members !== undefined) { if (d.members !== undefined) await guild.members.fromPayload(d.members)
const members = new MembersManager(gateway.client, guild)
await members.fromPayload(d.members) if (d.channels !== undefined) {
guild.members = members 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) { await guild.roles.fromPayload(d.roles)
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) { if (d.voice_states !== undefined)
const roles = new RolesManager(gateway.client, guild) await guild.voiceStates.fromPayload(d.voice_states)
await roles.fromPayload(d.roles)
guild.roles = roles
}
guild.refreshFromData(d) if (hasGuild === undefined) {
} else { // It wasn't lazy load, so emit event
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)
gateway.client.emit('guildCreate', guild) gateway.client.emit('guildCreate', guild)
} }
} }

View file

@ -1,6 +1,6 @@
import { Emoji } from "../../structures/emoji.ts" import { Emoji } from '../../structures/emoji.ts'
import { Guild } from '../../structures/guild.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 { GuildEmojiUpdatePayload } from '../../types/gateway.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
@ -13,27 +13,27 @@ export const guildEmojiUpdate: GatewayEventHandler = async (
const emojis = await guild.emojis.collection() const emojis = await guild.emojis.collection()
const deleted: Emoji[] = [] const deleted: Emoji[] = []
const added: Emoji[] = [] const added: Emoji[] = []
const updated: Array<{ before: Emoji, after: Emoji }> = [] const updated: Array<{ before: Emoji; after: Emoji }> = []
const _updated: EmojiPayload[] = [] const _updated: EmojiPayload[] = []
for (const raw of d.emojis) { for (const raw of d.emojis) {
const has = emojis.get(raw.id) const has = emojis.get(raw.id)
if (has === undefined) { if (has === undefined) {
await guild.emojis.set(raw.id, raw) 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) added.push(emoji)
} else _updated.push(raw) } else _updated.push(raw)
} }
for (const emoji of emojis.values()) { 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) { if (find === undefined) {
await guild.emojis.delete(emoji.id) await guild.emojis.delete(emoji.id)
deleted.push(emoji) deleted.push(emoji)
} else { } 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) 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 }) updated.push({ before, after })
} }
} }

View file

@ -1,6 +1,6 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { GuildIntegrationsUpdatePayload } from "../../types/gateway.ts" import { GuildIntegrationsUpdatePayload } from '../../types/gateway.ts'
export const guildIntegrationsUpdate: GatewayEventHandler = async ( export const guildIntegrationsUpdate: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,

View file

@ -1,7 +1,7 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { GuildMemberAddPayload } from "../../types/gateway.ts" import { GuildMemberAddPayload } from '../../types/gateway.ts'
import { Member } from "../../structures/member.ts" import { Member } from '../../structures/member.ts'
export const guildMemberAdd: GatewayEventHandler = async ( export const guildMemberAdd: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
@ -12,6 +12,6 @@ export const guildMemberAdd: GatewayEventHandler = async (
if (guild === undefined) return if (guild === undefined) return
await guild.members.set(d.user.id, d) 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) gateway.client.emit('guildMemberAdd', member)
} }

View file

@ -20,7 +20,7 @@ export const guildMemberUpdate: GatewayEventHandler = async (
nick: d.nick, nick: d.nick,
premium_since: d.premium_since, premium_since: d.premium_since,
deaf: member?.deaf ?? false, deaf: member?.deaf ?? false,
mute: member?.mute ?? false mute: member?.mute ?? false,
} }
await guild.members.set(d.user.id, newMemberPayload) await guild.members.set(d.user.id, newMemberPayload)
const newMember = await guild.members.get(d.user.id) const newMember = await guild.members.get(d.user.id)

View file

@ -1,6 +1,6 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { GuildRoleDeletePayload } from "../../types/gateway.ts" import { GuildRoleDeletePayload } from '../../types/gateway.ts'
export const guildRoleDelete: GatewayEventHandler = async ( export const guildRoleDelete: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
@ -12,7 +12,7 @@ export const guildRoleDelete: GatewayEventHandler = async (
const role = await guild.roles.get(d.role_id) const role = await guild.roles.get(d.role_id)
// Shouldn't happen either // Shouldn't happen either
if(role === undefined) return if (role === undefined) return
gateway.client.emit('guildRoleDelete', role) gateway.client.emit('guildRoleDelete', role)
} }

View file

@ -39,6 +39,12 @@ import { Collection } from '../../utils/collection.ts'
import { voiceServerUpdate } from './voiceServerUpdate.ts' import { voiceServerUpdate } from './voiceServerUpdate.ts'
import { voiceStateUpdate } from './voiceStateUpdate.ts' import { voiceStateUpdate } from './voiceStateUpdate.ts'
import { VoiceState } from '../../structures/voiceState.ts' import { VoiceState } from '../../structures/voiceState.ts'
import { messageReactionAdd } from './messageReactionAdd.ts'
import { messageReactionRemove } from './messageReactionRemove.ts'
import { messageReactionRemoveAll } from './messageReactionRemoveAll.ts'
import { messageReactionRemoveEmoji } from './messageReactionRemoveEmoji.ts'
import { guildMembersChunk } from './guildMembersChunk.ts'
import { presenceUpdate } from './presenceUpdate.ts'
import { inviteCreate } from './inviteCreate.ts' import { inviteCreate } from './inviteCreate.ts'
import { inviteDelete } from './inviteDelete.ts' import { inviteDelete } from './inviteDelete.ts'
@ -62,7 +68,7 @@ export const gatewayHandlers: {
GUILD_MEMBER_ADD: guildMemberAdd, GUILD_MEMBER_ADD: guildMemberAdd,
GUILD_MEMBER_REMOVE: guildMemberRemove, GUILD_MEMBER_REMOVE: guildMemberRemove,
GUILD_MEMBER_UPDATE: guildMemberUpdate, GUILD_MEMBER_UPDATE: guildMemberUpdate,
GUILD_MEMBERS_CHUNK: undefined, GUILD_MEMBERS_CHUNK: guildMembersChunk,
GUILD_ROLE_CREATE: guildRoleCreate, GUILD_ROLE_CREATE: guildRoleCreate,
GUILD_ROLE_UPDATE: guildRoleUpdate, GUILD_ROLE_UPDATE: guildRoleUpdate,
GUILD_ROLE_DELETE: guildRoleDelete, GUILD_ROLE_DELETE: guildRoleDelete,
@ -72,16 +78,16 @@ export const gatewayHandlers: {
MESSAGE_UPDATE: messageUpdate, MESSAGE_UPDATE: messageUpdate,
MESSAGE_DELETE: messageDelete, MESSAGE_DELETE: messageDelete,
MESSAGE_DELETE_BULK: messageDeleteBulk, MESSAGE_DELETE_BULK: messageDeleteBulk,
MESSAGE_REACTION_ADD: undefined, MESSAGE_REACTION_ADD: messageReactionAdd,
MESSAGE_REACTION_REMOVE: undefined, MESSAGE_REACTION_REMOVE: messageReactionRemove,
MESSAGE_REACTION_REMOVE_ALL: undefined, MESSAGE_REACTION_REMOVE_ALL: messageReactionRemoveAll,
MESSAGE_REACTION_REMOVE_EMOJI: undefined, MESSAGE_REACTION_REMOVE_EMOJI: messageReactionRemoveEmoji,
PRESENCE_UPDATE: undefined, PRESENCE_UPDATE: presenceUpdate,
TYPING_START: typingStart, TYPING_START: typingStart,
USER_UPDATE: userUpdate, USER_UPDATE: userUpdate,
VOICE_STATE_UPDATE: voiceStateUpdate, VOICE_STATE_UPDATE: voiceStateUpdate,
VOICE_SERVER_UPDATE: voiceServerUpdate, VOICE_SERVER_UPDATE: voiceServerUpdate,
WEBHOOKS_UPDATE: webhooksUpdate WEBHOOKS_UPDATE: webhooksUpdate,
} }
export interface EventTypes { export interface EventTypes {
@ -95,37 +101,46 @@ export interface VoiceServerUpdateData {
} }
export interface ClientEvents extends EventTypes { export interface ClientEvents extends EventTypes {
'ready': () => void ready: () => void
'reconnect': () => void reconnect: () => void
'resumed': () => void resumed: () => void
'channelCreate': (channel: Channel) => void channelCreate: (channel: Channel) => void
'channelDelete': (channel: Channel) => void channelDelete: (channel: Channel) => void
'channelPinsUpdate': (before: TextChannel, after: TextChannel) => void channelPinsUpdate: (before: TextChannel, after: TextChannel) => void
'channelUpdate': (before: Channel, after: Channel) => void channelUpdate: (before: Channel, after: Channel) => void
'guildBanAdd': (guild: Guild, user: User) => void guildBanAdd: (guild: Guild, user: User) => void
'guildBanRemove': (guild: Guild, user: User) => void guildBanRemove: (guild: Guild, user: User) => void
'guildCreate': (guild: Guild) => void guildCreate: (guild: Guild) => void
'guildDelete': (guild: Guild) => void guildDelete: (guild: Guild) => void
'guildEmojiAdd': (guild: Guild, emoji: Emoji) => void guildEmojiAdd: (guild: Guild, emoji: Emoji) => void
'guildEmojiDelete': (guild: Guild, emoji: Emoji) => void guildEmojiDelete: (guild: Guild, emoji: Emoji) => void
'guildEmojiUpdate': (guild: Guild, before: Emoji, after: Emoji) => void guildEmojiUpdate: (guild: Guild, before: Emoji, after: Emoji) => void
'guildIntegrationsUpdate': (guild: Guild) => void guildIntegrationsUpdate: (guild: Guild) => void
'guildMemberAdd': (member: Member) => void guildMemberAdd: (member: Member) => void
'guildMemberRemove': (member: Member) => void guildMemberRemove: (member: Member) => void
'guildMemberUpdate': (before: Member, after: Member) => void guildMemberUpdate: (before: Member, after: Member) => void
'guildRoleCreate': (role: Role) => void guildRoleCreate: (role: Role) => void
'guildRoleDelete': (role: Role) => void guildRoleDelete: (role: Role) => void
'guildRoleUpdate': (before: Role, after: Role) => void guildRoleUpdate: (before: Role, after: Role) => void
'guildUpdate': (before: Guild, after: Guild) => void guildUpdate: (before: Guild, after: Guild) => void
'messageCreate': (message: Message) => void messageCreate: (message: Message) => void
'messageDelete': (message: Message) => void messageDelete: (message: Message) => void
'messageDeleteBulk': (channel: GuildTextChannel, messages: Collection<string, Message>, uncached: Set<string>) => void messageDeleteBulk: (
'messageUpdate': (before: Message, after: Message) => void channel: GuildTextChannel,
'typingStart': (user: User, channel: TextChannel, at: Date, guildData?: TypingStartGuildData) => void messages: Collection<string, Message>,
'userUpdate': (before: User, after: User) => void uncached: Set<string>
'voiceServerUpdate': (data: VoiceServerUpdateData) => void ) => void
'voiceStateAdd': (state: VoiceState) => void messageUpdate: (before: Message, after: Message) => void
'voiceStateRemove': (state: VoiceState) => void typingStart: (
'voiceStateUpdate': (state: VoiceState, after: VoiceState) => void user: User,
'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void 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
} }

View file

@ -1,5 +1,5 @@
import { TextChannel } from '../../structures/textChannel.ts' import { TextChannel } from '../../structures/textChannel.ts'
import { MessageDeletePayload } from "../../types/gateway.ts" import { MessageDeletePayload } from '../../types/gateway.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
export const messageDelete: GatewayEventHandler = async ( export const messageDelete: GatewayEventHandler = async (
@ -13,7 +13,8 @@ export const messageDelete: GatewayEventHandler = async (
channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel
const message = await channel.messages.get(d.id) const message = await channel.messages.get(d.id)
if (message === undefined) return gateway.client.emit('messageDeleteUncached', d) if (message === undefined)
return gateway.client.emit('messageDeleteUncached', d)
await channel.messages.delete(d.id) await channel.messages.delete(d.id)
gateway.client.emit('messageDelete', message) gateway.client.emit('messageDelete', message)
} }

View file

@ -1,18 +1,22 @@
import { Message } from "../../structures/message.ts" import { Message } from '../../structures/message.ts'
import { GuildTextChannel } from '../../structures/textChannel.ts' import { GuildTextChannel } from '../../structures/textChannel.ts'
import { MessageDeleteBulkPayload } from "../../types/gateway.ts" import { MessageDeleteBulkPayload } from '../../types/gateway.ts'
import { Collection } from "../../utils/collection.ts" import { Collection } from '../../utils/collection.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
export const messageDeleteBulk: GatewayEventHandler = async ( export const messageDeleteBulk: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
d: MessageDeleteBulkPayload d: MessageDeleteBulkPayload
) => { ) => {
let channel = await gateway.client.channels.get<GuildTextChannel>(d.channel_id) let channel = await gateway.client.channels.get<GuildTextChannel>(
d.channel_id
)
// Fetch the channel if not cached // Fetch the channel if not cached
if (channel === undefined) if (channel === undefined)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion // 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<string, Message>() const messages = new Collection<string, Message>()
const uncached = new Set<string>() const uncached = new Set<string>()

View file

@ -0,0 +1,51 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
import { MessageReactionAddPayload } from '../../types/gateway.ts'
import { TextChannel } from '../../structures/textChannel.ts'
import { MessageReaction } from '../../structures/messageReaction.ts'
import { UserPayload } from '../../types/user.ts'
export const messageReactionAdd: GatewayEventHandler = async (
gateway: Gateway,
d: MessageReactionAddPayload
) => {
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
if (channel === undefined)
channel = await gateway.client.channels.fetch<TextChannel>(d.channel_id)
if (channel === undefined) return
let message = await channel.messages.get(d.message_id)
if (message === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
message = await channel.messages.fetch(d.message_id)
if (message === undefined) return
} else return
}
let user = await gateway.client.users.get(d.user_id)
if (user === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
user = await gateway.client.users.fetch(d.user_id)
if (user === undefined) return
} else return
}
let reaction = await message.reactions.get(d.emoji.id)
if (reaction === undefined) {
await message.reactions.set(d.emoji.id, {
count: 1,
emoji: d.emoji,
me: d.user_id === gateway.client.user?.id,
})
reaction = ((await message.reactions.get(
d.emoji.id
)) as unknown) as MessageReaction
}
const rawUser = ((await gateway.client.users.get(
d.user_id
)) as unknown) as UserPayload
reaction.users.set(rawUser.id, rawUser)
gateway.client.emit('messageReactionAdd', reaction, user)
}

View file

@ -0,0 +1,36 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
import { MessageReactionRemovePayload } from '../../types/gateway.ts'
import { TextChannel } from '../../structures/textChannel.ts'
export const messageReactionRemove: GatewayEventHandler = async (
gateway: Gateway,
d: MessageReactionRemovePayload
) => {
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
if (channel === undefined)
channel = await gateway.client.channels.fetch<TextChannel>(d.channel_id)
if (channel === undefined) return
let message = await channel.messages.get(d.message_id)
if (message === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
message = await channel.messages.fetch(d.message_id)
if (message === undefined) return
} else return
}
let user = await gateway.client.users.get(d.user_id)
if (user === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
user = await gateway.client.users.fetch(d.user_id)
if (user === undefined) return
} else return
}
const reaction = await message.reactions.get(d.emoji.id)
if (reaction === undefined) return
reaction.users.delete(d.user_id)
gateway.client.emit('messageReactionRemove', reaction, user)
}

View file

@ -0,0 +1,25 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
import { MessageReactionRemoveAllPayload } from '../../types/gateway.ts'
import { TextChannel } from '../../structures/textChannel.ts'
export const messageReactionRemoveAll: GatewayEventHandler = async (
gateway: Gateway,
d: MessageReactionRemoveAllPayload
) => {
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
if (channel === undefined)
channel = await gateway.client.channels.fetch<TextChannel>(d.channel_id)
if (channel === undefined) return
let message = await channel.messages.get(d.message_id)
if (message === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
message = await channel.messages.fetch(d.message_id)
if (message === undefined) return
} else return
}
await message.reactions.flush()
gateway.client.emit('messageReactionRemoveAll', message)
}

View file

@ -0,0 +1,28 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
import { MessageReactionRemoveEmojiPayload } from '../../types/gateway.ts'
import { TextChannel } from '../../structures/textChannel.ts'
export const messageReactionRemoveEmoji: GatewayEventHandler = async (
gateway: Gateway,
d: MessageReactionRemoveEmojiPayload
) => {
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
if (channel === undefined)
channel = await gateway.client.channels.fetch<TextChannel>(d.channel_id)
if (channel === undefined) return
let message = await channel.messages.get(d.message_id)
if (message === undefined) {
if (gateway.client.fetchUncachedReactions === true) {
message = await channel.messages.fetch(d.message_id)
if (message === undefined) return
} else return
}
const reaction = await message.reactions.get(d.emoji.id)
if (reaction === undefined) return
await reaction.users.flush()
gateway.client.emit('messageReactionRemoveEmoji', message, reaction.emoji)
}

View file

@ -1,6 +1,5 @@
import { Message } from '../../structures/message.ts' import { Message } from '../../structures/message.ts'
import { TextChannel } from '../../structures/textChannel.ts' import { TextChannel } from '../../structures/textChannel.ts'
import { User } from '../../structures/user.ts'
import { MessagePayload } from '../../types/channel.ts' import { MessagePayload } from '../../types/channel.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
@ -11,19 +10,19 @@ export const messageUpdate: GatewayEventHandler = async (
let channel = await gateway.client.channels.get<TextChannel>(d.channel_id) let channel = await gateway.client.channels.get<TextChannel>(d.channel_id)
// Fetch the channel if not cached // Fetch the channel if not cached
if (channel === undefined) if (channel === undefined)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion channel = await gateway.client.channels.fetch(d.channel_id)
channel = (await gateway.client.channels.fetch(d.channel_id)) as TextChannel
if (channel === undefined) return
const message = await channel.messages.get(d.id) const message = await channel.messages.get(d.id)
const author = if (message === undefined) return
message?.author !== undefined const raw = await channel.messages._get(d.id)
? message.author if (raw === undefined) return
: new User(gateway.client, d.author)
const newMsg = new Message(gateway.client, d, channel, author) const newRaw = raw
if (message === undefined) { Object.assign(newRaw, d)
await channel.messages.set(d.id, d) await channel.messages.set(d.id, newRaw)
return gateway.client.emit('messageUpdateUncached', newMsg)
} const newMsg = ((await channel.messages.get(d.id)) as unknown) as Message
await channel.messages.set(d.id, d)
gateway.client.emit('messageUpdate', message, newMsg) gateway.client.emit('messageUpdate', message, newMsg)
} }

View file

@ -0,0 +1,6 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
export const presenceUpdate: GatewayEventHandler = async (
gateway: Gateway,
d: any
) => {}

View file

@ -1,15 +1,18 @@
import { User } from '../../structures/user.ts' 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 { GuildPayload } from '../../types/guild.ts'
import { Gateway, GatewayEventHandler } from '../index.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() await gateway.client.guilds.flush()
gateway.client.user = new User(gateway.client, d.user) gateway.client.user = new User(gateway.client, d.user)
gateway.sessionID = d.session_id gateway.sessionID = d.session_id
gateway.debug(`Received READY. Session: ${gateway.sessionID}`) 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) => { d.guilds.forEach((guild: GuildPayload) => {
gateway.client.guilds.set(guild.id, guild) gateway.client.guilds.set(guild.id, guild)

View file

@ -1,5 +1,8 @@
import { Gateway , GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
export const reconnect: GatewayEventHandler = async (gateway: Gateway, d: any) => { export const reconnect: GatewayEventHandler = async (
gateway.reconnect() gateway: Gateway,
d: any
) => {
gateway.reconnect()
} }

View file

@ -1,5 +1,5 @@
import { Guild } from "../../structures/guild.ts" import { Guild } from '../../structures/guild.ts'
import { VoiceServerUpdatePayload } from "../../types/gateway.ts" import { VoiceServerUpdatePayload } from '../../types/gateway.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
export const voiceServerUpdate: GatewayEventHandler = async ( export const voiceServerUpdate: GatewayEventHandler = async (
@ -9,6 +9,6 @@ export const voiceServerUpdate: GatewayEventHandler = async (
gateway.client.emit('voiceServerUpdate', { gateway.client.emit('voiceServerUpdate', {
token: d.token, token: d.token,
endpoint: d.endpoint, 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,
}) })
} }

View file

@ -1,6 +1,7 @@
import { Guild } from "../../structures/guild.ts" import { Guild } from '../../structures/guild.ts'
import { VoiceState } from "../../structures/voiceState.ts" import { VoiceState } from '../../structures/voiceState.ts'
import { VoiceStatePayload } from "../../types/voice.ts" import { MemberPayload } from '../../types/guild.ts'
import { VoiceStatePayload } from '../../types/voice.ts'
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
export const voiceStateUpdate: GatewayEventHandler = async ( 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) // TODO(DjDeveloperr): Support self-bot here; they can be in DMs (Call)
if (d.guild_id === undefined) return 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) const voiceState = await guild.voiceStates.get(d.user_id)
if (d.channel_id === null) { 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 // No longer in the channel, so delete
await guild.voiceStates.delete(d.user_id) 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 return
} }
await guild.voiceStates.set(d.user_id, d) await guild.voiceStates.set(d.user_id, d)
const newVoiceState = await guild.voiceStates.get(d.user_id) const newVoiceState = await guild.voiceStates.get(d.user_id)
if (voiceState === undefined) { if (voiceState === undefined) {
gateway.client.emit('voiceStateAdd', (newVoiceState as unknown) as VoiceState) gateway.client.emit(
'voiceStateAdd',
(newVoiceState as unknown) as VoiceState
)
} else { } else {
gateway.client.emit('voiceStateUpdate', voiceState, (newVoiceState as unknown) as VoiceState) gateway.client.emit(
'voiceStateUpdate',
voiceState,
(newVoiceState as unknown) as VoiceState
)
} }
} }

View file

@ -1,7 +1,7 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { WebhooksUpdatePayload } from "../../types/gateway.ts" import { WebhooksUpdatePayload } from '../../types/gateway.ts'
import { GuildTextChannel } from "../../structures/textChannel.ts" import { GuildTextChannel } from '../../structures/textChannel.ts'
export const webhooksUpdate: GatewayEventHandler = async ( export const webhooksUpdate: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
@ -10,7 +10,10 @@ export const webhooksUpdate: GatewayEventHandler = async (
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id) const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
if (guild === undefined) return if (guild === undefined) return
const channel: GuildTextChannel | undefined = await guild.channels.get(d.channel_id) as GuildTextChannel const channel: GuildTextChannel | undefined = (await guild.channels.get(
if (channel === undefined) gateway.client.emit('webhooksUpdateUncached', guild, d.channel_id) d.channel_id
)) as GuildTextChannel
if (channel === undefined)
gateway.client.emit('webhooksUpdateUncached', guild, d.channel_id)
else gateway.client.emit('webhooksUpdate', guild, channel) else gateway.client.emit('webhooksUpdate', guild, channel)
} }

View file

@ -1,30 +1,48 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { Guild } from "../structures/guild.ts" import { Guild } from '../structures/guild.ts'
import { VoiceChannel } from "../structures/guildVoiceChannel.ts" import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
import { User } from "../structures/user.ts" import { User } from '../structures/user.ts'
import { VoiceState } from "../structures/voiceState.ts" import { VoiceState } from '../structures/voiceState.ts'
import { VoiceStatePayload } from "../types/voice.ts" import { VoiceStatePayload } from '../types/voice.ts'
import { BaseManager } from './base.ts' import { BaseManager } from './base.ts'
export class GuildVoiceStatesManager extends BaseManager<VoiceStatePayload, VoiceState> { export class GuildVoiceStatesManager extends BaseManager<
VoiceStatePayload,
VoiceState
> {
guild: Guild guild: Guild
async get (key: string): Promise<VoiceState | undefined> { constructor(client: Client, guild: Guild) {
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<VoiceChannel>(raw.channel_id) as unknown) as VoiceChannel,
guild,
member: guild === undefined ? undefined : await guild.members.get(raw.user_id)
})
}
constructor (client: Client, guild: Guild) {
super(client, `vs:${guild.id}`, VoiceState) super(client, `vs:${guild.id}`, VoiceState)
this.guild = guild this.guild = guild
} }
async get(key: string): Promise<VoiceState | undefined> {
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<VoiceChannel>(
raw.channel_id
)) as unknown) as VoiceChannel),
guild,
member:
guild === undefined ? undefined : await guild.members.get(raw.user_id),
})
}
async fromPayload(d: VoiceStatePayload[]): Promise<void> {
for (const data of d) {
await this.set(data.user_id, data)
}
}
} }

View file

@ -0,0 +1,44 @@
import { Client } from '../models/client.ts'
import { Emoji } from '../structures/emoji.ts'
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 { BaseManager } from './base.ts'
export class MessageReactionsManager extends BaseManager<
Reaction,
MessageReaction
> {
message: Message
constructor(client: Client, message: Message) {
super(client, `reactions:${message.id}`, Guild)
this.message = message
}
async get(id: string): Promise<MessageReaction | undefined> {
const raw = await this._get(id)
if (raw === undefined) return
let emoji = await this.client.emojis.get(raw.emoji.id)
if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji)
const reaction = new MessageReaction(this.client, raw, this.message, emoji)
return reaction
}
async set(key: string, value: Reaction): Promise<any> {
return this.client.cache.set(
this.cacheName,
key,
value,
this.client.reactionCacheLifetime
)
}
async flush(): Promise<any> {
await this.client.cache.deleteCache(`reaction_users:${this.message.id}`)
return this.client.cache.deleteCache(this.cacheName)
}
}

View file

@ -9,35 +9,46 @@ import { BaseManager } from './base.ts'
export class MessagesManager extends BaseManager<MessagePayload, Message> { export class MessagesManager extends BaseManager<MessagePayload, Message> {
channel: TextChannel channel: TextChannel
constructor (client: Client, channel: TextChannel) { constructor(client: Client, channel: TextChannel) {
super(client, 'messages', Message) super(client, 'messages', Message)
this.channel = channel this.channel = channel
} }
async get (key: string): Promise<Message | undefined> { async get(key: string): Promise<Message | undefined> {
const raw = await this._get(key) const raw = await this._get(key)
if (raw === undefined) return if (raw === undefined) return
if (raw.author === undefined) return
let channel = await this.client.channels.get(raw.channel_id) let channel = await this.client.channels.get(raw.channel_id)
if (channel === undefined) if (channel === undefined)
channel = await this.client.channels.fetch(raw.channel_id) channel = await this.client.channels.fetch(raw.channel_id)
const author = new User(this.client, raw.author) 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 const res = new this.DataType(this.client, raw, channel, author) as any
await res.mentions.fromPayload(raw) await res.mentions.fromPayload(raw)
return res return res
} }
async set (key: string, value: MessagePayload): Promise<any> { async set(key: string, value: MessagePayload): Promise<any> {
return this.client.cache.set(this.cacheName, key, value, this.client.messageCacheLifetime) return this.client.cache.set(
this.cacheName,
key,
value,
this.client.messageCacheLifetime
)
} }
async fetch (id: string): Promise<Message> { async fetch(id: string): Promise<Message> {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
this.client.rest this.client.rest
.get(CHANNEL_MESSAGE(this.channel.id, id)) .get(CHANNEL_MESSAGE(this.channel.id, id))
.then(async data => { .then(async (data) => {
await this.set(id, data as MessagePayload) await this.set(id, data as MessagePayload)
let channel: any = await this.client.channels.get<TextChannel>( let channel: any = await this.client.channels.get<TextChannel>(
@ -63,7 +74,7 @@ export class MessagesManager extends BaseManager<MessagePayload, Message> {
resolve(res) resolve(res)
}) })
.catch(e => reject(e)) .catch((e) => reject(e))
}) })
} }
} }

View file

@ -0,0 +1,13 @@
import { Client } from '../models/client.ts'
import { MessageReaction } from '../structures/messageReaction.ts'
import { UserManager } from './users.ts'
export class ReactionUsersManager extends UserManager {
reaction: MessageReaction
constructor(client: Client, reaction: MessageReaction) {
super(client)
this.cacheName = `reaction_users:${reaction.message.id}`
this.reaction = reaction
}
}

View file

@ -7,12 +7,10 @@ import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts'
import { UserManager } from '../managers/users.ts' import { UserManager } from '../managers/users.ts'
import { GuildManager } from '../managers/guilds.ts' import { GuildManager } from '../managers/guilds.ts'
import { ChannelsManager } from '../managers/channels.ts' import { ChannelsManager } from '../managers/channels.ts'
import { import { ClientPresence } from '../structures/presence.ts'
ClientPresence
} from '../structures/presence.ts'
import { EmojisManager } from '../managers/emojis.ts' import { EmojisManager } from '../managers/emojis.ts'
import { ActivityGame, ClientActivity } from "../types/presence.ts" import { ActivityGame, ClientActivity } from '../types/presence.ts'
import { ClientEvents } from "../gateway/handlers/index.ts" import { ClientEvents } from '../gateway/handlers/index.ts'
// import { Application } from "../../mod.ts" // import { Application } from "../../mod.ts"
/** Some Client Options to modify behaviour */ /** Some Client Options to modify behaviour */
@ -22,9 +20,9 @@ export interface ClientOptions {
/** Gateway Intents */ /** Gateway Intents */
intents?: GatewayIntents[] intents?: GatewayIntents[]
/** Cache Adapter to use, defaults to Collections one */ /** Cache Adapter to use, defaults to Collections one */
cache?: ICacheAdapter, cache?: ICacheAdapter
/** Force New Session and don't use cached Session (by persistent caching) */ /** Force New Session and don't use cached Session (by persistent caching) */
forceNewSession?: boolean, forceNewSession?: boolean
/** Startup presence of client */ /** Startup presence of client */
presence?: ClientPresence | ClientActivity | ActivityGame presence?: ClientPresence | ClientActivity | ActivityGame
/** Whether it's a bot user or not? Use this if selfbot! */ /** Whether it's a bot user or not? Use this if selfbot! */
@ -33,19 +31,21 @@ export interface ClientOptions {
canary?: boolean canary?: boolean
/** Time till which Messages are to be cached, in MS. Default is 3600000 */ /** Time till which Messages are to be cached, in MS. Default is 3600000 */
messageCacheLifetime?: number messageCacheLifetime?: number
/** Time till which Message Reactions are to be cached, in MS. Default is 3600000 */
reactionCacheLifetime?: number
/** Whether to fetch Uncached Message of Reaction or not? */
fetchUncachedReactions?: boolean
} }
export declare interface Client { export declare interface Client {
on: <U extends string>( on: <U extends string>(event: U, listener: ClientEvents[U]) => this
event: U, listener: ClientEvents[U]
) => this
emit: <U extends string>( emit: <U extends string>(
event: U, ...args: Parameters<ClientEvents[U]> event: U,
...args: Parameters<ClientEvents[U]>
) => boolean ) => boolean
} }
/** /**
* Discord Client. * Discord Client.
*/ */
@ -68,6 +68,10 @@ export class Client extends EventEmitter {
forceNewSession?: boolean forceNewSession?: boolean
/** Time till messages to stay cached, in MS. */ /** Time till messages to stay cached, in MS. */
messageCacheLifetime: number = 3600000 messageCacheLifetime: number = 3600000
/** Time till messages to stay cached, in MS. */
reactionCacheLifetime: number = 3600000
/** Whether to fetch Uncached Message of Reaction or not? */
fetchUncachedReactions: boolean = false
users: UserManager = new UserManager(this) users: UserManager = new UserManager(this)
guilds: GuildManager = new GuildManager(this) guilds: GuildManager = new GuildManager(this)
@ -81,7 +85,7 @@ export class Client extends EventEmitter {
/** Client's presence. Startup one if set before connecting */ /** Client's presence. Startup one if set before connecting */
presence: ClientPresence = new ClientPresence() presence: ClientPresence = new ClientPresence()
constructor (options: ClientOptions = {}) { constructor(options: ClientOptions = {}) {
super() super()
this.token = options.token this.token = options.token
this.intents = options.intents this.intents = options.intents
@ -94,17 +98,22 @@ export class Client extends EventEmitter {
: new ClientPresence(options.presence) : new ClientPresence(options.presence)
if (options.bot === false) this.bot = false if (options.bot === false) this.bot = false
if (options.canary === true) this.canary = true if (options.canary === true) this.canary = true
if (options.messageCacheLifetime !== undefined) this.messageCacheLifetime = options.messageCacheLifetime if (options.messageCacheLifetime !== undefined)
this.messageCacheLifetime = options.messageCacheLifetime
if (options.reactionCacheLifetime !== undefined)
this.reactionCacheLifetime = options.reactionCacheLifetime
if (options.fetchUncachedReactions === true)
this.fetchUncachedReactions = true
} }
/** Set Cache Adapter */ /** Set Cache Adapter */
setAdapter (adapter: ICacheAdapter): Client { setAdapter(adapter: ICacheAdapter): Client {
this.cache = adapter this.cache = adapter
return this return this
} }
/** Change Presence of Client */ /** Change Presence of Client */
setPresence (presence: ClientPresence | ClientActivity | ActivityGame): void { setPresence(presence: ClientPresence | ClientActivity | ActivityGame): void {
if (presence instanceof ClientPresence) { if (presence instanceof ClientPresence) {
this.presence = presence this.presence = presence
} else this.presence = new ClientPresence(presence) } else this.presence = new ClientPresence(presence)
@ -112,7 +121,7 @@ export class Client extends EventEmitter {
} }
/** Emit debug event */ /** Emit debug event */
debug (tag: string, msg: string): void { debug(tag: string, msg: string): void {
this.emit('debug', `[${tag}] ${msg}`) this.emit('debug', `[${tag}] ${msg}`)
} }
@ -124,7 +133,7 @@ export class Client extends EventEmitter {
* @param token Your token. This is required. * @param token Your token. This is required.
* @param intents Gateway intents in array. This is required. * @param intents Gateway intents in array. This is required.
*/ */
connect (token?: string, intents?: GatewayIntents[]): void { connect(token?: string, intents?: GatewayIntents[]): void {
if (token === undefined && this.token !== undefined) token = this.token if (token === undefined && this.token !== undefined) token = this.token
else if (this.token === undefined && token !== undefined) { else if (this.token === undefined && token !== undefined) {
this.token = token this.token = token

View file

@ -4,7 +4,7 @@ import { TextChannel } from '../structures/textChannel.ts'
import { User } from '../structures/user.ts' import { User } from '../structures/user.ts'
import { Collection } from '../utils/collection.ts' import { Collection } from '../utils/collection.ts'
import { CommandClient } from './commandClient.ts' import { CommandClient } from './commandClient.ts'
import { Extension } from "./extensions.ts" import { Extension } from './extensions.ts'
export interface CommandContext { export interface CommandContext {
/** The Client object */ /** The Client object */
@ -34,6 +34,8 @@ export class Command {
name: string = '' name: string = ''
/** Description of the Command */ /** Description of the Command */
description?: string description?: string
/** Category of the Command */
category?: string
/** Array of Aliases of Command, or only string */ /** Array of Aliases of Command, or only string */
aliases?: string | string[] aliases?: string | string[]
/** Extension (Parent) of the Command */ /** Extension (Parent) of the Command */
@ -42,10 +44,12 @@ export class Command {
usage?: string | string[] usage?: string | string[]
/** Usage Example of Command, only Arguments (without Prefix and Name) */ /** Usage Example of Command, only Arguments (without Prefix and Name) */
examples?: string | string[] examples?: string | string[]
/** Does the Command take Arguments? Maybe number of required arguments? */ /** Does the Command take Arguments? Maybe number of required arguments? Or list of arguments? */
args?: number | boolean args?: number | boolean | string[]
/** Permission(s) required for using Command */ /** Permissions(s) required by both User and Bot in order to use Command */
permissions?: string | string[] permissions?: string | string[]
/** Permission(s) required for using Command */
userPermissions?: string | string[]
/** Permission(s) bot will need in order to execute Command */ /** Permission(s) bot will need in order to execute Command */
botPermissions?: string | string[] botPermissions?: string | string[]
/** Role(s) user will require in order to use Command. List or one of ID or name */ /** 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 ownerOnly?: boolean
/** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */
beforeExecute(ctx: CommandContext): boolean | Promise<boolean> { return true } beforeExecute(ctx: CommandContext): boolean | Promise<boolean> {
return true
}
/** Actual command code, which is executed when all checks have passed. */ /** 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) */ /** 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 { 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 */ /** 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 a Command by name/alias */
find(search: string): Command | undefined { find(search: string): Command | undefined {
@ -99,7 +280,7 @@ export class CommandsManager {
if (typeof cmd.aliases === 'string') aliases = [cmd.aliases] if (typeof cmd.aliases === 'string') aliases = [cmd.aliases]
else aliases = cmd.aliases else aliases = cmd.aliases
if (this.client.caseSensitive === false) if (this.client.caseSensitive === false)
aliases = aliases.map(e => e.toLowerCase()) aliases = aliases.map((e) => e.toLowerCase())
return aliases.includes(search) return aliases.includes(search)
} else return false } else return false
}) })
@ -123,8 +304,9 @@ export class CommandsManager {
const aliases: string[] = const aliases: string[] =
typeof search.aliases === 'string' ? [search.aliases] : search.aliases typeof search.aliases === 'string' ? [search.aliases] : search.aliases
exists = exists =
aliases.map(alias => this.find(alias) !== undefined).find(e => e) ?? aliases
false .map((alias) => this.find(alias) !== undefined)
.find((e) => e) ?? false
} }
return exists return exists
} }
@ -134,7 +316,10 @@ export class CommandsManager {
add(cmd: Command | typeof Command): boolean { add(cmd: Command | typeof Command): boolean {
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
if (!(cmd instanceof Command)) cmd = new cmd() 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) this.list.set(cmd.name, cmd)
return true return true
} }
@ -148,7 +333,7 @@ export class CommandsManager {
/** Check whether a Command is disabled or not */ /** Check whether a Command is disabled or not */
isDisabled(name: string | Command): boolean { 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 if (cmd === undefined) return false
const exists = this.exists(name) const exists = this.exists(name)
if (!exists) return false if (!exists) return false
@ -157,12 +342,65 @@ export class CommandsManager {
/** Disable a Command */ /** Disable a Command */
disable(name: string | Command): boolean { 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 (cmd === undefined) return false
if (this.isDisabled(cmd)) return false if (this.isDisabled(cmd)) return false
this.disabled.add(cmd.name) this.disabled.add(cmd.name)
return true return true
} }
/** Get all commands of a Category */
category(category: string): Collection<string, Command> {
return this.list.filter(
(cmd) => cmd.category !== undefined && cmd.category === category
)
}
}
export class CategoriesManager {
client: CommandClient
list: Collection<string, CommandCategory> = new Collection()
constructor(client: CommandClient) {
this.client = client
}
/** Get a Collection of Categories */
all(): Collection<string, CommandCategory> {
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 { export interface ParsedCommand {
@ -185,6 +423,6 @@ export const parseCommand = (
return { return {
name, name,
args, args,
argString argString,
} }
} }

View file

@ -1,8 +1,13 @@
import { Message } from "../../mod.ts" import { Message } from '../../mod.ts'
import { awaitSync } from "../utils/mixedPromise.ts" import { awaitSync } from '../utils/mixedPromise.ts'
import { Client, ClientOptions } from './client.ts' import { Client, ClientOptions } from './client.ts'
import { CommandContext, CommandsManager, parseCommand } from './command.ts' import {
import { ExtensionsManager } from "./extensions.ts" CategoriesManager,
CommandContext,
CommandsManager,
parseCommand,
} from './command.ts'
import { ExtensionsManager } from './extensions.ts'
type PrefixReturnType = string | string[] | Promise<string | string[]> type PrefixReturnType = string | string[] | Promise<string | string[]>
@ -38,6 +43,7 @@ export class CommandClient extends Client implements CommandClientOptions {
caseSensitive: boolean caseSensitive: boolean
extensions: ExtensionsManager = new ExtensionsManager(this) extensions: ExtensionsManager = new ExtensionsManager(this)
commands: CommandsManager = new CommandsManager(this) commands: CommandsManager = new CommandsManager(this)
categories: CategoriesManager = new CategoriesManager(this)
constructor(options: CommandClientOptions) { constructor(options: CommandClientOptions) {
super(options) super(options)
@ -85,14 +91,20 @@ export class CommandClient extends Client implements CommandClientOptions {
async processMessage(msg: Message): Promise<any> { async processMessage(msg: Message): Promise<any> {
if (!this.allowBots && msg.author.bot === true) return if (!this.allowBots && msg.author.bot === true) return
const isUserBlacklisted = await awaitSync(this.isUserBlacklisted(msg.author.id)) const isUserBlacklisted = await awaitSync(
this.isUserBlacklisted(msg.author.id)
)
if (isUserBlacklisted === true) return if (isUserBlacklisted === true) return
const isChannelBlacklisted = await awaitSync(this.isChannelBlacklisted(msg.channel.id)) const isChannelBlacklisted = await awaitSync(
this.isChannelBlacklisted(msg.channel.id)
)
if (isChannelBlacklisted === true) return if (isChannelBlacklisted === true) return
if (msg.guild !== undefined) { if (msg.guild !== undefined) {
const isGuildBlacklisted = await awaitSync(this.isGuildBlacklisted(msg.guild.id)) const isGuildBlacklisted = await awaitSync(
this.isGuildBlacklisted(msg.guild.id)
)
if (isGuildBlacklisted === true) return if (isGuildBlacklisted === true) return
} }
@ -112,17 +124,20 @@ export class CommandClient extends Client implements CommandClientOptions {
else return else return
} }
} else { } else {
const usedPrefix = prefix.find(v => msg.content.startsWith(v)) const usedPrefix = prefix.find((v) => msg.content.startsWith(v))
if (usedPrefix === undefined) { if (usedPrefix === undefined) {
if (this.mentionPrefix) mentionPrefix = true if (this.mentionPrefix) mentionPrefix = true
else return else return
} } else prefix = usedPrefix
else prefix = usedPrefix
} }
if (mentionPrefix) { if (mentionPrefix) {
if (msg.content.startsWith(this.user?.mention as string) === true) prefix = this.user?.mention as string if (msg.content.startsWith(this.user?.mention as string) === true)
else if (msg.content.startsWith(this.user?.nickMention as string) === true) prefix = this.user?.nickMention as string 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 else return
} }
@ -132,10 +147,52 @@ export class CommandClient extends Client implements CommandClientOptions {
const command = this.commands.find(parsed.name) const command = this.commands.find(parsed.name)
if (command === undefined) return if (command === undefined) return
const category =
command.category !== undefined
? this.categories.get(command.category)
: undefined
if (command.whitelistedGuilds !== undefined && msg.guild !== undefined && command.whitelistedGuilds.includes(msg.guild.id) === false) return; // Guild whitelist exists, and if does and Command used in a Guild, is this Guild allowed?
if (command.whitelistedChannels !== undefined && command.whitelistedChannels.includes(msg.channel.id) === false) return; // This is a bit confusing here, if these settings on a Command exist, and also do on Category, Command overrides them
if (command.whitelistedUsers !== undefined && command.whitelistedUsers.includes(msg.author.id) === false) return; 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 = { const ctx: CommandContext = {
client: this, client: this,
@ -147,40 +204,117 @@ export class CommandClient extends Client implements CommandClientOptions {
author: msg.author, author: msg.author,
command, command,
channel: msg.channel, channel: msg.channel,
guild: msg.guild guild: msg.guild,
} }
if (command.ownerOnly === true && !this.owners.includes(msg.author.id)) return this.emit('commandOwnerOnly', ctx, command) // In these checks too, Command overrides Category if present
if (command.guildOnly === true && msg.guild === undefined) return this.emit('commandGuildOnly', ctx, command) // Check if Command is only for Owners
if (command.dmOnly === true && msg.guild !== undefined) return this.emit('commandDmOnly', ctx, command) if (
(command.ownerOnly !== undefined || category === undefined
? command.ownerOnly
: category.ownerOnly) === true &&
!this.owners.includes(msg.author.id)
)
return this.emit('commandOwnerOnly', ctx, command)
if (command.botPermissions !== undefined && msg.guild !== undefined) { // 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 // TODO: Check Overwrites too
const me = await msg.guild.me() const me = await msg.guild.me()
const missing: string[] = [] const missing: string[] = []
for (const perm of command.botPermissions) { let permissions =
if (me.permissions.has(perm) === false) missing.push(perm) command.botPermissions === undefined
} ? category?.permissions
: command.botPermissions
if (missing.length !== 0) return this.emit('commandBotMissingPermissions', ctx, command, missing) 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.permissions !== undefined && msg.guild !== undefined) { if (
const missing: string[] = [] (command.userPermissions !== undefined ||
let perms: string[] = [] category?.userPermissions !== undefined) &&
if (typeof command.permissions === 'string') perms = [command.permissions] msg.guild !== undefined
else perms = command.permissions ) {
for (const perm of perms) { let permissions =
const has = msg.member?.permissions.has(perm) command.userPermissions !== undefined
if (has !== true) missing.push(perm) ? 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 (missing.length !== 0) return this.emit('commandMissingPermissions', command, missing, ctx)
} }
if (command.args !== undefined) { if (command.args !== undefined) {
if (typeof command.args === 'boolean' && parsed.args.length === 0) return this.emit('commandMissingArgs', ctx, command) if (typeof command.args === 'boolean' && parsed.args.length === 0)
else if (typeof command.args === 'number' && parsed.args.length < command.args) this.emit('commandMissingArgs', ctx, command) return this.emit('commandMissingArgs', ctx, command)
else if (
typeof command.args === 'number' &&
parsed.args.length < command.args
)
this.emit('commandMissingArgs', ctx, command)
} }
try { try {

View file

@ -7,7 +7,6 @@ import {
MessageOption, MessageOption,
MessagePayload, MessagePayload,
MessageReference, MessageReference,
Reaction
} from '../types/channel.ts' } from '../types/channel.ts'
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { User } from './user.ts' import { User } from './user.ts'
@ -17,6 +16,7 @@ import { CHANNEL_MESSAGE } from '../types/endpoint.ts'
import { MessageMentions } from './messageMentions.ts' import { MessageMentions } from './messageMentions.ts'
import { TextChannel } from './textChannel.ts' import { TextChannel } from './textChannel.ts'
import { Guild } from './guild.ts' import { Guild } from './guild.ts'
import { MessageReactionsManager } from '../managers/messageReactions.ts'
type AllMessageOptions = MessageOption | Embed type AllMessageOptions = MessageOption | Embed
@ -38,7 +38,7 @@ export class Message extends Base {
mentionChannels?: ChannelMention[] mentionChannels?: ChannelMention[]
attachments: Attachment[] attachments: Attachment[]
embeds: Embed[] embeds: Embed[]
reactions?: Reaction[] reactions: MessageReactionsManager
nonce?: string | number nonce?: string | number
pinned: boolean pinned: boolean
webhookID?: string webhookID?: string
@ -48,7 +48,7 @@ export class Message extends Base {
messageReference?: MessageReference messageReference?: MessageReference
flags?: number flags?: number
constructor ( constructor(
client: Client, client: Client,
data: MessagePayload, data: MessagePayload,
channel: TextChannel, channel: TextChannel,
@ -68,8 +68,8 @@ export class Message extends Base {
this.mentionRoles = data.mention_roles this.mentionRoles = data.mention_roles
this.mentionChannels = data.mention_channels this.mentionChannels = data.mention_channels
this.attachments = data.attachments this.attachments = data.attachments
this.embeds = data.embeds.map(v => new Embed(v)) this.embeds = data.embeds.map((v) => new Embed(v))
this.reactions = data.reactions this.reactions = new MessageReactionsManager(this.client, this)
this.nonce = data.nonce this.nonce = data.nonce
this.pinned = data.pinned this.pinned = data.pinned
this.webhookID = data.webhook_id this.webhookID = data.webhook_id
@ -81,7 +81,7 @@ export class Message extends Base {
this.channel = channel this.channel = channel
} }
protected readFromData (data: MessagePayload): void { protected readFromData(data: MessagePayload): void {
super.readFromData(data) super.readFromData(data)
this.channelID = data.channel_id ?? this.channelID this.channelID = data.channel_id ?? this.channelID
this.guildID = data.guild_id ?? this.guildID this.guildID = data.guild_id ?? this.guildID
@ -93,8 +93,7 @@ export class Message extends Base {
this.mentionRoles = data.mention_roles ?? this.mentionRoles this.mentionRoles = data.mention_roles ?? this.mentionRoles
this.mentionChannels = data.mention_channels ?? this.mentionChannels this.mentionChannels = data.mention_channels ?? this.mentionChannels
this.attachments = data.attachments ?? this.attachments this.attachments = data.attachments ?? this.attachments
this.embeds = data.embeds.map(v => new Embed(v)) ?? this.embeds this.embeds = data.embeds.map((v) => new Embed(v)) ?? this.embeds
this.reactions = data.reactions ?? this.reactions
this.nonce = data.nonce ?? this.nonce this.nonce = data.nonce ?? this.nonce
this.pinned = data.pinned ?? this.pinned this.pinned = data.pinned ?? this.pinned
this.webhookID = data.webhook_id ?? this.webhookID this.webhookID = data.webhook_id ?? this.webhookID
@ -105,16 +104,26 @@ export class Message extends Base {
this.flags = data.flags ?? this.flags this.flags = data.flags ?? this.flags
} }
async edit (text?: string, option?: MessageOption): Promise<Message> { /** Edit this message. */
async edit(text?: string, option?: MessageOption): Promise<Message> {
if (
this.client.user !== undefined &&
this.author.id !== this.client.user?.id
)
throw new Error("Cannot edit other users' messages")
return this.channel.editMessage(this.id, text, option) return this.channel.editMessage(this.id, text, option)
} }
/** These will **not** work in all servers, as this feature is coming slowly. */ /** Create a Reply to this Message. */
async reply(text?: string | AllMessageOptions, option?: AllMessageOptions): Promise<Message> { async reply(
text?: string | AllMessageOptions,
option?: AllMessageOptions
): Promise<Message> {
return this.channel.send(text, option, this) return this.channel.send(text, option, this)
} }
async delete (): Promise<void> { /** Delete the Message. */
async delete(): Promise<void> {
return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id))
} }
} }

View file

@ -1,10 +1,10 @@
import { Client } from "../models/client.ts"; import { Client } from '../models/client.ts'
import { MessagePayload } from "../types/channel.ts"; import { MessagePayload } from '../types/channel.ts'
import { Collection } from "../utils/collection.ts"; import { Collection } from '../utils/collection.ts'
import { GuildTextChannel } from "./textChannel.ts"; import { GuildTextChannel } from './textChannel.ts'
import { Message } from "./message.ts"; import { Message } from './message.ts'
import { Role } from "./role.ts"; import { Role } from './role.ts'
import { User } from "./user.ts"; import { User } from './user.ts'
export class MessageMentions { export class MessageMentions {
client: Client client: Client
@ -25,27 +25,35 @@ export class MessageMentions {
} }
async fromPayload(payload: MessagePayload): Promise<MessageMentions> { async fromPayload(payload: MessagePayload): Promise<MessageMentions> {
payload.mentions.forEach(rawUser => { if (this.message === undefined) return this
this.users.set(rawUser.id, new User(this.client, rawUser)) if (payload.mentions !== undefined)
}) payload.mentions.forEach((rawUser) => {
this.users.set(rawUser.id, new User(this.client, rawUser))
})
if (this.message.guild !== undefined) { if (this.message.guild !== undefined) {
for (const id of payload.mention_roles) { for (const id of payload.mention_roles) {
const role = await this.message.guild.roles.get(id) const role = await this.message.guild.roles.get(id)
if(role !== undefined) this.roles.set(role.id, role) if (role !== undefined) this.roles.set(role.id, role)
} }
} }
if (payload.mention_channels !== undefined) { if (payload.mention_channels !== undefined) {
for (const mentionChannel of payload.mention_channels) { for (const mentionChannel of payload.mention_channels) {
const channel = await this.client.channels.get<GuildTextChannel>(mentionChannel.id) const channel = await this.client.channels.get<GuildTextChannel>(
mentionChannel.id
)
if (channel !== undefined) this.channels.set(channel.id, channel) if (channel !== undefined) this.channels.set(channel.id, channel)
} }
} }
const matchChannels = this.message.content.match(MessageMentions.CHANNEL_MENTION) const matchChannels = this.message.content.match(
MessageMentions.CHANNEL_MENTION
)
if (matchChannels !== null) { if (matchChannels !== null) {
for (const id of matchChannels) { for (const id of matchChannels) {
const parsedID = id.substr(2, id.length - 3) const parsedID = id.substr(2, id.length - 3)
const channel = await this.client.channels.get<GuildTextChannel>(parsedID) const channel = await this.client.channels.get<GuildTextChannel>(
parsedID
)
if (channel !== undefined) this.channels.set(channel.id, channel) if (channel !== undefined) this.channels.set(channel.id, channel)
} }
} }

View file

@ -0,0 +1,28 @@
import { ReactionUsersManager } from '../managers/reactionUsers.ts'
import { Client } from '../models/client.ts'
import { Reaction } from '../types/channel.ts'
import { Base } from './base.ts'
import { Emoji } from './emoji.ts'
import { Message } from './message.ts'
export class MessageReaction extends Base {
message: Message
count: number = 0
emoji: Emoji
me: boolean = false
users: ReactionUsersManager
constructor(client: Client, data: Reaction, message: Message, emoji: Emoji) {
super(client, data)
this.message = message
this.emoji = emoji
this.count = data.count
this.me = data.me
this.users = new ReactionUsersManager(client, this)
}
fromPayload(data: Reaction): void {
this.count = data.count
this.me = data.me
}
}

View file

@ -63,6 +63,7 @@ client.on('webhooksUpdate', (guild, channel) => {
console.log(`Webhooks Updated in #${channel.name} from ${guild.name}`) console.log(`Webhooks Updated in #${channel.name} from ${guild.name}`)
}) })
client.on('commandError', console.error)
client.on('inviteCreate', (invite: Invite) => { client.on('inviteCreate', (invite: Invite) => {
console.log(`Invite Create: ${invite.code}`) console.log(`Invite Create: ${invite.code}`)
}) })
@ -131,6 +132,22 @@ client.on('voiceStateRemove', (state) => {
console.log('VC Leave', state) console.log('VC Leave', state)
}) })
client.on('messageReactionAdd', (reaction, user) => {
console.log(`${user.tag} reacted with ${reaction.emoji.name}`)
})
client.on('messageReactionRemove', (reaction, user) => {
console.log(`${user.tag} removed reaction ${reaction.emoji.name}`)
})
client.on('messageReactionRemoveEmoji', (message, emoji) => {
console.log(`All ${emoji.name} emoji reactions removed from ${message.id}`)
})
client.on('messageReactionRemoveAll', (message) => {
console.log(`All reactions remove from Message: ${message.id}`)
})
// client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`)) // client.on('raw', (evt: string) => console.log(`EVENT: ${evt}`))
const files = Deno.readDirSync('./src/test/cmds') const files = Deno.readDirSync('./src/test/cmds')

View file

@ -1,14 +1,14 @@
// https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway // https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway
// https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events
import { Guild } from "../structures/guild.ts" import { Guild } from '../structures/guild.ts'
import { Member } from "../structures/member.ts" import { Member } from '../structures/member.ts'
import { EmojiPayload } from './emoji.ts' import { EmojiPayload } from './emoji.ts'
import { MemberPayload } from './guild.ts' import { MemberPayload } from './guild.ts'
import { import {
ActivityGame, ActivityGame,
ActivityPayload, ActivityPayload,
StatusType, StatusType,
ClientStatus ClientStatus,
} from './presence.ts' } from './presence.ts'
import { RolePayload } from './role.ts' import { RolePayload } from './role.ts'
import { UserPayload } from './user.ts' import { UserPayload } from './user.ts'
@ -27,7 +27,7 @@ export enum GatewayOpcodes { // 문서를 확인해본 결과 Opcode 5번은 비
REQUEST_GUILD_MEMBERS = 8, REQUEST_GUILD_MEMBERS = 8,
INVALID_SESSION = 9, INVALID_SESSION = 9,
HELLO = 10, HELLO = 10,
HEARTBEAT_ACK = 11 HEARTBEAT_ACK = 11,
} }
/** /**
@ -47,7 +47,7 @@ export enum GatewayCloseCodes {
SHARDING_REQUIRED = 4011, SHARDING_REQUIRED = 4011,
INVALID_API_VERSION = 4012, INVALID_API_VERSION = 4012,
INVALID_INTENTS = 4013, INVALID_INTENTS = 4013,
DISALLOWED_INTENTS = 4014 DISALLOWED_INTENTS = 4014,
} }
export enum GatewayIntents { export enum GatewayIntents {
@ -65,7 +65,7 @@ export enum GatewayIntents {
GUILD_MESSAGE_TYPING = 1 << 11, GUILD_MESSAGE_TYPING = 1 << 11,
DIRECT_MESSAGES = 1 << 12, DIRECT_MESSAGES = 1 << 12,
DIRECT_MESSAGE_REACTIONS = 1 << 13, DIRECT_MESSAGE_REACTIONS = 1 << 13,
DIRECT_MESSAGE_TYPING = 1 << 13 DIRECT_MESSAGE_TYPING = 1 << 13,
} }
export enum GatewayEvents { export enum GatewayEvents {
@ -105,7 +105,7 @@ export enum GatewayEvents {
User_Update = 'USER_UPDATE', User_Update = 'USER_UPDATE',
Voice_Server_Update = 'VOICE_SERVER_UPDATE', Voice_Server_Update = 'VOICE_SERVER_UPDATE',
Voice_State_Update = 'VOICE_STATE_UPDATE', Voice_State_Update = 'VOICE_STATE_UPDATE',
Webhooks_Update = 'WEBHOOKS_UPDATE' Webhooks_Update = 'WEBHOOKS_UPDATE',
} }
export interface IdentityPayload { export interface IdentityPayload {
@ -290,6 +290,13 @@ export interface MessageReactionRemoveAllPayload {
message_id: string message_id: string
} }
export interface MessageReactionRemoveEmojiPayload {
channel_id: string
message_id: string
guild_id?: string
emoji: EmojiPayload
}
export interface PresenceUpdatePayload { export interface PresenceUpdatePayload {
user: UserPayload user: UserPayload
guild_id: string guild_id: string