From d129457624160962b61c612d10819c16f7f9b750 Mon Sep 17 00:00:00 2001 From: Amish Shah Date: Thu, 27 Oct 2016 16:12:02 +0100 Subject: [PATCH] Improve emoji support --- src/structures/Emoji.js | 12 +++++ src/structures/Message.js | 10 +++- src/structures/MessageReaction.js | 82 ++++++++++++++++++++++++++++--- src/util/parseEmoji.js | 3 ++ 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/src/structures/Emoji.js b/src/structures/Emoji.js index 0349b6f70..eb64b4b99 100644 --- a/src/structures/Emoji.js +++ b/src/structures/Emoji.js @@ -101,6 +101,18 @@ class Emoji { toString() { return this.requiresColons ? `<:${this.name}:${this.id}>` : this.name; } + + /** + * The identifier of this emoji, used for message reactions + * @readonly + * @type {string} + */ + get identifier() { + if (this.id) { + return `${this.name}:${this.id}`; + } + return encodeURIComponent(this.name); + } } module.exports = Emoji; diff --git a/src/structures/Message.js b/src/structures/Message.js index 494c45779..3c86f8821 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -158,7 +158,7 @@ class Message { } _addReaction(emoji, user) { - const emojiID = emoji.id || emoji; + const emojiID = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name; let reaction; if (this.reactions.has(emojiID)) { reaction = this.reactions.get(emojiID); @@ -304,6 +304,14 @@ class Message { } addReaction(emoji) { + if (emoji.identifier) { + emoji = emoji.identifier; + } else if (typeof emoji === 'string') { + if (!emoji.includes('%')) emoji = encodeURIComponent(emoji); + } else { + return Promise.reject(`Emoji must be a string or an Emoji/ReactionEmoji`); + } + return this.client.rest.methods.addMessageReaction(this.channel.id, this.id, emoji); } diff --git a/src/structures/MessageReaction.js b/src/structures/MessageReaction.js index cab5bcd1f..e7f06ab0d 100644 --- a/src/structures/MessageReaction.js +++ b/src/structures/MessageReaction.js @@ -1,5 +1,55 @@ const Collection = require('../util/Collection'); -const parseEmoji = require('../util/ParseEmoji'); +const Emoji = require('./Emoji'); + +/** + * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis + * will use this class opposed to the Emoji class when the client doesn't know enough + * information about them. + */ +class ReactionEmoji { + constructor(reaction, name, id) { + /** + * The message reaction this emoji refers to + * @type {MessageReaction} + */ + this.reaction = reaction; + /** + * The name of this reaction emoji. + * @type {string} + */ + this.name = name; + /** + * The ID of this reaction emoji. + * @type {string} + */ + this.id = id; + } + + /** + * The identifier of this emoji, used for message reactions + * @readonly + * @type {string} + */ + get identifier() { + if (this.id) { + return `${this.name}:${this.id}`; + } + return encodeURIComponent(this.name); + } + + /** + * Creates the text required to form a graphical emoji on Discord. + * @example + * ```js + * // send the emoji used in a reaction to the channel the reaction is part of + * reaction.message.channel.sendMessage(`The emoji used is ${reaction.emoji}`); + * ``` + * @returns {string} + */ + toString() { + return this.id ? `<:${this.name}:${this.id}>` : this.name; + } +} /** * Represents a reaction to a message @@ -11,11 +61,8 @@ class MessageReaction { * @type {Message} */ this.message = message; - /** - * The emoji of this reaction, if this is a string it is a plain unicode emoji. - * @type {Emoji} - */ - this.emoji = emoji; + + this._emoji = new ReactionEmoji(this, emoji.name, emoji.id); /** * The number of people that have given the same reaction. * @type {number} @@ -28,13 +75,34 @@ class MessageReaction { this.users = new Collection(); } + /** + * The emoji of this reaction, either an Emoji object for known custom emojis, or a ReactionEmoji which has fewer + * properties. Whatever the prototype of the emoji, it will still have `name`, `id`, `identifier` and `toString()` + * @type {Emoji|ReactionEmoji} + */ + get emoji() { + if (this._emoji instanceof Emoji) { + return this._emoji; + } + // check to see if the emoji has become known to the client + if (this._emoji.id) { + const emojis = this.message.client.emojis; + if (emojis.has(this._emoji.id)) { + const emoji = emojis.get(this._emoji.id); + this._emoji = emoji; + return emoji; + } + } + return this._emoji; + } + /** * If the client has given this reaction to a message, it is removed. * @returns {Promise} */ remove() { const message = this.message; - return message.client.rest.methods.removeMessageReaction(message.channel.id, message.id, parseEmoji(this.emoji)); + return message.client.rest.methods.removeMessageReaction(message.channel.id, message.id, this.emoji.identifier); } } diff --git a/src/util/parseEmoji.js b/src/util/parseEmoji.js index 4bc1d1145..d9f7b2212 100644 --- a/src/util/parseEmoji.js +++ b/src/util/parseEmoji.js @@ -1,4 +1,7 @@ module.exports = function parseEmoji(text) { + if (text.includes('%')) { + text = decodeURIComponent(text); + } if (text.includes(':')) { const [name, id] = text.split(':'); return { name, id };