leaderboard working again and cleanup

This commit is contained in:
jsl12
2022-01-22 02:07:06 -06:00
parent a956ee7afe
commit b0515954af
2 changed files with 32 additions and 117 deletions

View File

@@ -1,7 +1,6 @@
import asyncio import asyncio
import logging import logging
import sqlite3 import sqlite3
from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union
@@ -66,17 +65,8 @@ class MsgData:
channel = await guild.fetch_channel(row['channel_id']) channel = await guild.fetch_channel(row['channel_id'])
return await channel.fetch_message(row['msg_id']) return await channel.fetch_message(row['msg_id'])
def __str__(self):
return str(self.msgs) + '\n\n' + str(self.reactions)
def __repr__(self): def __repr__(self):
return f'<{__name__}.{self.__class__.__name__} with {self.msgs.shape[0]} messages and {self.reactions.shape[0]} reactions>' return f'<{__name__}.{self.__class__.__name__} with {self.reactions.shape[0]} reactions>'
def __getitem__(self, item):
if isinstance(item, str):
return self.emoji_messages(emoji_name=item).sort_values('count', ascending=False)
elif isinstance(item, int):
return self.reactions.loc[pd.IndexSlice[item, :],].fillna(0).applymap(int)
# async def add_msg(self, message: Message): # async def add_msg(self, message: Message):
# async with self.lock: # async with self.lock:
@@ -105,62 +95,6 @@ class MsgData:
return new return new
def emoji_messages(self, emoji_name: str, days: int = None) -> pd.DataFrame:
"""Creates a DataFrame of the messages that have reactions with a certain emoji. Includes a 'count' column"""
counts: pd.Series = self.emoji_counts(emoji_name)
# Get the ids of messages that that have the targeted emoji
message_id_counts: pd.Index = counts.index.drop_duplicates()
# There could be a situation where a message id in message_id_counts isn't actually in the self.msgs DataFrame
# Filter to keep only the messages that have actually been captured in the self.msgs DataFrame
message_id_counts: pd.Index = message_id_counts[message_id_counts.isin(self.msgs.index.get_level_values(0))]
# If there were actually some message ids found
if message_id_counts.shape[0] > 0:
# Select the relevant messages from the self.msgs DataFrame using the filtered message ids
res: pd.DataFrame = self.msgs.loc[message_id_counts]
# Add the 'count' column
res['count'] = counts
# If necessary, filter by days into the past
if days is not None and days > 0:
res = res[res['created'] >= (datetime.today() - timedelta(days=days)).astimezone()]
# return the message DataFrame, sorted by created (sent) time
return res.sort_values('created', ascending=False)
else:
raise KeyError(f'No messages found with {emoji_name} reactions')
def emoji_counts(self, emoji_name: str) -> pd.Series:
"""Creates a Series indexed by message id and with the number of reactions with emoji_name as values"""
assert isinstance(emoji_name, str), f'emoji_name must be a string'
try:
return self.reactions.loc[pd.IndexSlice[:, emoji_name], 'count'].droplevel(1).sort_values(ascending=False)
except KeyError as e:
LOGGER.error(f' {emoji_name} not found out of {self.unique_emojis.shape[0]} unique emojis')
LOGGER.error(f'{self.reactions.index.get_level_values(1)}')
raise
@property
def unique_emojis(self) -> pd.Index:
return self.reactions.index.get_level_values(1).drop_duplicates()
def emoji_totals(self, emoji_name: str, days: int = None) -> pd.Series:
"""Creates a Series indexed by user id and with the number of reactions with emoji_name as values"""
messages = self.emoji_messages(emoji_name, days)
if messages.shape[0] > 0:
return (messages
.groupby('user id')
.apply(lambda gdf: gdf['count'].sum())
.sort_values(ascending=False))
else:
raise ValueError(f'No messages found for' + \
f' {type(emoji_name)}:{emoji_name}, {type(days)}:{days}')
# return pd.DataFrame()
async def emoji_user_counts(self, client: Client, emoji_name: str, days: int = None): async def emoji_user_counts(self, client: Client, emoji_name: str, days: int = None):
"""Creates a Series indexed by user display_name with the number of reactions with emoji_name as values""" """Creates a Series indexed by user display_name with the number of reactions with emoji_name as values"""
counts: pd.Series = self.emoji_totals(emoji_name, days) counts: pd.Series = self.emoji_totals(emoji_name, days)

View File

