From c7ef17a6d25ed44b46763d293a027541280261ac Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 2 Dec 2020 15:29:15 +0530 Subject: [PATCH] feat(reactions): implemented structures and events --- src/gateway/handlers/index.ts | 18 +- src/gateway/handlers/messageReactionAdd.ts | 51 ++++ src/gateway/handlers/messageReactionRemove.ts | 36 +++ .../handlers/messageReactionRemoveAll.ts | 25 ++ .../handlers/messageReactionRemoveEmoji.ts | 28 ++ src/gateway/handlers/presenceUpdate.ts | 6 + src/managers/messageReactions.ts | 44 ++++ src/managers/reactionUsers.ts | 13 + src/models/client.ts | 47 ++-- src/structures/message.ts | 249 +++++++++--------- src/structures/messageReaction.ts | 28 ++ src/test/cmd.ts | 16 ++ src/types/gateway.ts | 23 +- 13 files changed, 431 insertions(+), 153 deletions(-) create mode 100644 src/gateway/handlers/messageReactionAdd.ts create mode 100644 src/gateway/handlers/messageReactionRemove.ts create mode 100644 src/gateway/handlers/messageReactionRemoveAll.ts create mode 100644 src/gateway/handlers/messageReactionRemoveEmoji.ts create mode 100644 src/gateway/handlers/presenceUpdate.ts create mode 100644 src/managers/messageReactions.ts create mode 100644 src/managers/reactionUsers.ts create mode 100644 src/structures/messageReaction.ts diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 9bfecf6..2168667 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -39,6 +39,12 @@ import { Collection } from '../../utils/collection.ts' import { voiceServerUpdate } from './voiceServerUpdate.ts' import { voiceStateUpdate } from './voiceStateUpdate.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' export const gatewayHandlers: { [eventCode in GatewayEvents]: GatewayEventHandler | undefined @@ -60,7 +66,7 @@ export const gatewayHandlers: { GUILD_MEMBER_ADD: guildMemberAdd, GUILD_MEMBER_REMOVE: guildMemberRemove, GUILD_MEMBER_UPDATE: guildMemberUpdate, - GUILD_MEMBERS_CHUNK: undefined, + GUILD_MEMBERS_CHUNK: guildMembersChunk, GUILD_ROLE_CREATE: guildRoleCreate, GUILD_ROLE_UPDATE: guildRoleUpdate, GUILD_ROLE_DELETE: guildRoleDelete, @@ -70,11 +76,11 @@ export const gatewayHandlers: { MESSAGE_UPDATE: messageUpdate, MESSAGE_DELETE: messageDelete, MESSAGE_DELETE_BULK: messageDeleteBulk, - MESSAGE_REACTION_ADD: undefined, - MESSAGE_REACTION_REMOVE: undefined, - MESSAGE_REACTION_REMOVE_ALL: undefined, - MESSAGE_REACTION_REMOVE_EMOJI: undefined, - PRESENCE_UPDATE: undefined, + MESSAGE_REACTION_ADD: messageReactionAdd, + MESSAGE_REACTION_REMOVE: messageReactionRemove, + MESSAGE_REACTION_REMOVE_ALL: messageReactionRemoveAll, + MESSAGE_REACTION_REMOVE_EMOJI: messageReactionRemoveEmoji, + PRESENCE_UPDATE: presenceUpdate, TYPING_START: typingStart, USER_UPDATE: userUpdate, VOICE_STATE_UPDATE: voiceStateUpdate, diff --git a/src/gateway/handlers/messageReactionAdd.ts b/src/gateway/handlers/messageReactionAdd.ts new file mode 100644 index 0000000..f465a84 --- /dev/null +++ b/src/gateway/handlers/messageReactionAdd.ts @@ -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(d.channel_id) + if (channel === undefined) + channel = await gateway.client.channels.fetch(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) +} diff --git a/src/gateway/handlers/messageReactionRemove.ts b/src/gateway/handlers/messageReactionRemove.ts new file mode 100644 index 0000000..c809cc5 --- /dev/null +++ b/src/gateway/handlers/messageReactionRemove.ts @@ -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(d.channel_id) + if (channel === undefined) + channel = await gateway.client.channels.fetch(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) +} diff --git a/src/gateway/handlers/messageReactionRemoveAll.ts b/src/gateway/handlers/messageReactionRemoveAll.ts new file mode 100644 index 0000000..415b2d9 --- /dev/null +++ b/src/gateway/handlers/messageReactionRemoveAll.ts @@ -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(d.channel_id) + if (channel === undefined) + channel = await gateway.client.channels.fetch(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) +} diff --git a/src/gateway/handlers/messageReactionRemoveEmoji.ts b/src/gateway/handlers/messageReactionRemoveEmoji.ts new file mode 100644 index 0000000..b700d55 --- /dev/null +++ b/src/gateway/handlers/messageReactionRemoveEmoji.ts @@ -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(d.channel_id) + if (channel === undefined) + channel = await gateway.client.channels.fetch(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) +} diff --git a/src/gateway/handlers/presenceUpdate.ts b/src/gateway/handlers/presenceUpdate.ts new file mode 100644 index 0000000..27d0ad2 --- /dev/null +++ b/src/gateway/handlers/presenceUpdate.ts @@ -0,0 +1,6 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const presenceUpdate: GatewayEventHandler = async ( + gateway: Gateway, + d: any +) => {} diff --git a/src/managers/messageReactions.ts b/src/managers/messageReactions.ts new file mode 100644 index 0000000..2c249c4 --- /dev/null +++ b/src/managers/messageReactions.ts @@ -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 { + 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 { + return this.client.cache.set( + this.cacheName, + key, + value, + this.client.reactionCacheLifetime + ) + } + + async flush(): Promise { + await this.client.cache.deleteCache(`reaction_users:${this.message.id}`) + return this.client.cache.deleteCache(this.cacheName) + } +} diff --git a/src/managers/reactionUsers.ts b/src/managers/reactionUsers.ts new file mode 100644 index 0000000..7605194 --- /dev/null +++ b/src/managers/reactionUsers.ts @@ -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 + } +} diff --git a/src/models/client.ts b/src/models/client.ts index 7c62137..3f335e8 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -7,12 +7,10 @@ import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts' import { UserManager } from '../managers/users.ts' import { GuildManager } from '../managers/guilds.ts' import { ChannelsManager } from '../managers/channels.ts' -import { - ClientPresence -} from '../structures/presence.ts' +import { ClientPresence } from '../structures/presence.ts' import { EmojisManager } from '../managers/emojis.ts' -import { ActivityGame, ClientActivity } from "../types/presence.ts" -import { ClientEvents } from "../gateway/handlers/index.ts" +import { ActivityGame, ClientActivity } from '../types/presence.ts' +import { ClientEvents } from '../gateway/handlers/index.ts' // import { Application } from "../../mod.ts" /** Some Client Options to modify behaviour */ @@ -22,9 +20,9 @@ export interface ClientOptions { /** Gateway Intents */ intents?: GatewayIntents[] /** Cache Adapter to use, defaults to Collections one */ - cache?: ICacheAdapter, + cache?: ICacheAdapter /** Force New Session and don't use cached Session (by persistent caching) */ - forceNewSession?: boolean, + forceNewSession?: boolean /** Startup presence of client */ presence?: ClientPresence | ClientActivity | ActivityGame /** Whether it's a bot user or not? Use this if selfbot! */ @@ -33,19 +31,21 @@ export interface ClientOptions { canary?: boolean /** Time till which Messages are to be cached, in MS. Default is 3600000 */ 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 { - on: ( - event: U, listener: ClientEvents[U] - ) => this + on: (event: U, listener: ClientEvents[U]) => this emit: ( - event: U, ...args: Parameters + event: U, + ...args: Parameters ) => boolean } - /** * Discord Client. */ @@ -68,12 +68,16 @@ export class Client extends EventEmitter { forceNewSession?: boolean /** Time till messages to stay cached, in MS. */ 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) guilds: GuildManager = new GuildManager(this) channels: ChannelsManager = new ChannelsManager(this) emojis: EmojisManager = new EmojisManager(this) - + /** Whether this client will login as bot user or not */ bot: boolean = true /** Whether the REST Manager will use Canary API or not */ @@ -81,7 +85,7 @@ export class Client extends EventEmitter { /** Client's presence. Startup one if set before connecting */ presence: ClientPresence = new ClientPresence() - constructor (options: ClientOptions = {}) { + constructor(options: ClientOptions = {}) { super() this.token = options.token this.intents = options.intents @@ -94,17 +98,22 @@ export class Client extends EventEmitter { : new ClientPresence(options.presence) if (options.bot === false) this.bot = false 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 */ - setAdapter (adapter: ICacheAdapter): Client { + setAdapter(adapter: ICacheAdapter): Client { this.cache = adapter return this } /** Change Presence of Client */ - setPresence (presence: ClientPresence | ClientActivity | ActivityGame): void { + setPresence(presence: ClientPresence | ClientActivity | ActivityGame): void { if (presence instanceof ClientPresence) { this.presence = presence } else this.presence = new ClientPresence(presence) @@ -112,7 +121,7 @@ export class Client extends EventEmitter { } /** Emit debug event */ - debug (tag: string, msg: string): void { + debug(tag: string, msg: string): void { this.emit('debug', `[${tag}] ${msg}`) } @@ -124,7 +133,7 @@ export class Client extends EventEmitter { * @param token Your token. 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 else if (this.token === undefined && token !== undefined) { this.token = token diff --git a/src/structures/message.ts b/src/structures/message.ts index 95bbb40..4ede486 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -1,120 +1,129 @@ -import { Base } from './base.ts' -import { - Attachment, - ChannelMention, - MessageActivity, - MessageApplication, - MessageOption, - MessagePayload, - MessageReference, - Reaction -} from '../types/channel.ts' -import { Client } from '../models/client.ts' -import { User } from './user.ts' -import { Member } from './member.ts' -import { Embed } from './embed.ts' -import { CHANNEL_MESSAGE } from '../types/endpoint.ts' -import { MessageMentions } from './messageMentions.ts' -import { TextChannel } from './textChannel.ts' -import { Guild } from './guild.ts' - -type AllMessageOptions = MessageOption | Embed - -export class Message extends Base { - id: string - channelID: string - channel: TextChannel - guildID?: string - guild?: Guild - author: User - member?: Member - content: string - timestamp: string - editedTimestamp?: string - tts: boolean - mentionEveryone: boolean - mentions: MessageMentions - mentionRoles: string[] - mentionChannels?: ChannelMention[] - attachments: Attachment[] - embeds: Embed[] - reactions?: Reaction[] - nonce?: string | number - pinned: boolean - webhookID?: string - type: number - activity?: MessageActivity - application?: MessageApplication - messageReference?: MessageReference - flags?: number - - constructor ( - client: Client, - data: MessagePayload, - channel: TextChannel, - author: User - ) { - super(client) - this.id = data.id - this.channelID = data.channel_id - this.guildID = data.guild_id - this.author = author - this.content = data.content - this.timestamp = data.timestamp - this.editedTimestamp = data.edited_timestamp - this.tts = data.tts - this.mentionEveryone = data.mention_everyone - this.mentions = new MessageMentions(this.client, this) - this.mentionRoles = data.mention_roles - this.mentionChannels = data.mention_channels - this.attachments = data.attachments - this.embeds = data.embeds.map(v => new Embed(v)) - this.reactions = data.reactions - this.nonce = data.nonce - this.pinned = data.pinned - this.webhookID = data.webhook_id - this.type = data.type - this.activity = data.activity - this.application = data.application - this.messageReference = data.message_reference - this.flags = data.flags - this.channel = channel - } - - protected readFromData (data: MessagePayload): void { - super.readFromData(data) - this.channelID = data.channel_id ?? this.channelID - this.guildID = data.guild_id ?? this.guildID - this.content = data.content ?? this.content - this.timestamp = data.timestamp ?? this.timestamp - this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp - this.tts = data.tts ?? this.tts - this.mentionEveryone = data.mention_everyone ?? this.mentionEveryone - this.mentionRoles = data.mention_roles ?? this.mentionRoles - this.mentionChannels = data.mention_channels ?? this.mentionChannels - this.attachments = data.attachments ?? this.attachments - this.embeds = data.embeds.map(v => new Embed(v)) ?? this.embeds - this.reactions = data.reactions ?? this.reactions - this.nonce = data.nonce ?? this.nonce - this.pinned = data.pinned ?? this.pinned - this.webhookID = data.webhook_id ?? this.webhookID - this.type = data.type ?? this.type - this.activity = data.activity ?? this.activity - this.application = data.application ?? this.application - this.messageReference = data.message_reference ?? this.messageReference - this.flags = data.flags ?? this.flags - } - - async edit (text?: string, option?: MessageOption): Promise { - return this.channel.editMessage(this.id, text, option) - } - - /** These will **not** work in all servers, as this feature is coming slowly. */ - async reply(text?: string | AllMessageOptions, option?: AllMessageOptions): Promise { - return this.channel.send(text, option, this) - } - - async delete (): Promise { - return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) - } -} +import { Base } from './base.ts' +import { + Attachment, + ChannelMention, + MessageActivity, + MessageApplication, + MessageOption, + MessagePayload, + MessageReference, +} from '../types/channel.ts' +import { Client } from '../models/client.ts' +import { User } from './user.ts' +import { Member } from './member.ts' +import { Embed } from './embed.ts' +import { CHANNEL_MESSAGE } from '../types/endpoint.ts' +import { MessageMentions } from './messageMentions.ts' +import { TextChannel } from './textChannel.ts' +import { Guild } from './guild.ts' +import { MessageReactionsManager } from '../managers/messageReactions.ts' + +type AllMessageOptions = MessageOption | Embed + +export class Message extends Base { + id: string + channelID: string + channel: TextChannel + guildID?: string + guild?: Guild + author: User + member?: Member + content: string + timestamp: string + editedTimestamp?: string + tts: boolean + mentionEveryone: boolean + mentions: MessageMentions + mentionRoles: string[] + mentionChannels?: ChannelMention[] + attachments: Attachment[] + embeds: Embed[] + reactions: MessageReactionsManager + nonce?: string | number + pinned: boolean + webhookID?: string + type: number + activity?: MessageActivity + application?: MessageApplication + messageReference?: MessageReference + flags?: number + + constructor( + client: Client, + data: MessagePayload, + channel: TextChannel, + author: User + ) { + super(client) + this.id = data.id + this.channelID = data.channel_id + this.guildID = data.guild_id + this.author = author + this.content = data.content + this.timestamp = data.timestamp + this.editedTimestamp = data.edited_timestamp + this.tts = data.tts + this.mentionEveryone = data.mention_everyone + this.mentions = new MessageMentions(this.client, this) + this.mentionRoles = data.mention_roles + this.mentionChannels = data.mention_channels + this.attachments = data.attachments + this.embeds = data.embeds.map((v) => new Embed(v)) + this.reactions = new MessageReactionsManager(this.client, this) + this.nonce = data.nonce + this.pinned = data.pinned + this.webhookID = data.webhook_id + this.type = data.type + this.activity = data.activity + this.application = data.application + this.messageReference = data.message_reference + this.flags = data.flags + this.channel = channel + } + + protected readFromData(data: MessagePayload): void { + super.readFromData(data) + this.channelID = data.channel_id ?? this.channelID + this.guildID = data.guild_id ?? this.guildID + this.content = data.content ?? this.content + this.timestamp = data.timestamp ?? this.timestamp + this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp + this.tts = data.tts ?? this.tts + this.mentionEveryone = data.mention_everyone ?? this.mentionEveryone + this.mentionRoles = data.mention_roles ?? this.mentionRoles + this.mentionChannels = data.mention_channels ?? this.mentionChannels + this.attachments = data.attachments ?? this.attachments + this.embeds = data.embeds.map((v) => new Embed(v)) ?? this.embeds + this.nonce = data.nonce ?? this.nonce + this.pinned = data.pinned ?? this.pinned + this.webhookID = data.webhook_id ?? this.webhookID + this.type = data.type ?? this.type + this.activity = data.activity ?? this.activity + this.application = data.application ?? this.application + this.messageReference = data.message_reference ?? this.messageReference + this.flags = data.flags ?? this.flags + } + + /** Edit this message. */ + async edit(text?: string, option?: MessageOption): Promise { + 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) + } + + /** Create a Reply to this Message. */ + async reply( + text?: string | AllMessageOptions, + option?: AllMessageOptions + ): Promise { + return this.channel.send(text, option, this) + } + + /** Delete the Message. */ + async delete(): Promise { + return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) + } +} diff --git a/src/structures/messageReaction.ts b/src/structures/messageReaction.ts new file mode 100644 index 0000000..b20baf1 --- /dev/null +++ b/src/structures/messageReaction.ts @@ -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 + } +} diff --git a/src/test/cmd.ts b/src/test/cmd.ts index 00a2862..202a859 100644 --- a/src/test/cmd.ts +++ b/src/test/cmd.ts @@ -118,6 +118,22 @@ client.on('voiceStateRemove', (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}`)) const files = Deno.readDirSync('./src/test/cmds') diff --git a/src/types/gateway.ts b/src/types/gateway.ts index 188103c..73854c7 100644 --- a/src/types/gateway.ts +++ b/src/types/gateway.ts @@ -1,14 +1,14 @@ // https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway // https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events -import { Guild } from "../structures/guild.ts" -import { Member } from "../structures/member.ts" +import { Guild } from '../structures/guild.ts' +import { Member } from '../structures/member.ts' import { EmojiPayload } from './emoji.ts' import { MemberPayload } from './guild.ts' import { ActivityGame, ActivityPayload, StatusType, - ClientStatus + ClientStatus, } from './presence.ts' import { RolePayload } from './role.ts' import { UserPayload } from './user.ts' @@ -27,7 +27,7 @@ export enum GatewayOpcodes { // 문서를 확인해본 결과 Opcode 5번은 비 REQUEST_GUILD_MEMBERS = 8, INVALID_SESSION = 9, HELLO = 10, - HEARTBEAT_ACK = 11 + HEARTBEAT_ACK = 11, } /** @@ -47,7 +47,7 @@ export enum GatewayCloseCodes { SHARDING_REQUIRED = 4011, INVALID_API_VERSION = 4012, INVALID_INTENTS = 4013, - DISALLOWED_INTENTS = 4014 + DISALLOWED_INTENTS = 4014, } export enum GatewayIntents { @@ -65,7 +65,7 @@ export enum GatewayIntents { GUILD_MESSAGE_TYPING = 1 << 11, DIRECT_MESSAGES = 1 << 12, DIRECT_MESSAGE_REACTIONS = 1 << 13, - DIRECT_MESSAGE_TYPING = 1 << 13 + DIRECT_MESSAGE_TYPING = 1 << 13, } export enum GatewayEvents { @@ -105,7 +105,7 @@ export enum GatewayEvents { User_Update = 'USER_UPDATE', Voice_Server_Update = 'VOICE_SERVER_UPDATE', Voice_State_Update = 'VOICE_STATE_UPDATE', - Webhooks_Update = 'WEBHOOKS_UPDATE' + Webhooks_Update = 'WEBHOOKS_UPDATE', } export interface IdentityPayload { @@ -290,6 +290,13 @@ export interface MessageReactionRemoveAllPayload { message_id: string } +export interface MessageReactionRemoveEmojiPayload { + channel_id: string + message_id: string + guild_id?: string + emoji: EmojiPayload +} + export interface PresenceUpdatePayload { user: UserPayload guild_id: string @@ -335,4 +342,4 @@ export interface TypingStartPayload { export interface TypingStartGuildData { guild: Guild member: Member -} \ No newline at end of file +}