|  |  |  | @@ -2,12 +2,14 @@ from typing import Optional, List | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | import discord | 
		
	
		
			
				|  |  |  |  | from cpl_core.configuration import ConfigurationABC | 
		
	
		
			
				|  |  |  |  | from cpl_core.database.context import DatabaseContextABC | 
		
	
		
			
				|  |  |  |  | from cpl_discord.command import DiscordCommandABC | 
		
	
		
			
				|  |  |  |  | from cpl_discord.service import DiscordBotServiceABC | 
		
	
		
			
				|  |  |  |  | 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 | 
		
	
	
		
			
				
					
					|  |  |  | @@ -18,6 +20,7 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC | 
		
	
		
			
				|  |  |  |  | from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC | 
		
	
		
			
				|  |  |  |  | from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC | 
		
	
		
			
				|  |  |  |  | from bot_data.abc.user_repository_abc import UserRepositoryABC | 
		
	
		
			
				|  |  |  |  | from modules.level.service.level_service import LevelService | 
		
	
		
			
				|  |  |  |  | from modules.permission.abc.permission_service_abc import PermissionServiceABC | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -32,11 +35,13 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |             client_utils: ClientUtilsServiceABC, | 
		
	
		
			
				|  |  |  |  |             permissions: PermissionServiceABC, | 
		
	
		
			
				|  |  |  |  |             servers: ServerRepositoryABC, | 
		
	
		
			
				|  |  |  |  |             db: DatabaseContextABC, | 
		
	
		
			
				|  |  |  |  |             users: UserRepositoryABC, | 
		
	
		
			
				|  |  |  |  |             user_joined_servers: UserJoinedServerRepositoryABC, | 
		
	
		
			
				|  |  |  |  |             user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, | 
		
	
		
			
				|  |  |  |  |             translate: TranslatePipe, | 
		
	
		
			
				|  |  |  |  |             date: DateTimeOffsetPipe | 
		
	
		
			
				|  |  |  |  |             date: DateTimeOffsetPipe, | 
		
	
		
			
				|  |  |  |  |             level: LevelService | 
		
	
		
			
				|  |  |  |  |     ): | 
		
	
		
			
				|  |  |  |  |         DiscordCommandABC.__init__(self) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -47,14 +52,21 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |         self._client_utils = client_utils | 
		
	
		
			
				|  |  |  |  |         self._permissions = permissions | 
		
	
		
			
				|  |  |  |  |         self._servers = servers | 
		
	
		
			
				|  |  |  |  |         self._db = db | 
		
	
		
			
				|  |  |  |  |         self._users = users | 
		
	
		
			
				|  |  |  |  |         self._user_joined_servers = user_joined_servers | 
		
	
		
			
				|  |  |  |  |         self._user_joined_voice_channel = user_joined_voice_channel | 
		
	
		
			
				|  |  |  |  |         self._t = translate | 
		
	
		
			
				|  |  |  |  |         self._date = date | 
		
	
		
			
				|  |  |  |  |         self._level = level | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         self._atr_dict = { | 
		
	
		
			
				|  |  |  |  |             "xp": self._t.transform('modules.base.user.atr.xp'), | 
		
	
		
			
				|  |  |  |  |             'ontime': self._t.transform('modules.base.user.atr.ontime') | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     @commands.hybrid_group() | 
		
	
		
			
				|  |  |  |  |     @commands.guild_only() | 
		
	
		
			
				|  |  |  |  |     async def user(self, ctx: Context): | 
		
	
	
		
			
				
					
					|  |  |  | @@ -87,25 +99,25 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |         ontime = self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id).sum( | 
		
	
		
			
				|  |  |  |  |             lambda join: round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2)) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.id'), value=member.id) | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.name'), value=member.name) | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.discord_join'), | 
		
	
		
			
				|  |  |  |  |         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'), | 
		
	
		
			
				|  |  |  |  |                         value=self._date.transform(member.created_at), inline=False) | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.last_join'), | 
		
	
		
			
				|  |  |  |  |         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.info.fields.xp'), value=str(user.xp)) | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.ontime'), value=str(ontime)) | 
		
	
		
			
				|  |  |  |  |         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)) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         roles = '' | 
		
	
		
			
				|  |  |  |  |         for role in member.roles: | 
		
	
		
			
				|  |  |  |  |             roles += f'{role.name}\n' | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.info.fields.roles'), value=roles, inline=False) | 
		
	
		
			
				|  |  |  |  |         embed.add_field(name=self._t.transform('modules.base.user.atr.roles'), value=roles, inline=False) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if is_mod or member == ctx.author: | 
		
	
		
			
				|  |  |  |  |             joins_string = '' | 
		
	
		
			
				|  |  |  |  |             for join in joins: | 
		
	
		
			
				|  |  |  |  |                 joins_string += f'{self._date.transform(join.joined_on)}\n' | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.info.fields.joins'), value=joins_string) | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.atr.joins'), value=joins_string) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if is_mod or member == ctx.author: | 
		
	
		
			
				|  |  |  |  |             lefts_string = '' | 
		
	
	
		
			
				
					
					|  |  |  | @@ -116,10 +128,10 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |                     continue | 
		
	
		
			
				|  |  |  |  |                 lefts_string += f'{self._date.transform(join.leaved_on)}\n' | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.info.fields.lefts'), value=lefts_string) | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.atr.lefts'), value=lefts_string) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if is_mod or member == ctx.author: | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.info.fields.warnings'), | 
		
	
		
			
				|  |  |  |  |             embed.add_field(name=self._t.transform('modules.base.user.atr.warnings'), | 
		
	
		
			
				|  |  |  |  |                             value=self._t.transform('common.not_implemented_yet'), inline=False) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         # send to interaction because of sensitive data | 
		
	
	
		
			
				
					
					|  |  |  | @@ -130,7 +142,7 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |     @commands.guild_only() | 
		
	
		
			
				|  |  |  |  |     @CommandChecks.check_is_ready() | 
		
	
		
			
				|  |  |  |  |     async def get(self, ctx: Context, atr: str, member: discord.Member = None): | 
		
	
		
			
				|  |  |  |  |         self._logger.debug(__name__, f'Received command user-info {ctx}:{member}') | 
		
	
		
			
				|  |  |  |  |         self._logger.debug(__name__, f'Received command user-get {ctx}:{member}') | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         is_mod = self._permissions.is_member_moderator(ctx.author) | 
		
	
		
			
				|  |  |  |  |         if member is not None and not is_mod: | 
		
	
	
		
			
				
					
					|  |  |  | @@ -143,20 +155,19 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |         server = self._servers.find_server_by_discord_id(ctx.guild.id) | 
		
	
		
			
				|  |  |  |  |         user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         match atr: | 
		
	
		
			
				|  |  |  |  |             case 'xp': | 
		
	
		
			
				|  |  |  |  |                 value = str(user.xp) | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             case '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 | 
		
	
		
			
				|  |  |  |  |                 )) | 
		
	
		
			
				|  |  |  |  |         if atr == self._atr_dict["xp"]: | 
		
	
		
			
				|  |  |  |  |             value = str(user.xp) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             case other: | 
		
	
		
			
				|  |  |  |  |                 await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.base.user.get.atr_not_found').format(atr)) | 
		
	
		
			
				|  |  |  |  |                 return | 
		
	
		
			
				|  |  |  |  |         elif atr == self._atr_dict["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 | 
		
	
		
			
				|  |  |  |  |             )) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         else: | 
		
	
		
			
				|  |  |  |  |             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, | 
		
	
	
		
			
				
					
					|  |  |  | @@ -165,5 +176,51 @@ class UserGroup(DiscordCommandABC): | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     @get.autocomplete('atr') | 
		
	
		
			
				|  |  |  |  |     async def get_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: | 
		
	
		
			
				|  |  |  |  |         atr_list = ['xp', 'ontime'] | 
		
	
		
			
				|  |  |  |  |         atr_list = [self._atr_dict["xp"], self._atr_dict["ontime"]] | 
		
	
		
			
				|  |  |  |  |         return [app_commands.Choice(name=atr, value=atr) for atr in atr_list] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     @user.command() | 
		
	
		
			
				|  |  |  |  |     @commands.guild_only() | 
		
	
		
			
				|  |  |  |  |     @CommandChecks.check_is_ready() | 
		
	
		
			
				|  |  |  |  |     @CommandChecks.check_is_member_moderator() | 
		
	
		
			
				|  |  |  |  |     async def set(self, ctx: Context, atr: str, value: str, member: discord.Member = None): | 
		
	
		
			
				|  |  |  |  |         self._logger.debug(__name__, f'Received command user-set {ctx}:{member}') | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if member is None or not isinstance(member, discord.Member): | 
		
	
		
			
				|  |  |  |  |             member = ctx.author | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         server = self._servers.find_server_by_discord_id(ctx.guild.id) | 
		
	
		
			
				|  |  |  |  |         user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if atr == self._atr_dict["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')) | 
		
	
		
			
				|  |  |  |  |                 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')) | 
		
	
		
			
				|  |  |  |  |                 return | 
		
	
		
			
				|  |  |  |  |             else: | 
		
	
		
			
				|  |  |  |  |                 self._users.update_user(user) | 
		
	
		
			
				|  |  |  |  |                 self._db.save_changes() | 
		
	
		
			
				|  |  |  |  |                 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)) | 
		
	
		
			
				|  |  |  |  |             return | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         await self._message_service.send_interaction_msg( | 
		
	
		
			
				|  |  |  |  |             ctx.interaction, | 
		
	
		
			
				|  |  |  |  |             self._t.transform(f'modules.base.user.set.{atr}').format(member.mention, value) | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     @set.autocomplete('atr') | 
		
	
		
			
				|  |  |  |  |     async def set_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: | 
		
	
		
			
				|  |  |  |  |         atr_list = [self._atr_dict["xp"]] | 
		
	
		
			
				|  |  |  |  |         return [app_commands.Choice(name=atr, value=atr) for atr in atr_list] | 
		
	
	
		
			
				
					
					| 
							
							
							
						 |  |  |   |