@@ -69,25 +69,45 @@ class Kwaylon(Client):
LOGGER.info(emoji) LOGGER.info(emoji)
if (most := self.data.most(emoji=emoji)) is not None: if (most := self.data.most(emoji=emoji)) is not None:
LOGGER.info(f'\n{str(most)}') # LOGGER.info(f'\n{str(most)}')
kwargs = {'emoji_name': emoji}
if (day_match := re.search('(?P<days>\d+) days', message.content)): if (day_match := re.search('(?P<days>\d+) days', message.content)):
start = (datetime.today() - timedelta(days=int(day_match.group('days')))).astimezone() days = int(day_match.group('days'))
valid_dates = most['datetime'] > start kwargs['days'] = days
most = most.loc[valid_dates]
if most.shape[0] > 0: if 'leaderboard' in message.content:
most = most.iloc[0] await message.reply(await self.leaderboard(**kwargs))
msg = await self.data.fetch_message(self, most) else:
await message.reply(f'{msg.jump_url}') if most.shape[0] > 0:
LOGGER.info(f'{msg.clean_content}') most = most.iloc[0]
LOGGER.info(f' - {msg.author}') msg = await self.data.fetch_message(self, most)
LOGGER.info(f'{most["count"]}x {emoji}') await message.reply(f'{msg.jump_url}')
LOGGER.info(f'{msg.clean_content}')
LOGGER.info(f' - {msg.author}')
LOGGER.info(f'{most["count"]}x {emoji}')
for joke in self.jokes: for joke in self.jokes:
if (m := joke.scan(message)) is not None: if (m := joke.scan(message)) is not None:
LOGGER.info(f'{joke.__class__.__name__} detected: {message.content}, {m.group()}') LOGGER.info(f'{joke.__class__.__name__} detected: {message.content}, {m.group()}')
await joke.respond(message, self, m) await joke.respond(message, self, m)
async def leaderboard(self, emoji_name: str, days: int = 14) -> str:
df = self.data.most(emoji=emoji_name)
start = (datetime.today() - timedelta(days=days)).astimezone()
valid_dates = df['datetime'] > start
df = df.loc[valid_dates]
df = df.groupby('auth_id').sum()
counts = df['count'].sort_values(ascending=False)
counts.index = [(await self.fetch_user(idx)).display_name for idx in counts.index]
width = max([len(str(s)) for s in counts.index])
res = f'{emoji_name} totals, past {days} days\n'
res += '\n'.join(f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`"
for name, cnt in counts.iteritems())
return res
# async def handle_raw_reaction(self, payload: RawReactionActionEvent): # async def handle_raw_reaction(self, payload: RawReactionActionEvent):
# LOGGER.info(payload) # LOGGER.info(payload)
# guild = await self.fetch_guild(payload.guild_id) # guild = await self.fetch_guild(payload.guild_id)
@@ -104,45 +124,6 @@ class Kwaylon(Client):
# if hasattr(self, 'data'): # if hasattr(self, 'data'):
# await self.data.update_reaction(msg=message) # await self.data.update_reaction(msg=message)
# async def leaderboard(self, match: re.Match) -> str:
# emoji_name = get_emoji_name(match.group('emoji'))
# days = match.group('days') or 14
# days = int(days)
# counts = await self.data.emoji_user_counts(client=self,
# emoji_name=emoji_name,
# days=days)
# width = max([len(str(s)) for s in counts.index.values])
# res = f'{match.group("emoji")} totals, past {days} days\n'
# res += '\n'.join(f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`"
# for name, cnt in counts.iteritems())
# return res
#
# async def biggest_single(self, match: re.Match) -> str:
# days = match.group('days') or 14
# days = int(days)
# data: pd.Series = self.data.emoji_totals(
# emoji_name=get_emoji_name(match.group('emoji')),
# days=days
# )
# user: User = await self.fetch_user(user_id=data.index[0])
# LOGGER.info(f'User: {user.mention}')
# msg = f'{user.mention} with {data.iloc[0]:.0f}x {match.group("emoji")} over the past {days} days'
# msg += '\n' + await self.worst_offsenses(user=user, days=days, top=3, emoji_str=match.group('emoji'))
# return msg
#
# async def worst_offsenses(self, user: User, emoji_str: str, days: int = None, top: int = 3) -> str:
# df: pd.DataFrame = self.data.emoji_messages(get_emoji_name(emoji_str), days=days)
# df: pd.DataFrame = df[df['user id'] == user.id].sort_values('count', ascending=False).iloc[:top]
#
# if df.shape[0] > 0:
# res = f'Top {top} {emoji_str}\n'
# res += f'\n'.join(
# f'{emoji_str}x{row["count"]:.0f}\n{row["link"]}' for idx, row in df.iterrows())
# else:
# res = f'No {emoji_str} for {user} in the past {days} days'
#
# return res
def get_emoji_name(string: str) -> str: def get_emoji_name(string: str) -> str:
if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)): if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)):