diff --git a/.gitignore b/.gitignore index 3e0d7a3f..b447aabb 100644 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,7 @@ cython_debug/ # custom vs code PythonImportHelper-v2-Completion.json +deploy/ # idea .idea/ \ No newline at end of file diff --git a/src/bot/config/appsettings.development.json b/src/bot/config/appsettings.development.json index d61a00bc..53ed5d70 100644 --- a/src/bot/config/appsettings.development.json +++ b/src/bot/config/appsettings.development.json @@ -28,7 +28,8 @@ "Technicians": [ 240160344557879316, 236592458664902657 - ] + ], + "DeployFilesPath": "../../deploy" }, "Base": { "910199451145076828": { diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/src/bot/config/appsettings.edrafts-lapi.json index 71cee08b..8c26c3ea 100644 --- a/src/bot/config/appsettings.edrafts-lapi.json +++ b/src/bot/config/appsettings.edrafts-lapi.json @@ -25,7 +25,8 @@ }, "Technicians": [ 240160344557879316 - ] + ], + "DeployFilesPath": "../../deploy" }, "Base": { "910199451145076828": { diff --git a/src/bot/config/appsettings.production.json b/src/bot/config/appsettings.production.json index 9beda2f3..ac7d4879 100644 --- a/src/bot/config/appsettings.production.json +++ b/src/bot/config/appsettings.production.json @@ -27,7 +27,8 @@ "Technicians": [ 240160344557879316, 236592458664902657 - ] + ], + "DeployFilesPath": "../../deploy" }, "Base": { "650366049023295514": { diff --git a/src/bot/config/appsettings.staging.json b/src/bot/config/appsettings.staging.json index 75d992cb..eea3c0e1 100644 --- a/src/bot/config/appsettings.staging.json +++ b/src/bot/config/appsettings.staging.json @@ -24,7 +24,8 @@ "Technicians": [ 240160344557879316, 236592458664902657 - ] + ], + "DeployFilesPath": "../../deploy" }, "Base": { "910199451145076828": { diff --git a/src/bot/startup_discord_extension.py b/src/bot/startup_discord_extension.py index 4d20e684..7a389e4e 100644 --- a/src/bot/startup_discord_extension.py +++ b/src/bot/startup_discord_extension.py @@ -5,6 +5,7 @@ from cpl_core.environment import ApplicationEnvironmentABC from cpl_discord import get_discord_collection from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum +from modules.admin.command.deploy_command import DeployCommand from modules.admin.command.restart_command import RestartCommand from modules.admin.command.shutdown_command import ShutdownCommand from modules.base.command.afk_command import AFKCommand @@ -39,6 +40,7 @@ class StartupDiscordExtension(StartupExtensionABC): # admin dc.add_command(RestartCommand) dc.add_command(ShutdownCommand) + dc.add_command(DeployCommand) # moderator dc.add_command(PurgeCommand) # simple diff --git a/src/bot_core/configuration/bot_settings.py b/src/bot_core/configuration/bot_settings.py index 96bd61c8..4c0656b2 100644 --- a/src/bot_core/configuration/bot_settings.py +++ b/src/bot_core/configuration/bot_settings.py @@ -14,6 +14,7 @@ class BotSettings(ConfigurationModelABC): self._servers: List[ServerSettings] = List() self._technicians: list[int] = [] + self._deploy_file_path = './' @property def servers(self) -> List[ServerSettings]: @@ -23,10 +24,16 @@ class BotSettings(ConfigurationModelABC): def technicians(self) -> list[int]: return self._technicians + @property + def deploy_file_path(self) -> str: + return self._deploy_file_path + def from_dict(self, settings: dict): try: self._technicians = settings["Technicians"] + self._deploy_file_path = settings["DeployFilesPath"] settings.pop("Technicians") + settings.pop("DeployFilesPath") servers = List(ServerSettings) for s in settings: st = ServerSettings() diff --git a/src/bot_core/service/message_service.py b/src/bot_core/service/message_service.py index c0c1ca59..14ce60ca 100644 --- a/src/bot_core/service/message_service.py +++ b/src/bot_core/service/message_service.py @@ -96,7 +96,7 @@ class MessageService(MessageServiceABC): self._logger.error(__name__, f'Send message to channel {ctx.channel.id} failed', e) else: self._logger.info(__name__, f'Sent message to channel {ctx.channel.id}') - if not without_tracking: + if not without_tracking and ctx.guild is not None: self._clients.append_sent_message_count(self._bot.user.id, ctx.guild.id, 1) self._db.save_changes() diff --git a/src/modules/admin/command/deploy_command.py b/src/modules/admin/command/deploy_command.py new file mode 100644 index 00000000..0d2078dc --- /dev/null +++ b/src/modules/admin/command/deploy_command.py @@ -0,0 +1,86 @@ +import os +import shutil +import zipfile +from io import BytesIO + +import requests +from cpl_core.configuration import ConfigurationABC +from cpl_core.logging import LoggerABC +from cpl_discord.command import DiscordCommandABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.bot_settings import BotSettings +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class DeployCommand(DiscordCommandABC): + + def __init__( + self, + logger: LoggerABC, + config: ConfigurationABC, + message_service: MessageServiceABC, + bot: DiscordBotServiceABC, + client_utils: ClientUtilsServiceABC, + translate: TranslatePipe, + bot_settings: BotSettings + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._config = config + self._message_service = message_service + self._bot = bot + self._client_utils = client_utils + self._t = translate + self._bot_settings = bot_settings + + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + + @commands.command() + @commands.dm_only() + async def deploy(self, ctx: Context, old_version: str): + self._logger.debug(__name__, f'Received command deploy {ctx}') + + if ctx.author.id not in self._bot_settings.technicians: + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished deploy command') + return + + if len(ctx.message.attachments) > 1: + raise IndexError(self._t.transform('common.errors.too_many_arguments')) + + if len(ctx.message.attachments) < 1: + raise IndexError(self._t.transform('common.errors.missing_required_argument')) + + try: + attachment_url = ctx.message.attachments[0].url + file_request = requests.get(attachment_url) + except Exception as e: + self._logger.error(__name__, f'An error occurred downloading the zip file', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.command_error')) + return + + try: + self._logger.debug(__name__, f'Deploying files to {self._bot_settings.deploy_file_path}/latest') + deploy_path = f'{self._bot_settings.deploy_file_path}/latest' + deploy_old_path = f'{self._bot_settings.deploy_file_path}/{old_version}' + + if os.path.exists(deploy_path): + self._logger.debug(__name__, f'Moving files of {self._bot_settings.deploy_file_path}/latest to {self._bot_settings.deploy_file_path}/{old_version}') + shutil.move(deploy_path, deploy_old_path) + os.makedirs(deploy_path) + + file = zipfile.ZipFile(BytesIO(file_request.content)) + file.extractall(deploy_path) + except Exception as e: + self._logger.error(__name__, f'An error occurred extracting the zip file', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.command_error')) + return + + self._logger.trace(__name__, f'Finished deploy command') diff --git a/src/modules/base/events/base_on_command_error_event.py b/src/modules/base/events/base_on_command_error_event.py index 1ae7a9c2..cbe9b930 100644 --- a/src/modules/base/events/base_on_command_error_event.py +++ b/src/modules/base/events/base_on_command_error_event.py @@ -1,4 +1,5 @@ import datetime +import traceback import uuid from cpl_core.time import TimeFormatSettings