From 1629a8982c75611cb06952866022cdfec92de107 Mon Sep 17 00:00:00 2001 From: Jeremy Zhang Date: Fri, 27 Jul 2018 08:57:54 +0000 Subject: [PATCH] Implement reactions --- discordbot/titanembeds/bot.py | 37 +++++++++++++ .../titanembeds/socketio/socketiointerface.py | 15 +++++ discordbot/titanembeds/utils.py | 26 ++++++++- webapp/titanembeds/redisqueue.py | 1 + webapp/titanembeds/static/css/embed.css | 36 ++++++++++++ webapp/titanembeds/static/js/embed.js | 55 +++++++++++++++++++ webapp/titanembeds/templates/embed.html.j2 | 6 +- 7 files changed, 174 insertions(+), 2 deletions(-) diff --git a/discordbot/titanembeds/bot.py b/discordbot/titanembeds/bot.py index fddeb1b..a15f2cf 100644 --- a/discordbot/titanembeds/bot.py +++ b/discordbot/titanembeds/bot.py @@ -102,6 +102,18 @@ class Titan(discord.AutoShardedClient): self.delete_list.append(message.id) await self.redisqueue.delete_message(message) await self.socketio.on_message_delete(message) + + async def on_reaction_add(self, reaction, user): + await self.redisqueue.update_message(reaction.message) + await self.socketio.on_reaction_add(reaction.message) + + async def on_reaction_remove(self, reaction, user): + await self.redisqueue.update_message(reaction.message) + await self.socketio.on_reaction_remove(reaction.message) + + async def on_reaction_clear(self, message, reactions): + await self.redisqueue.update_message(message) + await self.socketio.on_reaction_clear(message) async def on_guild_join(self, guild): await self.redisqueue.update_guild(guild) @@ -206,6 +218,31 @@ class Titan(discord.AutoShardedClient): msg = discord.Message(channel=channel, state=self._connection, data=data) # Procreate a fake message object await self.on_message_delete(msg) + async def on_raw_reaction_add(self, payload): + message_id = payload.message_id + if not self.in_messages_cache(message_id): + channel = self.get_channel(payload.channel_id) + message = await channel.get_message(message_id) + await self.on_reaction_add(message.reactions[0], None) + + async def on_raw_reaction_remove(self, payload): + message_id = payload.message_id + if not self.in_messages_cache(message_id): + partial = payload.emoji + emoji = self._connection._upgrade_partial_emoji(partial) + channel = self.get_channel(payload.channel_id) + message = await channel.get_message(message_id) + message._add_reaction({"me": payload.user_id == self.user.id}, emoji, payload.user_id) + reaction = message._remove_reaction({}, emoji, payload.user_id) + await self.on_reaction_remove(reaction, None) + + async def on_raw_reaction_clear(self, payload): + message_id = payload.message_id + if not self.in_messages_cache(message_id): + channel = self.get_channel(payload.channel_id) + message = await channel.get_message(message_id) + await self.on_reaction_clear(message, []) + def in_messages_cache(self, msg_id): for msg in self._connection._messages: if msg.id == msg_id: diff --git a/discordbot/titanembeds/socketio/socketiointerface.py b/discordbot/titanembeds/socketio/socketiointerface.py index 45349a2..1268438 100644 --- a/discordbot/titanembeds/socketio/socketiointerface.py +++ b/discordbot/titanembeds/socketio/socketiointerface.py @@ -22,6 +22,21 @@ class SocketIOInterface: msg = get_formatted_message(message) await self.io.emit('MESSAGE_UPDATE', data=msg, room=str("CHANNEL_"+str(message.channel.id)), namespace='/gateway') + async def on_reaction_add(self, message): + if message.guild: + msg = get_formatted_message(message) + await self.io.emit('MESSAGE_REACTION_ADD', data=msg, room=str("CHANNEL_"+str(message.channel.id)), namespace='/gateway') + + async def on_reaction_remove(self, message): + if message.guild: + msg = get_formatted_message(message) + await self.io.emit('MESSAGE_REACTION_REMOVE', data=msg, room=str("CHANNEL_"+str(message.channel.id)), namespace='/gateway') + + async def on_reaction_clear(self, message): + if message.guild: + msg = get_formatted_message(message) + await self.io.emit('MESSAGE_REACTION_REMOVE_ALL', data=msg, room=str("CHANNEL_"+str(message.channel.id)), namespace='/gateway') + async def on_guild_member_add(self, member): user = get_formatted_user(member) await self.io.emit('GUILD_MEMBER_ADD', data=user, room=str("GUILD_"+str(member.guild.id)), namespace='/gateway') diff --git a/discordbot/titanembeds/utils.py b/discordbot/titanembeds/utils.py index 4530f90..c21da56 100644 --- a/discordbot/titanembeds/utils.py +++ b/discordbot/titanembeds/utils.py @@ -36,6 +36,8 @@ def get_formatted_message(message): member = message.guild.get_member(mention["id"]) if member: mention["nickname"] = member.nick + if hasattr(message, "reactions"): + msg["reactions"] = get_message_reactions(message.reactions) return msg def get_formatted_user(user): @@ -246,4 +248,26 @@ def get_embeds_list(embeds): em = [] for e in embeds: em.append(e.to_dict()) - return em \ No newline at end of file + return em + +def get_message_reactions(reactions): + reacts = [] + for reaction in reactions: + reacts.append({ + "emoji": get_partial_emoji(reaction.emoji), + "count": reaction.count + }) + return reacts + +def get_partial_emoji(emoji): + emote = { + "animated": False, + "id": None, + "name": str(emoji) + } + if isinstance(emoji, str): + return emote + emote["animated"] = emoji.animated + emote["id"] = str(emoji.id) + emote["name"] = emoji.name + return emote \ No newline at end of file diff --git a/webapp/titanembeds/redisqueue.py b/webapp/titanembeds/redisqueue.py index e74da3e..8d41fe3 100644 --- a/webapp/titanembeds/redisqueue.py +++ b/webapp/titanembeds/redisqueue.py @@ -59,6 +59,7 @@ class RedisQueue: "channel_id": str(x["channel_id"]), "mentions": x["mentions"], "embeds": x["embeds"], + "reactions": x["reactions"] } if message["author"]["id"] not in guild_members: member = self.get_guild_member(guild_id, message["author"]["id"]) diff --git a/webapp/titanembeds/static/css/embed.css b/webapp/titanembeds/static/css/embed.css index 14cb0c3..f7e4120 100644 --- a/webapp/titanembeds/static/css/embed.css +++ b/webapp/titanembeds/static/css/embed.css @@ -793,6 +793,42 @@ p.mentioned span.chatmessage { max-width: 400px; } +#chatcontent span.reactions { + display: block; +} + +#chatcontent span.reactions .reaction { + background-color: rgba(0, 0, 0, 0.1); + font-size: 12pt; + border-radius: 5px; + margin-left: 6px; + padding-left: 3px; + padding-right: 3px; + color: #C3C4C5; + cursor: pointer; + height: 27px; + display: inline-block; + min-width: 50px; +} + +#chatcontent span.reactions .reaction:hover { + transform: scale(1.3); +} + +#chatcontent span.reactions .reaction:hover:active { + transform: scale(0.9); +} + +#chatcontent span.reactions .reaction img { + width: 18px; + vertical-align: middle; +} + +#chatcontent span.reactions .reaction .count { + float: right; + margin-right: 6px; +} + .wdt-emoji-popup { position: fixed; bottom: 5%; diff --git a/webapp/titanembeds/static/js/embed.js b/webapp/titanembeds/static/js/embed.js index 31080a3..874f021 100644 --- a/webapp/titanembeds/static/js/embed.js +++ b/webapp/titanembeds/static/js/embed.js @@ -1152,6 +1152,29 @@ } return emb; } + + function parse_message_reactions(reactions) { + var reacts = [] + var template = $("#mustache_reactionchip").html(); + Mustache.parse(template); + for (var i = 0; i < reactions.length; i++) { + var disreact = reactions[i]; + var emoji = disreact.emoji; + if (emoji.id) { + disreact.img_url = "https://cdn.discordapp.com/emojis/" + emoji.id; + if (emoji.animated) { + disreact.img_url += ".gif"; + } else { + disreact.img_url += ".png"; + } + } else { + disreact.img_url = $(twemoji.parse(emoji.name)).attr("src"); + } + var rendered = Mustache.render(template, disreact); + reacts.push(rendered); + } + return reacts; + } function fill_discord_messages(messages, jumpscroll, replace) { if (replace === undefined) { @@ -1215,6 +1238,11 @@ for(var j = 0; j < embeds.length; j++) { $("#discordmessage_"+message.id).parent().find("span.embeds").append(embeds[j]); } + var reactions = parse_message_reactions(message.reactions); + $("#discordmessage_"+message.id).parent().find("span.reactions").text(""); + for(var j = 0; j < reactions.length; j++) { + $("#discordmessage_"+message.id).parent().find("span.reactions").append(reactions[j]); + } var usrcachekey = username + "#" + message.author.discriminator; if (usrcachekey.startsWith("(Titan Dev) ")) { usrcachekey = usrcachekey.substr(12); @@ -1783,6 +1811,33 @@ fill_discord_messages([msg], false, msgelem_parent); }); + socket.on("MESSAGE_REACTION_ADD", function (msg) { + var msgelem = $("#discordmessage_"+msg.id); + if (msgelem.length == 0) { + return; + } + var msgelem_parent = msgelem.parent(); + fill_discord_messages([msg], false, msgelem_parent); + }); + + socket.on("MESSAGE_REACTION_REMOVE", function (msg) { + var msgelem = $("#discordmessage_"+msg.id); + if (msgelem.length == 0) { + return; + } + var msgelem_parent = msgelem.parent(); + fill_discord_messages([msg], false, msgelem_parent); + }); + + socket.on("MESSAGE_REACTION_REMOVE_ALL", function (msg) { + var msgelem = $("#discordmessage_"+msg.id); + if (msgelem.length == 0) { + return; + } + var msgelem_parent = msgelem.parent(); + fill_discord_messages([msg], false, msgelem_parent); + }); + socket.on("GUILD_MEMBER_ADD", function (usr) { if (usr.status != "offline") { discord_users_list.push(usr); diff --git a/webapp/titanembeds/templates/embed.html.j2 b/webapp/titanembeds/templates/embed.html.j2 index 8cc7ea1..9fa5768 100644 --- a/webapp/titanembeds/templates/embed.html.j2 +++ b/webapp/titanembeds/templates/embed.html.j2 @@ -350,7 +350,7 @@ + + {% endraw %}