From 6d4133a04d79b3313c352e536788333af491d4f8 Mon Sep 17 00:00:00 2001 From: Dmitrii Morozov Date: Fri, 21 Jun 2024 20:17:43 +0200 Subject: Reworked statistics --- formatter/__init__.py | 4 ++- main.py | 4 +-- persistence/__init__.py | 30 +++++++++++---------- telegram_bot/__init__.py | 12 ++++++++- telegram_bot/commands.py | 68 +++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 88 insertions(+), 30 deletions(-) diff --git a/formatter/__init__.py b/formatter/__init__.py index edc1aa9..71ea150 100644 --- a/formatter/__init__.py +++ b/formatter/__init__.py @@ -40,10 +40,11 @@ def format_user(user: User): def format_user_stats_list(stats: typing.List[UserStats]): stats_formatted = [__format_stats(single_stats) for single_stats in stats] return formatting.format_text( + formatting.mbold('Current Fortnite statistics\n'), '\n\n'.join(stats_formatted), separator='\n') -def format_user_stats_difference(old_user_stats: typing.List[UserStats], new_user_stats: typing.List[UserStats]): +def format_user_stats_difference(old_user_stats: typing.List[UserStats], new_user_stats: typing.List[UserStats], timeframe_alias: str): stats_formatted = [] for stats in old_user_stats: @@ -51,6 +52,7 @@ def format_user_stats_difference(old_user_stats: typing.List[UserStats], new_use stats_formatted.append(__format_stats_difference(stats, matched)) return formatting.format_text( + formatting.mbold('Fortnite statistics for last {alias}\n'.format(alias = timeframe_alias)), '\n\n'.join(stats_formatted), separator='\n') diff --git a/main.py b/main.py index 62b2b43..03f889e 100755 --- a/main.py +++ b/main.py @@ -35,8 +35,8 @@ record_stats_command = RecordStatsCommand(fortnite_client, stats_repository) telegram_bot.register_command_handler('start', StartCommand(telegram_bot, user_repository)) telegram_bot.register_command_handler('status', GetStatusCommand(telegram_bot)) telegram_bot.register_command_handler('friends', GetFriendsCommand(telegram_bot, fortnite_client)) -telegram_bot.register_command_handler('stats', GetStatsCommand(telegram_bot, fortnite_client)) -telegram_bot.register_command_handler('todaystats', GetTodayStatsCommand(telegram_bot, fortnite_client, stats_repository)) +telegram_bot.register_command_handler('stats', GetStatsCommand(telegram_bot, fortnite_client, stats_repository)) +telegram_bot.register_command_handler('todaystats', GetTodayStatsCommand(telegram_bot)) telegram_bot.register_command_handler('recordstats', record_stats_command) async def run_tgbot(): diff --git a/persistence/__init__.py b/persistence/__init__.py index 9d9220e..663522e 100644 --- a/persistence/__init__.py +++ b/persistence/__init__.py @@ -1,5 +1,6 @@ import sqlite3, typing from app_types import * +from datetime import date class UserRepository: @@ -17,21 +18,21 @@ class UserRepository: def get_user(self, chat_id): connection = self.__get_connection() cur = connection.cursor() - query = "select * from user where chat_id = {chat_id}".format(chat_id = chat_id) + query = "SELECT * FROM user WHERE chat_id = {chat_id}".format(chat_id = chat_id) cur.execute(query) return cur.fetchone() def get_all_users(self): connection = self.__get_connection() cur = connection.cursor() - query = "select * from user" + query = "SELECT * FROM user" cur.execute(query) return cur.fetchall() def remove_chat(self, chat_id): connection = self.__get_connection() cur = connection.cursor() - query = "DELETE FROM user where chat_id = {chat_id}".format( + query = "DELETE FROM user WHERE chat_id = {chat_id}".format( chat_id = chat_id) cur.execute(query) connection.commit() @@ -57,16 +58,17 @@ class StatsRepository: def __initialize(self): cur = self.__get_connection().cursor() - cur.execute("CREATE TABLE IF NOT EXISTS stats(user_id TEXT, display_name TEXT, level INT, matches_played INT, kills INT, wins INT)") - cur.execute("CREATE UNIQUE INDEX IF NOT EXISTS user_id_idx ON stats(user_id)") + cur.execute("CREATE TABLE IF NOT EXISTS stats(user_id TEXT, stats_date DATE, display_name TEXT, level INT, matches_played INT, kills INT, wins INT)") + cur.execute("CREATE UNIQUE INDEX IF NOT EXISTS user_id_date_idx ON stats(user_id, stats_date)") - async def put_stats(self, user: User): + async def put_stats(self, user: User, stats_date: date = date.today()): stats: UserStats = await user.fetch_stats() connection = self.__get_connection() cur = connection.cursor() - query = "INSERT OR REPLACE INTO stats(user_id, display_name, level, matches_played, kills, wins) VALUES('{user_id}', '{display_name}', {level}, {matches_played}, {kills}, {wins})".format( + query = "INSERT OR REPLACE INTO stats(user_id, stats_date, display_name, level, matches_played, kills, wins) VALUES('{user_id}', '{stats_date}', '{display_name}', {level}, {matches_played}, {kills}, {wins})".format( user_id = user.id, + stats_date = stats_date, display_name = user.display_name, level = stats.level, matches_played = stats.matches_played, @@ -75,10 +77,10 @@ class StatsRepository: cur.execute(query) connection.commit() - def get_stats(self) -> typing.List[UserStats]: + def get_stats(self, stats_date: date = date.today()) -> typing.List[UserStats]: connection = self.__get_connection() cur = connection.cursor() - query = "SELECT * FROM stats" + query = "SELECT * FROM stats WHERE stats_date IN (SELECT MIN(stats_date) FROM stats WHERE stats_date >= '{stats_date}')".format(stats_date = stats_date) cur.execute(query) result = cur.fetchall() return [self.__map_from_db(stats) for stats in result] @@ -86,11 +88,11 @@ class StatsRepository: def __map_from_db(self, record): user_stats = UserStats() user_stats.user_id = str(record[0]) - user_stats.user_display_name = str(record[1]) - user_stats.level = int(record[2]) - user_stats.matches_played = int(record[3]) - user_stats.kills = int(record[4]) - user_stats.wins = int(record[5]) + user_stats.user_display_name = str(record[2]) + user_stats.level = int(record[3]) + user_stats.matches_played = int(record[4]) + user_stats.kills = int(record[5]) + user_stats.wins = int(record[6]) return user_stats class PresenceRepository: diff --git a/telegram_bot/__init__.py b/telegram_bot/__init__.py index 5e3d455..0818d67 100644 --- a/telegram_bot/__init__.py +++ b/telegram_bot/__init__.py @@ -10,6 +10,10 @@ class CommandHandler: async def handle(self, message: telebot.types.Message): pass +class CallbackQueryHandler: + async def handle(self, call: telebot.types.CallbackQuery): + pass + class TelegramBot: __bot: AsyncTeleBot __user_repository: UserRepository @@ -35,6 +39,11 @@ class TelegramBot: command_handler.handle, commands=[ command ]) + def register_callback_query(self, callback_query_handler: CallbackQueryHandler, filter: typing.Callable): + self.__bot.register_callback_query_handler( + callback=callback_query_handler.handle, + func=filter) + async def send_message_to_all(self, message_text: str): for user in self.__user_repository.get_all_users(): try: @@ -47,8 +56,9 @@ class TelegramBot: if 'bot was kicked from the group chat' in str(error): self.__user_repository.remove_chat(user[0]) - async def reply(self, message, message_text): + async def reply(self, message, message_text, reply_markup = None): await self.__bot.reply_to( message, message_text, + reply_markup=reply_markup, parse_mode='MarkdownV2') \ No newline at end of file diff --git a/telegram_bot/commands.py b/telegram_bot/commands.py index 1e3295b..a1a2888 100644 --- a/telegram_bot/commands.py +++ b/telegram_bot/commands.py @@ -1,10 +1,16 @@ import telebot +from datetime import date, datetime from telegram_bot import * from formatter import * from persistence import * from fortnite_client import * from fortnite_status import * +__stats_now__ = 'stats_now' +__stats_day__ = 'stats_day' +__stats_week__ = 'stats_week' +__stats_month__ = 'stats_month' + class StartCommand(CommandHandler): __telegram_bot: TelegramBot @@ -48,39 +54,77 @@ class GetFriendsCommand(CommandHandler): friends = await self.__fortnite_client.get_friends() await self.__telegram_bot.reply(message, format_users(friends)) - -class GetStatsCommand(CommandHandler): +class GetStatsCallbackQueryHandler(CallbackQueryHandler): __telegram_bot: TelegramBot __fortnite_client: FortniteClient + __stats_repository: StatsRepository - def __init__(self, telegram_bot: TelegramBot, fortnite_client: FortniteClient): + def __init__(self, telegram_bot: TelegramBot, fortnite_client: FortniteClient, stats_repository: StatsRepository): self.__telegram_bot = telegram_bot self.__fortnite_client = fortnite_client + self.__stats_repository = stats_repository - async def handle(self, message: telebot.types.Message): + async def handle(self, call: telebot.types.CallbackQuery): if self.__fortnite_client.is_initialized(): + if call.data == __stats_now__: + await self.reply_with_today_stats(call.message) + elif call.data == __stats_day__: + await self.reply_with_stats_days_difference(call.message, 1, 'day') + elif call.data == __stats_week__: + await self.reply_with_stats_days_difference(call.message, 7, 'week') + elif call.data == __stats_month__: + await self.reply_with_stats_days_difference(call.message, 30, 'month') + + async def reply_with_today_stats(self, message): + friends = await self.__fortnite_client.get_friends() + stats = [await friend.fetch_stats() for friend in friends] + await self.__telegram_bot.reply(message, format_user_stats_list(stats)) + + async def reply_with_stats_days_difference(self, message, days: int, timeframe_alias: str): + stats_date = date.today() - datetime.timedelta(days) + persisted_stats = self.__stats_repository.get_stats(stats_date) + if len(persisted_stats) > 0: friends = await self.__fortnite_client.get_friends() - stats = [await friend.fetch_stats() for friend in friends] - await self.__telegram_bot.reply(message, format_user_stats_list(stats)) + current_stats = [await friend.fetch_stats() for friend in friends] + await self.__telegram_bot.reply(message, format_user_stats_difference(persisted_stats, current_stats, timeframe_alias)) + else: + await self.__telegram_bot.reply(message, 'No stats available yet for selected time period') -class GetTodayStatsCommand(CommandHandler): +class GetStatsCommand(CommandHandler): __telegram_bot: TelegramBot __fortnite_client: FortniteClient __stats_repository: StatsRepository + __reply_markup: any def __init__(self, telegram_bot: TelegramBot, fortnite_client: FortniteClient, stats_repository: StatsRepository): self.__telegram_bot = telegram_bot self.__fortnite_client = fortnite_client self.__stats_repository = stats_repository + self.__reply_markup = telebot.types.InlineKeyboardMarkup() + self.__reply_markup.add(telebot.types.InlineKeyboardButton('Now', callback_data=__stats_now__)) + self.__reply_markup.add(telebot.types.InlineKeyboardButton('1 day', callback_data=__stats_day__)) + self.__reply_markup.add(telebot.types.InlineKeyboardButton('1 week', callback_data=__stats_week__)) + self.__reply_markup.add(telebot.types.InlineKeyboardButton('1 month', callback_data=__stats_month__)) + + self.__telegram_bot.register_callback_query( + GetStatsCallbackQueryHandler(self.__telegram_bot, self.__fortnite_client, self.__stats_repository), + lambda call: True) + async def handle(self, message: telebot.types.Message): - if self.__fortnite_client.is_initialized(): - persisted_stats = self.__stats_repository.get_stats() - friends = await self.__fortnite_client.get_friends() - current_stats = [await friend.fetch_stats() for friend in friends] - await self.__telegram_bot.reply(message, format_user_stats_difference(persisted_stats, current_stats)) + await self.__telegram_bot.reply(message, 'Which statistics would you like to see?', reply_markup=self.__reply_markup) + +class GetTodayStatsCommand(CommandHandler): + + __telegram_bot: TelegramBot + + def __init__(self, telegram_bot: TelegramBot): + self.__telegram_bot = telegram_bot + + async def handle(self, message: telebot.types.Message): + await self.__telegram_bot.reply(message, 'Use new command /stats, you must, young Padawan') class RecordStatsCommand(CommandHandler): -- cgit v1.2.3