forked from sh-edraft.de/sh_discord_bot
		
	Added logic to handle custom statistics #46
This commit is contained in:
		| @@ -227,6 +227,10 @@ | ||||
|         "statistic": "Statistik", | ||||
|         "description": "Beschreibung", | ||||
|         "failed": "Statistik kann nicht gezeigt werden :(" | ||||
|       }, | ||||
|       "add": { | ||||
|         "failed": "Statistik kann nicht hinzugefügt werden :(", | ||||
|         "success": "Statistik wurde hinzugefügt :D" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   | ||||
| @@ -3,6 +3,7 @@ from typing import Union | ||||
|  | ||||
| import discord | ||||
| from cpl_query.extension import List | ||||
| from discord import Interaction | ||||
| from discord.ext.commands import Context | ||||
|  | ||||
|  | ||||
| @@ -10,18 +11,21 @@ class MessageServiceABC(ABC): | ||||
|  | ||||
|     @abstractmethod | ||||
|     def __init__(self): pass | ||||
|      | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass | ||||
|      | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def delete_message(self, message: discord.Message, without_tracking=False): pass | ||||
|      | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass | ||||
|      | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass | ||||
|      | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from cpl_core.configuration.configuration_abc import ConfigurationABC | ||||
| from cpl_core.database.context.database_context_abc import DatabaseContextABC | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| from cpl_query.extension import List | ||||
| from discord import Interaction | ||||
| from discord.ext.commands import Context | ||||
|  | ||||
| from bot_core.abc.message_service_abc import MessageServiceABC | ||||
| @@ -117,3 +118,32 @@ class MessageService(MessageServiceABC): | ||||
|  | ||||
|             if ctx.guild is not None: | ||||
|                 await self.delete_message(msg, without_tracking) | ||||
|  | ||||
|     async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False): | ||||
|         if interaction is None: | ||||
|             self._logger.warn(__name__, 'Message context is empty') | ||||
|             self._logger.debug(__name__, f'Message: {message}') | ||||
|             return | ||||
|  | ||||
|         self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') | ||||
|         msg = None | ||||
|         try: | ||||
|             if isinstance(message, discord.Embed): | ||||
|                 msg = await interaction.response.send_message(embed=message) | ||||
|             else: | ||||
|                 msg = await interaction.response.send_message(message) | ||||
|         except Exception as e: | ||||
|             self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) | ||||
|         else: | ||||
|             self._logger.info(__name__, f'Sent message to channel {interaction.channel.id}') | ||||
|             if not without_tracking and interaction.guild is not None: | ||||
|                 self._clients.append_sent_message_count(self._bot.user.id, interaction.guild.id, 1) | ||||
|                 self._db.save_changes() | ||||
|  | ||||
|             if wait_before_delete is not None: | ||||
|                 await asyncio.sleep(wait_before_delete) | ||||
|  | ||||
|             if is_persistent: | ||||
|                 return | ||||
|  | ||||
|             await self.delete_message(msg, without_tracking) | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import textwrap | ||||
| from typing import List as TList | ||||
|  | ||||
| import discord | ||||
| @@ -15,8 +16,25 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC | ||||
| from modules.permission.abc.permission_service_abc import PermissionServiceABC | ||||
| from modules.stats.model.statistic import Statistic | ||||
| from modules.stats.service.statistic_service import StatisticService | ||||
| from modules.stats.test.user_xp_asc import user_xp_asc | ||||
| from modules.stats.test.user_xp_desc import user_xp_desc | ||||
| from modules.stats.ui.add_statistic_form import AddStatisticForm | ||||
|  | ||||
| stats = List(Statistic, [ | ||||
|     Statistic( | ||||
|         'Benutzer XP Aufsteigend', | ||||
|         'Zeigt XP von jedem Benutzer, aufsteigend nach XP', | ||||
|         textwrap.dedent("""\ | ||||
|         result.header.append('Name') | ||||
|         result.header.append('XP') | ||||
|          | ||||
|         for user in users.order_by(lambda u: u.xp): | ||||
|             row = List(str) | ||||
|             member = guild.get_member(user.discord_id) | ||||
|             row.append(member.name) | ||||
|             row.append(str(user.xp)) | ||||
|             result.values.append(row) | ||||
|         """)), | ||||
|     Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', '') | ||||
| ]) | ||||
|  | ||||
|  | ||||
| class StatsGroup(DiscordCommandABC): | ||||
| @@ -41,17 +59,12 @@ class StatsGroup(DiscordCommandABC): | ||||
|         self._statistic = statistic | ||||
|         self._servers = servers | ||||
|  | ||||
|         self._stats = List(Statistic, [ | ||||
|             Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), | ||||
|             Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) | ||||
|         ]) | ||||
|  | ||||
|     @commands.hybrid_group() | ||||
|     @commands.guild_only() | ||||
|     async def stats(self, ctx: Context): | ||||
|         pass | ||||
|  | ||||
|     @stats.command(alias='rules') | ||||
|     @stats.command() | ||||
|     @commands.guild_only() | ||||
|     async def list(self, ctx: Context, wait: int = None): | ||||
|         self._logger.debug(__name__, f'Received command stats list {ctx}') | ||||
| @@ -92,8 +105,8 @@ class StatsGroup(DiscordCommandABC): | ||||
|  | ||||
|         try: | ||||
|             server = self._servers.get_server_by_discord_id(ctx.guild.id) | ||||
|             statistic = self._stats.where(lambda s: s.name == name).single() | ||||
|             result = await self._statistic.execute(statistic.func, server) | ||||
|             statistic = stats.where(lambda s: s.name == name).single() | ||||
|             result = await self._statistic.execute(statistic.code, server) | ||||
|  | ||||
|             embed = discord.Embed( | ||||
|                 title=statistic.name, | ||||
| @@ -117,4 +130,21 @@ class StatsGroup(DiscordCommandABC): | ||||
|  | ||||
|     @view.autocomplete('name') | ||||
|     async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: | ||||
|         return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] | ||||
|         return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] | ||||
|  | ||||
|     @stats.command() | ||||
|     @commands.guild_only() | ||||
|     async def add(self, ctx: Context, name: str): | ||||
|         self._logger.debug(__name__, f'Received command stats list {ctx}') | ||||
|         if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): | ||||
|             return | ||||
|  | ||||
|         if not self._permissions.is_member_technician(ctx.author): | ||||
|             await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) | ||||
|             self._logger.trace(__name__, f'Finished command stats list') | ||||
|             return | ||||
|  | ||||
|         form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) | ||||
|         self._logger.trace(__name__, f'Finished stats command') | ||||
|         self._logger.trace(__name__, f'Started stats command form') | ||||
|         await ctx.interaction.response.send_modal(form) | ||||
|   | ||||
| @@ -3,10 +3,10 @@ from typing import Callable | ||||
|  | ||||
| class Statistic: | ||||
|  | ||||
|     def __init__(self, name: str, description: str, func: Callable): | ||||
|     def __init__(self, name: str, description: str, code: str): | ||||
|         self._name = name | ||||
|         self._description = description | ||||
|         self._func = func | ||||
|         self._code = code | ||||
|          | ||||
|     @property | ||||
|     def name(self) -> str: | ||||
| @@ -17,5 +17,5 @@ class Statistic: | ||||
|         return self._description | ||||
|      | ||||
|     @property | ||||
|     def func(self) -> Callable: | ||||
|         return self._func | ||||
|     def code(self) -> str: | ||||
|         return self._code | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| from abc import abstractmethod | ||||
| from typing import Callable | ||||
|  | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| from cpl_query.extension import List | ||||
| from discord import Guild | ||||
|  | ||||
| from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC | ||||
| from bot_data.abc.client_repository_abc import ClientRepositoryABC | ||||
| @@ -11,7 +12,14 @@ 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 bot_data.model.auto_role import AutoRole | ||||
| from bot_data.model.client import Client | ||||
| from bot_data.model.known_user import KnownUser | ||||
| from bot_data.model.level import Level | ||||
| from bot_data.model.server import Server | ||||
| from bot_data.model.user import User | ||||
| from bot_data.model.user_joined_server import UserJoinedServer | ||||
| from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel | ||||
| from modules.stats.model.statistic_result import StatisticResult | ||||
|  | ||||
|  | ||||
| @@ -39,10 +47,11 @@ class StatisticService: | ||||
|         self._users = users | ||||
|         self._bot = bot | ||||
|  | ||||
|     async def execute(self, _f: Callable, server: Server) -> StatisticResult: | ||||
|     async def execute(self, code: str, server: Server) -> StatisticResult: | ||||
|         guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() | ||||
|  | ||||
|         return await _f( | ||||
|         return await self.get_data( | ||||
|             code, | ||||
|             self._auto_roles | ||||
|                 .get_auto_roles() | ||||
|                 .where(lambda x: x.server.server_id == server.server_id), | ||||
| @@ -67,3 +76,21 @@ class StatisticService: | ||||
|                 .where(lambda x: x.server.server_id == server.server_id), | ||||
|             guild | ||||
|         ) | ||||
|  | ||||
|     @staticmethod | ||||
|     async def get_data( | ||||
|             code: str, | ||||
|             auto_roles: List[AutoRole], | ||||
|             clients: List[Client], | ||||
|             known_users: List[KnownUser], | ||||
|             levels: List[Level], | ||||
|             servers: List[Server], | ||||
|             user_joined_servers: List[UserJoinedServer], | ||||
|             user_joined_voice_channel: List[UserJoinedVoiceChannel], | ||||
|             users: List[User], | ||||
|             guild: Guild | ||||
|     ) -> StatisticResult: | ||||
|         result = StatisticResult() | ||||
|         exec(code) | ||||
|  | ||||
|         return result | ||||
|   | ||||
							
								
								
									
										1
									
								
								kdb-bot/src/modules/stats/ui/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/modules/stats/ui/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # imports | ||||
							
								
								
									
										35
									
								
								kdb-bot/src/modules/stats/ui/add_statistic_form.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								kdb-bot/src/modules/stats/ui/add_statistic_form.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import discord | ||||
| from cpl_query.extension import List | ||||
| from cpl_translation import TranslatePipe | ||||
| from discord import ui, TextStyle | ||||
|  | ||||
| from bot_core.abc.message_service_abc import MessageServiceABC | ||||
| from bot_core.logging.command_logger import CommandLogger | ||||
| from modules.stats.model.statistic import Statistic | ||||
|  | ||||
|  | ||||
| class AddStatisticForm(ui.Modal): | ||||
|  | ||||
|     description = ui.TextInput(label='Beschreibung', required=True) | ||||
|     code = ui.TextInput(label='Code', required=True, style=TextStyle.long) | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             stats: List[Statistic], | ||||
|             name: str, | ||||
|             message_service: MessageServiceABC, | ||||
|             logger: CommandLogger, | ||||
|             t: TranslatePipe, | ||||
|     ): | ||||
|         ui.Modal.__init__(self, title=name) | ||||
|  | ||||
|         self._stats = stats | ||||
|         self._name = name | ||||
|         self._message_service = message_service | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|  | ||||
|     async def on_submit(self, interaction: discord.Interaction): | ||||
|         self._stats.append(Statistic(self._name, self.description.value, self.code.value)) | ||||
|         await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) | ||||
|         self._logger.trace(__name__, f'Finished stats command form') | ||||
		Reference in New Issue
	
	Block a user