From 58a2464cb40346c5123b76110e55c59ba21613e5 Mon Sep 17 00:00:00 2001 From: jsl12 Date: Wed, 11 Aug 2021 15:17:02 -0500 Subject: [PATCH] added the ability to read unicode emojis --- msg.py | 76 +++++++++++++++++++++++++++++++---------------------- robopage.py | 16 ++++++++--- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/msg.py b/msg.py index f7649ca..4392869 100644 --- a/msg.py +++ b/msg.py @@ -73,28 +73,29 @@ class MsgData: LOGGER.info(f'Added message id {message.id} from {message.author}: {message.content}') async def update_reaction(self, client: discord.Client, payload: RawReactionActionEvent): - if isinstance(payload.emoji, discord.PartialEmoji): - chan: discord.TextChannel = await client.fetch_channel(channel_id=payload.channel_id) - msg: discord.Message = await chan.fetch_message(id=payload.message_id) - idx = (msg.id, payload.emoji.name) + payload.emoji: discord.PartialEmoji = convert_emoji(payload.emoji) + LOGGER.info(repr(payload.emoji)) + chan: discord.TextChannel = await client.fetch_channel(channel_id=payload.channel_id) + msg: discord.Message = await chan.fetch_message(payload.message_id) + idx = (msg.id, payload.emoji.name) - for reaction in msg.reactions: - if isinstance(reaction.emoji, discord.Emoji) and reaction.emoji.name == payload.emoji.name: - reactions = pd.Series(await reaction_dict(reaction)) + for reaction in msg.reactions: + if isinstance(reaction.emoji, discord.Emoji) and reaction.emoji.name == payload.emoji.name: + reactions = pd.Series(await reaction_dict(reaction)) + async with self.lock: + self.reactions.loc[pd.IndexSlice[idx], :] = reactions + LOGGER.info(f'Added {str(idx)}, {int(self.reactions.loc[pd.IndexSlice[idx], "count"])} total') + break + else: + # only reaches here if the remove action was to take off the last reaction of that type + if payload.event_type == 'REACTION_REMOVE': + try: async with self.lock: - self.reactions.loc[pd.IndexSlice[idx], :] = reactions - LOGGER.info(f'Added {str(idx)}, {int(self.reactions.loc[pd.IndexSlice[idx], "count"])} total') - break - else: - # only reaches here if the remove action was to take off the last reaction of that type - if payload.event_type == 'REACTION_REMOVE': - try: - async with self.lock: - self.reactions = self.reactions.drop(idx, axis=0) - except KeyError as e: - LOGGER.info(f'{idx} not in index') - else: - LOGGER.info(f'Dropped {idx}') + self.reactions = self.reactions.drop(idx, axis=0) + except KeyError as e: + LOGGER.info(f'{idx} not in index') + else: + LOGGER.info(f'Dropped {idx}') def emoji_messages(self, emoji_name: str, days: int): return emoji_messages(msg_df=self.msgs, react_df=self.reactions, emoji_name=emoji_name, days=days) @@ -148,6 +149,13 @@ class MsgData: LOGGER.info(f'User: {user.mention}') return f'{user.mention} with {data.iloc[0]["total"]:.0f} over the past {int(days)} days' +def convert_emoji(emoji): + try: + emoji.name.encode('ascii') + except UnicodeEncodeError as e: + emoji.name = emoji.name.encode('unicode-escape').decode('ascii') + return emoji + async def message_df(client: discord.Client, **kwargs): return pd.DataFrame( [message_dict(m) async for m in message_gen(client, **kwargs)] @@ -191,34 +199,38 @@ async def reaction_series(msg: discord.Message): return pd.DataFrame([ await reaction_dict(r) for r in msg.reactions - if isinstance(r.emoji, discord.Emoji) ]) async def reaction_dict(r: discord.Reaction) -> Dict: + is_emoji = isinstance(r.emoji, (discord.Emoji, discord.PartialEmoji)) + LOGGER.info(repr(r.emoji)) return { 'msg id': r.message.id, - 'emoji': r.emoji.name, - 'emoji id': r.emoji.id, + 'emoji': r.emoji.name if is_emoji else r.emoji.encode('unicode-escape').decode('ascii'), + 'emoji id': r.emoji.id if is_emoji else None, 'count': int(r.count), - # 'users': str(list(map(lambda u: u.display_name, (u for u in await r.users().flatten())))), } def emoji_messages(msg_df, react_df, emoji_name: str, days: int = 10) -> pd.DataFrame: # get reactions with a cancellation emoji - reactions = react_df.loc[pd.IndexSlice[:, emoji_name], :] - reacted_msgs = msg_df.loc[reactions.index.get_level_values(0).to_list()] + try: + reactions = react_df.loc[pd.IndexSlice[:, emoji_name], :] + except KeyError as e: + LOGGER.error(f'Emoji not found in reactions DataFrame: {emoji_name}') + else: + reacted_msgs = msg_df.loc[reactions.index.get_level_values(0).to_list()] - reacted_msgs['count'] = reacted_msgs.index.to_series().apply( - lambda idx: reactions.loc[pd.IndexSlice[idx, emoji_name], 'count']) + reacted_msgs['count'] = reacted_msgs.index.to_series().apply( + lambda idx: reactions.loc[pd.IndexSlice[idx, emoji_name], 'count']) - # filter outdated messages - reacted_msgs = reacted_msgs[reacted_msgs['created'] >= (datetime.today() - timedelta(days=days)).astimezone()] + # filter outdated messages + reacted_msgs = reacted_msgs[reacted_msgs['created'] >= (datetime.today() - timedelta(days=days)).astimezone()] - reacted_msgs = reacted_msgs.sort_values('count', ascending=False) + reacted_msgs = reacted_msgs.sort_values('count', ascending=False) - return reacted_msgs + return reacted_msgs def emoji_totals(edf: pd.DataFrame) -> pd.DataFrame: diff --git a/robopage.py b/robopage.py index 6734bb3..8ab7daa 100644 --- a/robopage.py +++ b/robopage.py @@ -26,6 +26,7 @@ class RoboPage(discord.Client): self.jokes = list(attrs) self.lock = Lock() self.emoji_regex = re.compile("^who is the most (?P\w+)(?: in the past (?P\d+) days)?\??$", re.IGNORECASE) + self.leaderboard_regex = re.compile('^most (?P\w+) leaderboard$', re.IGNORECASE) def run(self): return super().run(os.getenv('DISCORD_TOKEN')) @@ -37,8 +38,8 @@ class RoboPage(discord.Client): self.data: msg.MsgData = await msg.MsgData.create( client=self, - limit=3000, - # limit=20, + # limit=3000, + limit=20, days=14, ) self.data.to_sql('messages.db') @@ -78,6 +79,7 @@ if __name__ == '__main__': async def on_ready(): # print(len(list(client.get_all_members()))) await client.handle_ready() + print('\n'.join(client.data.reactions.index.get_level_values(1).drop_duplicates().sort_values())) @client.event @@ -88,13 +90,19 @@ if __name__ == '__main__': @client.event async def on_raw_reaction_add(payload): LOGGER.info(payload) - await client.data.update_reaction(payload=payload, client=client) + try: + await client.data.update_reaction(payload=payload, client=client) + except AttributeError as e: + LOGGER.info(f'Robopage not initialized yet') @client.event async def on_raw_reaction_remove(payload): LOGGER.info(payload) - await client.data.update_reaction(payload=payload, client=client) + try: + await client.data.update_reaction(payload=payload, client=client) + except AttributeError as e: + LOGGER.info(f'Robopage not initialized yet') client.run()