From 5489cc96b4a801cc79d3b5acfced3d66a0e561a5 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Jan 2022 16:38:15 +0100 Subject: [PATCH] Added info command & Added dynamic embed --- src/gismo/startup.py | 2 + src/gismo_core/__init__.py | 24 ------ .../abc/client_utils_service_abc.py | 3 + src/gismo_core/abc/message_service_abc.py | 6 +- .../embed_description_settings.py | 53 +++++++++++++ src/gismo_core/model/__init__.py | 1 + src/gismo_core/model/embed_description.py | 52 +++++++++++++ .../model/embed_description_field.py | 27 +++++++ .../service/client_utils_service.py | 5 ++ src/gismo_core/service/embed_service.py | 23 ++++++ src/gismo_core/service/message_service.py | 22 ++++-- src/modules/base/base.py | 9 ++- src/modules/base/base_settings.py | 8 ++ .../base/service/help_command_service.py | 2 +- .../base/service/info_command_service.py | 75 +++++++++++++++++++ 15 files changed, 273 insertions(+), 39 deletions(-) create mode 100644 src/gismo_core/configuration/embed_description_settings.py create mode 100644 src/gismo_core/model/__init__.py create mode 100644 src/gismo_core/model/embed_description.py create mode 100644 src/gismo_core/model/embed_description_field.py create mode 100644 src/gismo_core/service/embed_service.py create mode 100644 src/modules/base/service/info_command_service.py diff --git a/src/gismo/startup.py b/src/gismo/startup.py index cefbac1..827d3d9 100644 --- a/src/gismo/startup.py +++ b/src/gismo/startup.py @@ -39,6 +39,7 @@ from gismo_data.service.user_repository_service import UserRepositoryService from modules.base.base import Base from modules.base.service.afk_command_service import AFKCommandService from modules.base.service.help_command_service import HelpCommandService +from modules.base.service.info_command_service import InfoCommandService from modules.base.service.ping_command_service import PingCommandService from modules.base.service.purge_command_service import PurgeCommandService from modules.boot_log.boot_log import BootLog @@ -101,6 +102,7 @@ class Startup(StartupABC): services.add_singleton(CommandABC, PurgeCommandService) services.add_singleton(CommandABC, AFKCommandService) services.add_singleton(CommandABC, HelpCommandService) + services.add_singleton(CommandABC, InfoCommandService) # modules services.add_transient(ModuleABC, Database) diff --git a/src/gismo_core/__init__.py b/src/gismo_core/__init__.py index bdc445a..425ab6c 100644 --- a/src/gismo_core/__init__.py +++ b/src/gismo_core/__init__.py @@ -1,25 +1 @@ -# -*- coding: utf-8 -*- - -""" -gismo sh-edraft Gismo -~~~~~~~~~~~~~~~~~~~ - -sh-edraft Dicord bot Gismo - -:copyright: (c) 2021 - 2022 sh-edraft.de -:license: MIT, see LICENSE for more details. - -""" - -__title__ = 'gismo_core' -__author__ = 'Sven Heidemann' -__license__ = 'MIT' -__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.4.8' - -from collections import namedtuple - # imports - -VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='4', micro='8') diff --git a/src/gismo_core/abc/client_utils_service_abc.py b/src/gismo_core/abc/client_utils_service_abc.py index c4ac152..5abf2ed 100644 --- a/src/gismo_core/abc/client_utils_service_abc.py +++ b/src/gismo_core/abc/client_utils_service_abc.py @@ -11,3 +11,6 @@ class ClientUtilsServiceABC(ABC): @abstractmethod def moved_user(self, guild_id: int): pass + + @abstractmethod + def get_client(self, dc_ic: int, guild_id: int): pass diff --git a/src/gismo_core/abc/message_service_abc.py b/src/gismo_core/abc/message_service_abc.py index 3226cc7..61a0211 100644 --- a/src/gismo_core/abc/message_service_abc.py +++ b/src/gismo_core/abc/message_service_abc.py @@ -18,10 +18,10 @@ class MessageServiceABC(ABC): async def delete_message(self, message: discord.Message): pass @abstractmethod - async def send_channel_message(self, channel: discord.TextChannel, message: str): pass + async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed]): pass @abstractmethod - async def send_dm_message(self, message: str, receiver: Union[discord.User, discord.Member]): pass + async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member]): pass @abstractmethod - async def send_ctx_msg(self, ctx: Context, message: str, file: discord.File = None, is_persistent: bool = False): pass + async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False): pass diff --git a/src/gismo_core/configuration/embed_description_settings.py b/src/gismo_core/configuration/embed_description_settings.py new file mode 100644 index 0000000..fb87c8e --- /dev/null +++ b/src/gismo_core/configuration/embed_description_settings.py @@ -0,0 +1,53 @@ +import traceback +from typing import Optional + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console +from cpl_query.extension import List + +from gismo_core.model.embed_description import EmbedDescription +from gismo_core.model.embed_description_field import EmbedDescriptionField + + +class EmbedDescriptionSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._embed_description: Optional[EmbedDescription] = None + + @property + def embed_description(self) -> EmbedDescription: + return self._embed_description + + def from_dict(self, settings: dict): + try: + title = '' + description = '' + url = '' + color = '' + fields = List(EmbedDescriptionField) + footer = '' + + for key in settings: + if key == 'Title': + title = settings[key] + elif key == 'Description': + description = settings[key] + elif key == 'URL': + url = settings[key] + elif key == 'Color': + color = settings[key] + elif key == 'Fields': + for field in settings[key]: + fields.append(EmbedDescriptionField(field['Name'], field['Value'], bool(field['Inline']))) + elif key == 'Footer': + footer = settings[key] + else: + raise Exception(f'Unexpected key {key}') + + self._embed_description = EmbedDescription(title, description, url, color, fields, footer) + + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/src/gismo_core/model/__init__.py b/src/gismo_core/model/__init__.py new file mode 100644 index 0000000..425ab6c --- /dev/null +++ b/src/gismo_core/model/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/src/gismo_core/model/embed_description.py b/src/gismo_core/model/embed_description.py new file mode 100644 index 0000000..6684ee9 --- /dev/null +++ b/src/gismo_core/model/embed_description.py @@ -0,0 +1,52 @@ +from cpl_query.extension import List + +from gismo_core.model.embed_description_field import EmbedDescriptionField + + +class EmbedDescription: + + def __init__( + self, + title: str = None, + description: str = None, + url: str = None, + color: str = None, + fields: List[EmbedDescriptionField] = None, + footer: str = None + ): + self._title = title + self._description = description + self._url = url + self._color = color + self._fields = fields + self._footer = footer + + @property + def title(self) -> str: + return self._title + + @property + def description(self) -> str: + return self._description + + @property + def url(self) -> str: + return self._url + + @property + def color(self) -> str: + return self._color + + @property + def fields(self) -> list[EmbedDescriptionField]: + return self._fields + + @fields.setter + def fields(self, value: list[EmbedDescriptionField]): + self._fields = value + + @property + def footer(self) -> str: + return self._footer + + diff --git a/src/gismo_core/model/embed_description_field.py b/src/gismo_core/model/embed_description_field.py new file mode 100644 index 0000000..cb47b02 --- /dev/null +++ b/src/gismo_core/model/embed_description_field.py @@ -0,0 +1,27 @@ +class EmbedDescriptionField: + + def __init__( + self, + name: str, + value: str, + inline: bool + ): + self._name = name + self._value = value + self._inline = inline + + @property + def name(self) -> str: + return self._name + + @property + def value(self) -> str: + return self._value + + @value.setter + def value(self, value: str): + self._value = value + + @property + def inline(self) -> bool: + return self._inline diff --git a/src/gismo_core/service/client_utils_service.py b/src/gismo_core/service/client_utils_service.py index a3ed597..7fa8f81 100644 --- a/src/gismo_core/service/client_utils_service.py +++ b/src/gismo_core/service/client_utils_service.py @@ -34,3 +34,8 @@ class ClientUtilsService(ClientUtilsServiceABC): client.moved_users_count += 1 self._clients.update_client(client) self._db.save_changes() + + def get_client(self, dc_ic: int, guild_id: int): + server = self._servers.get_server_by_discord_id(guild_id) + client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.server_id) + return client diff --git a/src/gismo_core/service/embed_service.py b/src/gismo_core/service/embed_service.py new file mode 100644 index 0000000..4585f1f --- /dev/null +++ b/src/gismo_core/service/embed_service.py @@ -0,0 +1,23 @@ +import discord + +from gismo_core.model.embed_description import EmbedDescription + + +class EmbedService: + + @staticmethod + def get_embed(description: EmbedDescription) -> discord.Embed: + embed = discord.Embed( + title=description.title, + url=description.url, + description=description.description, + color=int(description.color, 16) + ) + + for field in description.fields: + embed.add_field(name=field.name, value=field.value, inline=field.inline) + + if description.footer is not None: + embed.set_footer(text=description.footer) + + return embed diff --git a/src/gismo_core/service/message_service.py b/src/gismo_core/service/message_service.py index 32ebd29..32d509c 100644 --- a/src/gismo_core/service/message_service.py +++ b/src/gismo_core/service/message_service.py @@ -46,11 +46,14 @@ class MessageService(MessageServiceABC): self._db.save_changes() self._logger.info(__name__, f'Deleted message {message}') - async def send_channel_message(self, channel: discord.TextChannel, message: str): + async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed]): self._logger.debug(__name__, f'Try to send message\n\t{message}\n\tto: {channel}') msg = None try: - msg = await channel.send(message) + if isinstance(message, discord.Embed): + msg = await channel.send(embed=message) + else: + msg = await channel.send(message) except Exception as e: self._logger.error(__name__, f'Send message to channel {channel.id} failed', e) else: @@ -59,10 +62,13 @@ class MessageService(MessageServiceABC): self._db.save_changes() await self.delete_message(msg) - async def send_dm_message(self, message: str, receiver: Union[discord.User, discord.Member]): + async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member]): self._logger.debug(__name__, f'Try to send message\n\t{message}\n\tto: {receiver}') try: - await receiver.send(message) + if isinstance(message, discord.Embed): + msg = await receiver.send(embed=message) + else: + msg = await receiver.send(message) except Exception as e: self._logger.error(__name__, f'Send message to user {receiver.id} failed', e) else: @@ -70,7 +76,7 @@ class MessageService(MessageServiceABC): self._db.save_changes() self._logger.info(__name__, f'Sent message to user {receiver.id}') - async def send_ctx_msg(self, ctx: Context, message: str, file: discord.File = None, is_persistent: bool = False): + async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False): if ctx is None: self._logger.warn(__name__, 'Message context is empty') self._logger.debug(__name__, f'Message: {message}') @@ -79,10 +85,10 @@ class MessageService(MessageServiceABC): self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {ctx.channel}') msg = None try: - if isinstance(message, discord.File): - msg = await ctx.send(file=message) + if isinstance(message, discord.Embed): + msg = await ctx.send(embed=message, file=file) else: - msg = await ctx.send(message) + msg = await ctx.send(message, file=file) except Exception as e: self._logger.error(__name__, f'Send message to channel {ctx.channel.id} failed', e) else: diff --git a/src/modules/base/base.py b/src/modules/base/base.py index 82db15d..0612208 100644 --- a/src/modules/base/base.py +++ b/src/modules/base/base.py @@ -23,6 +23,7 @@ from gismo_data.model.user_joined_voice_channel import UserJoinedVoiceChannel from modules.base.base_settings import BaseSettings from modules.base.service.afk_command_service import AFKCommandService from modules.base.service.help_command_service import HelpCommandService +from modules.base.service.info_command_service import InfoCommandService from modules.base.service.ping_command_service import PingCommandService from modules.base.service.purge_command_service import PurgeCommandService from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -53,7 +54,8 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS ping_command: PingCommandService, purge_command: PurgeCommandService, afk_command: AFKCommandService, - help_command: HelpCommandService + help_command: HelpCommandService, + info_command: InfoCommandService, ): self._config = config self._logger = logger @@ -83,6 +85,7 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS self._bot.add_cog(purge_command) self._bot.add_cog(afk_command) self._bot.add_cog(help_command) + self._bot.add_cog(info_command) self._logger.info(__name__, f'Module {type(self)} loaded') @@ -121,10 +124,10 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS settings: BaseSettings = self._get_config(member.guild.id) await self._messenger.send_dm_message(settings.welcome_message.format(member.guild.name), member) - for admin in self._permission_service.get_admins(): + for admin in self._permission_service.get_admins(member.guild.id): await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), admin) - for moderator in self._permission_service.get_moderators(): + for moderator in self._permission_service.get_moderators(member.guild.id): await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), moderator) try: diff --git a/src/modules/base/base_settings.py b/src/modules/base/base_settings.py index abc881f..859f458 100644 --- a/src/modules/base/base_settings.py +++ b/src/modules/base/base_settings.py @@ -3,6 +3,8 @@ import traceback from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC from cpl_core.console import Console +from gismo_core.configuration.embed_description_settings import EmbedDescriptionSettings + class BaseSettings(ConfigurationModelABC): @@ -22,6 +24,7 @@ class BaseSettings(ConfigurationModelABC): self._afk_command_channel_missing_message: str = '' self._afk_command_move_message: str = '' self._help_command_reference_url: str = '' + self._info_command_message: EmbedDescriptionSettings = EmbedDescriptionSettings() @property def welcome_message(self) -> str: @@ -75,6 +78,10 @@ class BaseSettings(ConfigurationModelABC): def help_command_reference_url(self) -> str: return self._help_command_reference_url + @property + def info_command_message(self) -> EmbedDescriptionSettings: + return self._info_command_message + def from_dict(self, settings: dict): try: self._welcome_message = settings['WelcomeMessage'] @@ -91,6 +98,7 @@ class BaseSettings(ConfigurationModelABC): self._afk_command_channel_missing_message = settings['AFKCommandChannelMissingMessage'] self._afk_command_move_message = settings['AFKCommandMoveMessage'] self._help_command_reference_url = settings['HelpCommandReferenceUrl'] + self._info_command_message.from_dict(settings['InfoCommandMessage']) except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/src/modules/base/service/help_command_service.py b/src/modules/base/service/help_command_service.py index 6a97d6c..bcf6b1c 100644 --- a/src/modules/base/service/help_command_service.py +++ b/src/modules/base/service/help_command_service.py @@ -32,7 +32,7 @@ class HelpCommandService(CommandABC): @commands.command() async def help(self, ctx: Context, persistent_flag: str = None): - self._logger.debug(__name__, f'Received command ping {ctx}:{persistent_flag}') + self._logger.debug(__name__, f'Received command help {ctx}:{persistent_flag}') self._client_utils.received_command(ctx.guild.id) settings: BaseSettings = self._config.get_configuration(f'Base_{ctx.guild.id}') is_persistent = persistent_flag == '--stay' diff --git a/src/modules/base/service/info_command_service.py b/src/modules/base/service/info_command_service.py new file mode 100644 index 0000000..5803f34 --- /dev/null +++ b/src/modules/base/service/info_command_service.py @@ -0,0 +1,75 @@ +from datetime import datetime + +from cpl_core.configuration import ConfigurationABC +from cpl_core.logging import LoggerABC +from cpl_query.extension import List +from discord.ext import commands +from discord.ext.commands import Context + +import gismo +from gismo_core.abc.bot_service_abc import BotServiceABC +from gismo_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from gismo_core.abc.command_abc import CommandABC +from gismo_core.abc.message_service_abc import MessageServiceABC +from gismo_core.abc.module_abc import ModuleABC +from gismo_core.model.embed_description_field import EmbedDescriptionField +from gismo_core.service.embed_service import EmbedService +from modules.base.base_settings import BaseSettings + + +class InfoCommandService(CommandABC): + + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + message_service: MessageServiceABC, + bot: BotServiceABC, + client_utils: ClientUtilsServiceABC + ): + CommandABC.__init__(self) + + self._config = config + self._logger = logger + self._message_service = message_service + self._bot = bot + self._client_utils = client_utils + + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + + @commands.command() + async def info(self, ctx: Context): + self._logger.debug(__name__, f'Received command info {ctx}') + self._client_utils.received_command(ctx.guild.id) + settings: BaseSettings = self._config.get_configuration(f'Base_{ctx.guild.id}') + embed_description = settings.info_command_message.embed_description + client = self._client_utils.get_client(self._bot.user.id, ctx.guild.id) + + for i in range(len(embed_description.fields)): + field = embed_description.fields[i] + if field.value == '$version': + field.value = gismo.__version__ + elif field.value == '$ontime': + start_time = self._config.get_configuration('Bot_StartTime') + ontime = round((datetime.now() - datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S.%f')).total_seconds()/3600, 2) + field.value = f'{ontime}h' + elif field.value == '$sent_message_count': + field.value = client.sent_message_count + elif field.value == '$received_message_count': + field.value = client.received_message_count + elif field.value == '$deleted_message_count': + field.value = client.deleted_message_count + elif field.value == '$received_command_count': + field.value = client.received_command_count + elif field.value == '$moved_users_count': + field.value = client.moved_users_count + elif field.value == '$modules': + field.value = '' + for module in ModuleABC.__subclasses__(): + field.value += f'{module.__name__}\n' + + await self._message_service.send_ctx_msg( + ctx, + EmbedService.get_embed(embed_description) + ) + self._logger.trace(__name__, f'Finished ping command')