From 706b6732eb52a7957aeee89f3d7fa552775bef66 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 11 Jan 2023 16:59:47 +0100 Subject: [PATCH 1/5] Fixed ontime calculation #166 --- .../bot_core/abc/client_utils_service_abc.py | 5 ++++ .../bot_core/service/client_utils_service.py | 12 +++++++- .../src/modules/base/command/user_group.py | 28 +++++++++++-------- .../base_on_voice_state_update_event.py | 11 ++++++-- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/kdb-bot/src/bot_core/abc/client_utils_service_abc.py b/kdb-bot/src/bot_core/abc/client_utils_service_abc.py index ca6405ef..531e21c3 100644 --- a/kdb-bot/src/bot_core/abc/client_utils_service_abc.py +++ b/kdb-bot/src/bot_core/abc/client_utils_service_abc.py @@ -4,6 +4,8 @@ from typing import Callable from cpl_query.extension import List from discord.ext.commands import Context +from bot_data.model.user import User + class ClientUtilsServiceABC(ABC): @@ -33,3 +35,6 @@ class ClientUtilsServiceABC(ABC): @abstractmethod def get_auto_complete_list(self, _l: List, current: str, select: Callable = None) -> List: pass + + @abstractmethod + def get_ontime_for_user(self, user: User) -> float: pass diff --git a/kdb-bot/src/bot_core/service/client_utils_service.py b/kdb-bot/src/bot_core/service/client_utils_service.py index bf2aaef1..c4bfaba2 100644 --- a/kdb-bot/src/bot_core/service/client_utils_service.py +++ b/kdb-bot/src/bot_core/service/client_utils_service.py @@ -17,6 +17,8 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.model.user import User class ClientUtilsService(ClientUtilsServiceABC): @@ -28,6 +30,7 @@ class ClientUtilsService(ClientUtilsServiceABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, clients: ClientRepositoryABC, + user_joined_vc: UserJoinedVoiceChannelRepositoryABC, message_service: MessageServiceABC, db: DatabaseContextABC, t: TranslatePipe, @@ -39,6 +42,7 @@ class ClientUtilsService(ClientUtilsServiceABC): self._bot = bot self._servers = servers self._clients = clients + self._user_joined_voice_channel = user_joined_vc self._message_service = message_service self._db = db self._t = t @@ -80,7 +84,8 @@ class ClientUtilsService(ClientUtilsServiceABC): async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: result = await self.check_if_bot_is_ready_yet() if not result: - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), without_tracking=True) + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), + without_tracking=True) return result @@ -106,3 +111,8 @@ class ClientUtilsService(ClientUtilsServiceABC): _l = _l.where(lambda x: x.name in sl) return _l.take(25) + + def get_ontime_for_user(self, user: User) -> float: + return self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id) \ + .where(lambda x: x.leaved_on is not None and x.joined_on is not None) \ + .sum(lambda join: round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2)) diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index 5725e5b6..6ca75b90 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -98,10 +98,6 @@ class UserGroup(DiscordCommandABC): color=int('ef9d0d', 16) ) - ontime = self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id)\ - .where(lambda x: x.leaved_on is not None and x.joined_on is not None)\ - .sum(lambda join: round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2)) - embed.add_field(name=self._t.transform('modules.base.user.atr.id'), value=member.id) embed.add_field(name=self._t.transform('modules.base.user.atr.name'), value=member.name) embed.add_field(name=self._t.transform('modules.base.user.atr.discord_join'), @@ -109,7 +105,8 @@ class UserGroup(DiscordCommandABC): embed.add_field(name=self._t.transform('modules.base.user.atr.last_join'), value=self._date.transform(member.joined_at), inline=False) embed.add_field(name=self._t.transform('modules.base.user.atr.xp'), value=str(user.xp)) - embed.add_field(name=self._t.transform('modules.base.user.atr.ontime'), value=str(ontime)) + embed.add_field(name=self._t.transform('modules.base.user.atr.ontime'), + value=str(self._client_utils.get_ontime_for_user(user))) roles = '' for role in member.roles: @@ -169,7 +166,8 @@ class UserGroup(DiscordCommandABC): )) else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + 'modules.base.user.error.atr_not_found').format(atr)) return await self._message_service.send_interaction_msg( @@ -196,14 +194,16 @@ class UserGroup(DiscordCommandABC): if atr == 'xp': if not value.isnumeric(): - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.set.error.value_type_not_numeric')) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + 'modules.base.user.set.error.value_type_not_numeric')) return try: user.xp = int(value) except TypeError as te: self._logger.error(__name__, f'String value couldn\'t be converted to int', te) - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.set.error.type_error')) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + 'modules.base.user.set.error.type_error')) return else: self._users.update_user(user) @@ -211,10 +211,12 @@ class UserGroup(DiscordCommandABC): await self._level.check_level(member) else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + 'modules.base.user.error.atr_not_found').format(atr)) return - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform(f'modules.base.user.set.{atr.lower()}').format(member.mention, value)) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + f'modules.base.user.set.{atr.lower()}').format(member.mention, value)) @set.autocomplete('atr') async def set_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: @@ -245,7 +247,8 @@ class UserGroup(DiscordCommandABC): self._db.save_changes() else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( + 'modules.base.user.error.atr_not_found').format(atr)) return await self._message_service.send_interaction_msg( @@ -254,5 +257,6 @@ class UserGroup(DiscordCommandABC): ) @remove.autocomplete('atr') - async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: + async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> List[ + app_commands.Choice[str]]: return [app_commands.Choice(name=value, value=key) for key, value in self._atr_list] diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py index 83794725..1eeb6424 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py @@ -7,6 +7,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnVoiceStateUpdateABC +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.helper.event_checks import EventChecks from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -32,6 +33,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): users: UserRepositoryABC, user_joins: UserJoinedServerRepositoryABC, user_joins_vc: UserJoinedVoiceChannelRepositoryABC, + client_utils: ClientUtilsServiceABC, db: DatabaseContextABC, ): OnVoiceStateUpdateABC.__init__(self) @@ -43,6 +45,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._users = users self._user_joins = user_joins self._user_joins_vc = user_joins_vc + self._client_utils = client_utils self._db = db self._logger.info(__name__, f'Module {type(self)} loaded') @@ -72,7 +75,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): join.leaved_on = datetime.now() # ontime as hours - ontime = round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2) + ontime = self._client_utils.get_ontime_for_user(user) old_xp = user.xp user.xp += round(ontime * settings.xp_per_ontime_hour) @@ -80,12 +83,14 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._users.update_user(user) self._db.save_changes() - self._logger.debug(__name__, f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') + self._logger.debug(__name__, + f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') except Exception as e: self._logger.error(__name__, f'Ontime validation failed', e) @EventChecks.check_is_ready() - async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, + after: discord.VoiceState): self._logger.debug(__name__, f'Module {type(self)} started') self._logger.trace(__name__, f'Detected on_voice_state_update {member.id} from {before} to {after}') settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) From b89fa12ec66773b0fa7bb36e82f50055ee3138d0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 11 Jan 2023 17:10:02 +0100 Subject: [PATCH 2/5] Fixed level service #166 --- kdb-bot/src/modules/level/service/level_service.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index e49d6f31..7bd7f2d6 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -42,8 +42,15 @@ class LevelService: self._t = t def get_level(self, user: User) -> Level: - levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) - return levels.where(lambda l: user.xp >= l.min_xp).last() + levels_by_server = self._levels.get_levels_by_server_id(user.server.server_id) + levels = levels_by_server \ + .order_by(lambda l: l.min_xp) \ + .where(lambda l: user.xp >= l.min_xp) + + if levels.count() == 0: + return levels_by_server.order_by(lambda l: l.min_xp).last() + + return levels.last() async def set_level(self, user: User): level_names = self._levels.get_levels_by_server_id(user.server.server_id).select(lambda l: l.name) From b80958e3abd8874bf81e59868f439d5d3f369346 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 11 Jan 2023 17:17:50 +0100 Subject: [PATCH 3/5] Fixed vc_state_update #166 --- .../src/bot_data/service/user_joined_voice_channel_service.py | 2 +- .../modules/base/events/base_on_voice_state_update_event.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py b/kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py index 919b2ee5..4d99065a 100644 --- a/kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py +++ b/kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py @@ -94,7 +94,7 @@ class UserJoinedVoiceChannelRepositoryService(UserJoinedVoiceChannelRepositoryAB ) def find_active_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[Optional[UserJoinedVoiceChannel]]: - self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_by_user_id_string(user_id)}') + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_active_by_user_id_string(user_id)}') result = List(UserJoinedVoiceChannel) db_results = self._context.select(UserJoinedVoiceChannel.get_select_active_by_user_id_string(user_id)) diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py index 1eeb6424..a3f36f41 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py @@ -83,8 +83,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._users.update_user(user) self._db.save_changes() - self._logger.debug(__name__, - f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') + self._logger.debug(__name__, f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') except Exception as e: self._logger.error(__name__, f'Ontime validation failed', e) From 3cf0fe34798f6c12b720afcfc10a66b6e3df11fb Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 12 Jan 2023 07:20:00 +0100 Subject: [PATCH 4/5] Improved formatting #166 --- .../bot_core/service/client_utils_service.py | 7 +++- .../src/modules/base/command/user_group.py | 38 ++++++++++++------- .../base_on_voice_state_update_event.py | 11 ++++-- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/kdb-bot/src/bot_core/service/client_utils_service.py b/kdb-bot/src/bot_core/service/client_utils_service.py index c4bfaba2..48d87d84 100644 --- a/kdb-bot/src/bot_core/service/client_utils_service.py +++ b/kdb-bot/src/bot_core/service/client_utils_service.py @@ -84,8 +84,11 @@ class ClientUtilsService(ClientUtilsServiceABC): async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: result = await self.check_if_bot_is_ready_yet() if not result: - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), - without_tracking=True) + await self._message_service.send_ctx_msg( + ctx, + self._t.transform('common.errors.bot_not_ready_yet'), + without_tracking=True + ) return result diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index 6ca75b90..f0b5844e 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -166,8 +166,10 @@ class UserGroup(DiscordCommandABC): )) else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - 'modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg( + ctx.interaction, + self._t.transform('modules.base.user.error.atr_not_found').format(atr) + ) return await self._message_service.send_interaction_msg( @@ -194,16 +196,19 @@ class UserGroup(DiscordCommandABC): if atr == 'xp': if not value.isnumeric(): - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - 'modules.base.user.set.error.value_type_not_numeric')) + await self._message_service.send_interaction_msg( + ctx.interaction, self._t.transform('modules.base.user.set.error.value_type_not_numeric') + ) return try: user.xp = int(value) except TypeError as te: self._logger.error(__name__, f'String value couldn\'t be converted to int', te) - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - 'modules.base.user.set.error.type_error')) + await self._message_service.send_interaction_msg( + ctx.interaction, + self._t.transform('modules.base.user.set.error.type_error') + ) return else: self._users.update_user(user) @@ -211,12 +216,16 @@ class UserGroup(DiscordCommandABC): await self._level.check_level(member) else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - 'modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg( + ctx.interaction, + self._t.transform('modules.base.user.error.atr_not_found').format(atr) + ) return - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - f'modules.base.user.set.{atr.lower()}').format(member.mention, value)) + await self._message_service.send_interaction_msg( + ctx.interaction, + self._t.transform(f'modules.base.user.set.{atr.lower()}').format(member.mention, value) + ) @set.autocomplete('atr') async def set_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: @@ -247,8 +256,10 @@ class UserGroup(DiscordCommandABC): self._db.save_changes() else: - await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform( - 'modules.base.user.error.atr_not_found').format(atr)) + await self._message_service.send_interaction_msg( + ctx.interaction, + self._t.transform('modules.base.user.error.atr_not_found').format(atr) + ) return await self._message_service.send_interaction_msg( @@ -258,5 +269,6 @@ class UserGroup(DiscordCommandABC): @remove.autocomplete('atr') async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> List[ - app_commands.Choice[str]]: + app_commands.Choice[str] + ]: return [app_commands.Choice(name=value, value=key) for key, value in self._atr_list] diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py index a3f36f41..c9fcd41b 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py @@ -83,13 +83,18 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._users.update_user(user) self._db.save_changes() - self._logger.debug(__name__, f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') + self._logger.debug(__name__, + f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') except Exception as e: self._logger.error(__name__, f'Ontime validation failed', e) @EventChecks.check_is_ready() - async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, - after: discord.VoiceState): + async def on_voice_state_update( + self, + member: discord.Member, + before: discord.VoiceState, + after: discord.VoiceState + ): self._logger.debug(__name__, f'Module {type(self)} started') self._logger.trace(__name__, f'Detected on_voice_state_update {member.id} from {before} to {after}') settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) From 34ebb48c831b25f56e89fb7950ae09903355bc14 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 13 Jan 2023 12:23:49 +0100 Subject: [PATCH 5/5] Centralised ontime calculation #166 --- kdb-bot/src/modules/base/command/user_group.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index f0b5844e..b0f41230 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -9,7 +9,6 @@ from cpl_translation import TranslatePipe from discord import app_commands from discord.ext import commands from discord.ext.commands import Context -from mysql.connector.errors import DatabaseError from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC @@ -159,11 +158,7 @@ class UserGroup(DiscordCommandABC): value = str(user.xp) elif atr == 'ontime': - value = str(round( - self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id) - .sum(lambda join: (join.leaved_on - join.joined_on).total_seconds() / 3600), - 2 - )) + value = str(self._client_utils.get_ontime_for_user(user)) else: await self._message_service.send_interaction_msg(