diff --git a/kdb-bot/.cpl/schematic_db_table.py b/kdb-bot/.cpl/schematic_db_table.py new file mode 100644 index 00000000..4c01c549 --- /dev/null +++ b/kdb-bot/.cpl/schematic_db_table.py @@ -0,0 +1,100 @@ +from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC + + +class DBTable(GenerateSchematicABC): + def __init__(self, *args: str): + GenerateSchematicABC.__init__(self, *args) + self._name = self._name.replace("_db_table", "") + self._class_name = self._class_name.split("Db_table")[0] + + def get_code(self) -> str: + import textwrap + + code = textwrap.dedent( + """\ + from datetime import datetime + + from cpl_core.database import TableABC + + + class $ClassName(TableABC): + def __init__( + self, + value: str, + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._value = value + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def value(self) -> str: + return self._value + + @value.setter + def value(self, value: str): + self._value = value + + @staticmethod + def get_select_all_string() -> str: + return str( + f\""" + SELECT * FROM `$TableName`; + \""" + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f\""" + SELECT * FROM `$TableName` + WHERE `Id` = {id}; + \""" + ) + + @property + def insert_string(self) -> str: + return str( + f\""" + INSERT INTO `$TableName` ( + `Value` + ) VALUES ( + {self._value} + ); + \""" + ) + + @property + def udpate_string(self) -> str: + return str( + f\""" + UPDATE `$TableName` + SET `Value` = {self._value} + WHERE `Id` = {self._id}; + \""" + ) + + @property + def delete_string(self) -> str: + return str( + f\""" + DELETE FROM `$TableName` + WHERE `Id` = {self._id}; + \""" + ) + """ + ) + return self.build_code_str( + code, + ClassName=self._class_name, + TableName=self._class_name, + ) + + @classmethod + def register(cls): + GenerateSchematicABC.register(cls, "db-table", []) diff --git a/kdb-bot/.cpl/schematic_migration.py b/kdb-bot/.cpl/schematic_migration.py new file mode 100644 index 00000000..16c0fe5f --- /dev/null +++ b/kdb-bot/.cpl/schematic_migration.py @@ -0,0 +1,55 @@ +from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC + + +class Migration(GenerateSchematicABC): + def __init__(self, *args: str): + GenerateSchematicABC.__init__(self, *args) + + def get_code(self) -> str: + import textwrap + + code = textwrap.dedent( + """\ + from bot_core.logging.database_logger import DatabaseLogger + from bot_data.abc.migration_abc import MigrationABC + from bot_data.db_context import DBContext + + + class $ClassName(MigrationABC): + name = "1.0_$ClassName" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + self._cursor.execute( + str( + f\""" + CREATE TABLE IF NOT EXISTS `$TableName` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`Id`) + ); + \""" + ) + ) + + def downgrade(self): + self._cursor.execute("DROP TABLE `$TableName`;") + """ + ) + return self.build_code_str( + code, + ClassName=self._class_name, + TableName=self._class_name.split("Migration")[0], + ) + + @classmethod + def register(cls): + GenerateSchematicABC.register(cls, "migration", []) diff --git a/kdb-bot/.cpl/schematic_query.py b/kdb-bot/.cpl/schematic_query.py index e34a3b6e..767cd77d 100644 --- a/kdb-bot/.cpl/schematic_query.py +++ b/kdb-bot/.cpl/schematic_query.py @@ -1,7 +1,7 @@ from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC -class query(GenerateSchematicABC): +class Query(GenerateSchematicABC): def __init__(self, *args: str): GenerateSchematicABC.__init__(self, *args) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 90d77e00..229bc577 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -7,9 +7,11 @@ "bot-core": "src/bot_core/bot-core.json", "bot-data": "src/bot_data/bot-data.json", "bot-graphql": "src/bot_graphql/bot-graphql.json", + "achievements": "src/modules/achievements/achievements.json", "auto-role": "src/modules/auto_role/auto-role.json", "base": "src/modules/base/base.json", "boot-log": "src/modules/boot_log/boot-log.json", + "config": "src/modules/config/config.json", "database": "src/modules/database/database.json", "level": "src/modules/level/level.json", "permission": "src/modules/permission/permission.json", @@ -21,25 +23,18 @@ }, "Scripts": { "format": "black ./", - "sv": "cpl set-version $ARGS", "set-version": "cpl run set-version $ARGS --dev; echo '';", - "gv": "cpl get-version", "get-version": "export VERSION=$(cpl run get-version --dev); echo $VERSION;", - "pre-build": "cpl set-version $ARGS; black ./;", "post-build": "cpl run post-build --dev; black ./;", - "pre-prod": "cpl build", "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", - "pre-stage": "cpl build", "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", - "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "docker-build": "cpl build $ARGS; docker build -t kdb-bot/kdb-bot:$(cpl gv) .;", "dc-up": "docker-compose up -d", "dc-down": "docker-compose down", diff --git a/kdb-bot/docker b/kdb-bot/docker index 62475d65..31078179 160000 --- a/kdb-bot/docker +++ b/kdb-bot/docker @@ -1 +1 @@ -Subproject commit 62475d65465f289604c7a32d23b5c29bb8cd38a4 +Subproject commit 3107817939dffc1da94a11ef8658a184a66dbcb4 diff --git a/kdb-bot/src/bot/__init__.py b/kdb-bot/src/bot/__init__.py index fc0aa0ba..662b8261 100644 --- a/kdb-bot/src/bot/__init__.py +++ b/kdb-bot/src/bot/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot/application.py b/kdb-bot/src/bot/application.py index 4198de6b..13d46815 100644 --- a/kdb-bot/src/bot/application.py +++ b/kdb-bot/src/bot/application.py @@ -10,6 +10,7 @@ from cpl_translation import TranslatePipe, TranslationServiceABC, TranslationSet from bot_api.api_thread import ApiThread from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.service.data_integrity_service import DataIntegrityService class Application(DiscordBotApplicationABC): @@ -21,6 +22,7 @@ class Application(DiscordBotApplicationABC): # cpl-core self._logger: LoggerABC = services.get_service(LoggerABC) + self._data_integrity: DataIntegrityService = services.get_service(DataIntegrityService) # cpl-discord self._bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) self._bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings) @@ -69,6 +71,7 @@ class Application(DiscordBotApplicationABC): self._api.stop() await self._bot.close() + await self._data_integrity.check_data_integrity(is_for_shutdown=True) self._logger.info(__name__, f"Stopped {DiscordBotService.__name__}") except Exception as e: self._logger.error(__name__, "stop failed", e) @@ -76,4 +79,4 @@ class Application(DiscordBotApplicationABC): Console.write_line() def is_restart(self): - return True if self._configuration.get_configuration("IS_RESTART") == "true" else False # + return True if self._configuration.get_configuration("IS_RESTART") == "true" else False diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 507da098..7dd1f434 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -3,8 +3,8 @@ "Name": "bot", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", @@ -16,24 +16,26 @@ "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [ - "cpl-core==2022.12.1.post3", - "cpl-translation==2022.12.1", - "cpl-query==2022.12.2.post2", - "cpl-discord==2022.12.2.post1", - "Flask==2.2.2", + "cpl-core==2023.4.0.post5", + "cpl-translation==2023.4.0.post1", + "cpl-query==2023.4.0.post1", + "cpl-discord==2023.4.0.post3", + "Flask==2.3.2", "Flask-Classful==0.14.2", - "Flask-Cors==3.0.10", - "PyJWT==2.6.0", + "Flask-Cors==4.0.0", + "PyJWT==2.8.0", "waitress==2.1.2", - "Flask-SocketIO==5.3.2", + "Flask-SocketIO==5.3.4", "eventlet==0.33.3", "requests-oauthlib==1.3.1", "icmplib==3.0.3", - "ariadne==0.17.1" + "ariadne==0.20.1", + "cryptography==41.0.2", + "discord>=2.3.2" ], "DevDependencies": [ - "cpl-cli==2022.12.1.post3", - "pygount==1.5.1" + "cpl-cli==2023.4.0.post3", + "pygount==1.6.1" ], "PythonVersion": ">=3.10.4", "PythonPath": {}, @@ -58,9 +60,11 @@ "../bot_core/bot-core.json", "../bot_data/bot-data.json", "../bot_graphql/bot-graphql.json", + "../modules/achievements/achievements.json", "../modules/auto_role/auto-role.json", "../modules/base/base.json", "../modules/boot_log/boot-log.json", + "../modules/config/config.json", "../modules/database/database.json", "../modules/level/level.json", "../modules/permission/permission.json", diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 0c946375..23eafb2e 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 0c9463753731ab1f5d0f916d21ac7ea304742995 +Subproject commit 23eafb2e211241acbbc52833d139c67f1ecc69f5 diff --git a/kdb-bot/src/bot/extension/__init__.py b/kdb-bot/src/bot/extension/__init__.py index fd479614..163a3f8c 100644 --- a/kdb-bot/src/bot/extension/__init__.py +++ b/kdb-bot/src/bot/extension/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot.extension" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot/extension/init_bot_extension.py b/kdb-bot/src/bot/extension/init_bot_extension.py index a4e0732a..04a4fcf0 100644 --- a/kdb-bot/src/bot/extension/init_bot_extension.py +++ b/kdb-bot/src/bot/extension/init_bot_extension.py @@ -3,7 +3,7 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceProviderABC from cpl_discord.service import DiscordBotServiceABC -from bot_core.configuration.bot_settings import BotSettings +from bot_data.model.technician_config import TechnicianConfig class InitBotExtension(ApplicationExtensionABC): @@ -11,6 +11,6 @@ class InitBotExtension(ApplicationExtensionABC): ApplicationExtensionABC.__init__(self) async def run(self, config: ConfigurationABC, services: ServiceProviderABC): - settings = config.get_configuration(BotSettings) + settings = config.get_configuration(TechnicianConfig) bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC, max_messages=settings.cache_max_messages) diff --git a/kdb-bot/src/bot/main.py b/kdb-bot/src/bot/main.py index 1b240eca..c004dd7d 100644 --- a/kdb-bot/src/bot/main.py +++ b/kdb-bot/src/bot/main.py @@ -15,6 +15,7 @@ from bot.startup_settings_extension import StartupSettingsExtension from bot_api.app_api_extension import AppApiExtension from bot_core.core_extension.core_extension import CoreExtension from modules.boot_log.boot_log_extension import BootLogExtension +from modules.config.config_extension import ConfigExtension from modules.database.database_extension import DatabaseExtension @@ -30,9 +31,10 @@ class Program: .use_extension(StartupDiscordExtension) .use_extension(StartupModuleExtension) .use_extension(StartupMigrationExtension) + .use_extension(DatabaseExtension) + .use_extension(ConfigExtension) .use_extension(InitBotExtension) .use_extension(BootLogExtension) - .use_extension(DatabaseExtension) .use_extension(AppApiExtension) .use_extension(CoreExtension) .use_startup(Startup) diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 700f0c73..3e5fea34 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -5,9 +5,11 @@ from bot_core.core_extension.core_extension_module import CoreExtensionModule from bot_core.core_module import CoreModule from bot_data.data_module import DataModule from bot_graphql.graphql_module import GraphQLModule +from modules.achievements.achievements_module import AchievementsModule from modules.auto_role.auto_role_module import AutoRoleModule from modules.base.base_module import BaseModule from modules.boot_log.boot_log_module import BootLogModule +from modules.config.config_module import ConfigModule from modules.database.database_module import DatabaseModule from modules.level.level_module import LevelModule from modules.permission.permission_module import PermissionModule @@ -23,14 +25,16 @@ class ModuleList: [ CoreModule, # has to be first! DataModule, + ConfigModule, # has to be before db check + DatabaseModule, GraphQLModule, PermissionModule, - DatabaseModule, AutoRoleModule, BaseModule, LevelModule, ApiModule, TechnicianModule, + AchievementsModule, # has to be last! BootLogModule, CoreExtensionModule, diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index 5eba461b..89c5ae7d 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -4,10 +4,13 @@ from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC from bot_data.abc.migration_abc import MigrationABC +from bot_data.migration.achievements_migration import AchievementsMigration from bot_data.migration.api_key_migration import ApiKeyMigration from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration from bot_data.migration.auto_role_migration import AutoRoleMigration +from bot_data.migration.config_feature_flags_migration import ConfigFeatureFlagsMigration +from bot_data.migration.config_migration import ConfigMigration from bot_data.migration.db_history_migration import DBHistoryMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.level_migration import LevelMigration @@ -42,3 +45,6 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, RemoveStatsMigration) # 19.02.2023 #190 - 1.0.0 services.add_transient(MigrationABC, UserWarningMigration) # 21.02.2023 #35 - 1.0.0 services.add_transient(MigrationABC, DBHistoryMigration) # 06.03.2023 #246 - 1.0.0 + services.add_transient(MigrationABC, AchievementsMigration) # 14.06.2023 #268 - 1.1.0 + services.add_transient(MigrationABC, ConfigMigration) # 19.07.2023 #127 - 1.1.0 + services.add_transient(MigrationABC, ConfigFeatureFlagsMigration) # 15.08.2023 #334 - 1.1.0 diff --git a/kdb-bot/src/bot/startup_settings_extension.py b/kdb-bot/src/bot/startup_settings_extension.py index 008ebd10..caf34f19 100644 --- a/kdb-bot/src/bot/startup_settings_extension.py +++ b/kdb-bot/src/bot/startup_settings_extension.py @@ -1,6 +1,6 @@ import os from datetime import datetime -from typing import Callable, Type, Optional +from typing import Optional, Type, Callable from cpl_core.application import StartupExtensionABC from cpl_core.configuration import ConfigurationABC @@ -8,11 +8,6 @@ from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC from bot_core.configuration.bot_logging_settings import BotLoggingSettings -from bot_core.configuration.bot_settings import BotSettings -from modules.base.configuration.base_settings import BaseSettings -from modules.boot_log.configuration.boot_log_settings import BootLogSettings -from modules.level.configuration.level_settings import LevelSettings -from modules.permission.configuration.permission_settings import PermissionSettings class StartupSettingsExtension(StartupExtensionABC): @@ -34,13 +29,6 @@ class StartupSettingsExtension(StartupExtensionABC): configuration.add_json_file(f"config/feature-flags.{environment.host_name}.json", optional=True) configuration.add_configuration("Startup_StartTime", str(self._start_time)) - self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id) - self._configure_settings_with_sub_settings(configuration, BaseSettings, lambda x: x.servers, lambda x: x.id) - self._configure_settings_with_sub_settings(configuration, BootLogSettings, lambda x: x.servers, lambda x: x.id) - self._configure_settings_with_sub_settings(configuration, LevelSettings, lambda x: x.servers, lambda x: x.id) - self._configure_settings_with_sub_settings( - configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id - ) self._configure_settings_with_sub_settings( configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key ) @@ -50,9 +38,9 @@ class StartupSettingsExtension(StartupExtensionABC): @staticmethod def _configure_settings_with_sub_settings( - config: ConfigurationABC, settings: Type, list_atr: Callable, atr: Callable + config: ConfigurationABC, settings_type: Type, list_atr: Callable, atr: Callable ): - settings: Optional[settings] = config.get_configuration(settings) + settings: Optional[settings_type] = config.get_configuration(settings_type) if settings is None: return diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 4b6654d9..82842240 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -93,6 +93,12 @@ } }, "modules": { + "achievements": { + "got_new_achievement": "{} hat die Errungenschaft {} freigeschaltet :D", + "commands": { + "check": "Alles klar, ich schaue eben nach... nom nom" + } + }, "auto_role": { "add": { "error": { @@ -145,6 +151,19 @@ } }, "base": { + "member_left_message": "{} hat uns leider verlassen :(", + "complaints": { + "title": "Beschwerde einreichen", + "label": "Beschwerde", + "message": "{} hat eine Beschwerde eingereicht:\n{}", + "response": "Danke für deine Beschwerde" + }, + "bug": { + "title": "Bug melden", + "label": "Bug", + "message": "{} meldet einen Bug:\n{}", + "response": "Danke für dein Feedback :D" + }, "afk_command_channel_missing_message": "Zu unfähig einem Sprachkanal beizutreten?", "afk_command_move_message": "Ich verschiebe dich ja schon... (◔_◔)", "game_server": { diff --git a/kdb-bot/src/bot_api/__init__.py b/kdb-bot/src/bot_api/__init__.py index 454c0bec..e52ba906 100644 --- a/kdb-bot/src/bot_api/__init__.py +++ b/kdb-bot/src/bot_api/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py index 81e04caa..a291d905 100644 --- a/kdb-bot/src/bot_api/abc/__init__.py +++ b/kdb-bot/src/bot_api/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.abc" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/bot-api.json b/kdb-bot/src/bot_api/bot-api.json index ec17faaf..056ba98e 100644 --- a/kdb-bot/src/bot_api/bot-api.json +++ b/kdb-bot/src/bot_api/bot-api.json @@ -3,8 +3,8 @@ "Name": "bot-api", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index c712f856..3d81c81c 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit c712f856ebe30c71ac0b144045599ed2f91a1cba +Subproject commit 3d81c81c9ef9b02f33b4f1a6167c67c040170d0a diff --git a/kdb-bot/src/bot_api/configuration/__init__.py b/kdb-bot/src/bot_api/configuration/__init__.py index 1032663c..22527b30 100644 --- a/kdb-bot/src/bot_api/configuration/__init__.py +++ b/kdb-bot/src/bot_api/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.configuration" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/configuration/api_settings.py b/kdb-bot/src/bot_api/configuration/api_settings.py index eb136f3b..62e0d71b 100644 --- a/kdb-bot/src/bot_api/configuration/api_settings.py +++ b/kdb-bot/src/bot_api/configuration/api_settings.py @@ -1,16 +1,13 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console class ApiSettings(ConfigurationModelABC): - def __init__(self): + def __init__(self, port: int = None, host: str = None, redirect_uri: bool = None): ConfigurationModelABC.__init__(self) - self._port = 80 - self._host = "" - self._redirect_to_https = False + self._port = 80 if port is None else port + self._host = "" if host is None else host + self._redirect_to_https = False if redirect_uri is None else redirect_uri @property def port(self) -> int: @@ -23,12 +20,3 @@ class ApiSettings(ConfigurationModelABC): @property def redirect_to_https(self) -> bool: return self._redirect_to_https - - def from_dict(self, settings: dict): - try: - self._port = int(settings["Port"]) - self._host = settings["Host"] - self._redirect_to_https = bool(settings["RedirectToHTTPS"]) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_api/configuration/authentication_settings.py b/kdb-bot/src/bot_api/configuration/authentication_settings.py index 72f00c99..b9c3fff2 100644 --- a/kdb-bot/src/bot_api/configuration/authentication_settings.py +++ b/kdb-bot/src/bot_api/configuration/authentication_settings.py @@ -1,19 +1,22 @@ -import traceback -from datetime import datetime - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console class AuthenticationSettings(ConfigurationModelABC): - def __init__(self): + def __init__( + self, + secret_key: str = None, + issuer: str = None, + audience: str = None, + token_expire_time: int = None, + refresh_token_expire_time: int = None, + ): ConfigurationModelABC.__init__(self) - self._secret_key = "" - self._issuer = "" - self._audience = "" - self._token_expire_time = 0 - self._refresh_token_expire_time = 0 + self._secret_key = "" if secret_key is None else secret_key + self._issuer = "" if issuer is None else issuer + self._audience = "" if audience is None else audience + self._token_expire_time = 0 if token_expire_time is None else token_expire_time + self._refresh_token_expire_time = 0 if refresh_token_expire_time is None else refresh_token_expire_time @property def secret_key(self) -> str: @@ -34,14 +37,3 @@ class AuthenticationSettings(ConfigurationModelABC): @property def refresh_token_expire_time(self) -> int: return self._refresh_token_expire_time - - def from_dict(self, settings: dict): - try: - self._secret_key = settings["SecretKey"] - self._issuer = settings["Issuer"] - self._audience = settings["Audience"] - self._token_expire_time = int(settings["TokenExpireTime"]) - self._refresh_token_expire_time = int(settings["RefreshTokenExpireTime"]) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_api/configuration/discord_authentication_settings.py b/kdb-bot/src/bot_api/configuration/discord_authentication_settings.py index eaf76eec..8dcfbc84 100644 --- a/kdb-bot/src/bot_api/configuration/discord_authentication_settings.py +++ b/kdb-bot/src/bot_api/configuration/discord_authentication_settings.py @@ -1,19 +1,23 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console from cpl_query.extension import List class DiscordAuthenticationSettings(ConfigurationModelABC): - def __init__(self): + def __init__( + self, + client_secret: str = None, + redirect_uri: str = None, + scope: list = None, + token_url: str = None, + auth_url: str = None, + ): ConfigurationModelABC.__init__(self) - self._client_secret = "" - self._redirect_url = "" - self._scope = List() - self._token_url = "" - self._auth_url = "" + self._client_secret = "" if client_secret is None else client_secret + self._redirect_url = "" if redirect_uri is None else redirect_uri + self._scope = List() if scope is None else List(str, scope) + self._token_url = "" if token_url is None else token_url + self._auth_url = "" if auth_url is None else auth_url @property def client_secret(self) -> str: @@ -34,14 +38,3 @@ class DiscordAuthenticationSettings(ConfigurationModelABC): @property def auth_url(self) -> str: return self._auth_url - - def from_dict(self, settings: dict): - try: - self._client_secret = settings["ClientSecret"] - self._redirect_url = settings["RedirectURL"] - self._scope = List(str, settings["Scope"]) - self._token_url = settings["TokenURL"] - self._auth_url = settings["AuthURL"] - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_api/configuration/frontend_settings.py b/kdb-bot/src/bot_api/configuration/frontend_settings.py index da80ab6c..58c5aee1 100644 --- a/kdb-bot/src/bot_api/configuration/frontend_settings.py +++ b/kdb-bot/src/bot_api/configuration/frontend_settings.py @@ -1,22 +1,12 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console class FrontendSettings(ConfigurationModelABC): - def __init__(self): + def __init__(self, url: str = None): ConfigurationModelABC.__init__(self) - self._url = "" + self._url = "" if url is None else url @property def url(self) -> str: return self._url - - def from_dict(self, settings: dict): - try: - self._url = settings["URL"] - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_api/configuration/version_settings.py b/kdb-bot/src/bot_api/configuration/version_settings.py deleted file mode 100644 index c1439673..00000000 --- a/kdb-bot/src/bot_api/configuration/version_settings.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import Optional - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum - - -class VersionSettings(ConfigurationModelABC): - def __init__(self, major: str = None, minor: str = None, micro: str = None): - ConfigurationModelABC.__init__(self) - - self._major: Optional[str] = major - self._minor: Optional[str] = minor - self._micro: Optional[str] = micro - - @property - def major(self) -> str: - return self._major - - @property - def minor(self) -> str: - return self._minor - - @property - def micro(self) -> str: - return self._micro - - def to_str(self) -> str: - if self._micro is None: - return f"{self._major}.{self._minor}" - else: - return f"{self._major}.{self._minor}.{self._micro}" - - def from_dict(self, settings: dict): - self._major = settings[VersionSettingsNameEnum.major.value] - self._minor = settings[VersionSettingsNameEnum.minor.value] - micro = settings[VersionSettingsNameEnum.micro.value] - if micro != "": - self._micro = micro - - def to_dict(self) -> dict: - version = { - VersionSettingsNameEnum.major.value: self._major, - VersionSettingsNameEnum.minor.value: self._minor, - } - - if self._micro is not None: - version[VersionSettingsNameEnum.micro.value] = self._micro - - return version diff --git a/kdb-bot/src/bot_api/controller/__init__.py b/kdb-bot/src/bot_api/controller/__init__.py index acf87a13..ff058d0b 100644 --- a/kdb-bot/src/bot_api/controller/__init__.py +++ b/kdb-bot/src/bot_api/controller/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.controller" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/controller/graphql_controller.py b/kdb-bot/src/bot_api/controller/graphql_controller.py index d9072029..fda337d5 100644 --- a/kdb-bot/src/bot_api/controller/graphql_controller.py +++ b/kdb-bot/src/bot_api/controller/graphql_controller.py @@ -1,5 +1,5 @@ from ariadne import graphql_sync -from ariadne.constants import PLAYGROUND_HTML +from ariadne.explorer import ExplorerPlayground from cpl_core.configuration import ConfigurationABC from cpl_core.environment import ApplicationEnvironmentABC from flask import request, jsonify @@ -30,7 +30,7 @@ class GraphQLController: if self._env.environment_name != "development": return "", 403 - return PLAYGROUND_HTML, 200 + return ExplorerPlayground().html(None), 200 @Route.post(f"{BasePath}") @Route.authorize(by_api_key=True) diff --git a/kdb-bot/src/bot_api/event/__init__.py b/kdb-bot/src/bot_api/event/__init__.py index fcc31927..6bdda951 100644 --- a/kdb-bot/src/bot_api/event/__init__.py +++ b/kdb-bot/src/bot_api/event/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.event" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/exception/__init__.py b/kdb-bot/src/bot_api/exception/__init__.py index 960f3c9f..242ada1a 100644 --- a/kdb-bot/src/bot_api/exception/__init__.py +++ b/kdb-bot/src/bot_api/exception/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.exception" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/filter/__init__.py b/kdb-bot/src/bot_api/filter/__init__.py index 09612152..369fe1ce 100644 --- a/kdb-bot/src/bot_api/filter/__init__.py +++ b/kdb-bot/src/bot_api/filter/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.filter" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/filter/discord/__init__.py b/kdb-bot/src/bot_api/filter/discord/__init__.py index 4da9c9a1..9d7d77ae 100644 --- a/kdb-bot/src/bot_api/filter/discord/__init__.py +++ b/kdb-bot/src/bot_api/filter/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.filter.discord" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/logging/__init__.py b/kdb-bot/src/bot_api/logging/__init__.py index 8e9eaaf9..5e98be69 100644 --- a/kdb-bot/src/bot_api/logging/__init__.py +++ b/kdb-bot/src/bot_api/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.logging" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/model/__init__.py b/kdb-bot/src/bot_api/model/__init__.py index bade452d..351b07f4 100644 --- a/kdb-bot/src/bot_api/model/__init__.py +++ b/kdb-bot/src/bot_api/model/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.model" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/model/discord/__init__.py b/kdb-bot/src/bot_api/model/discord/__init__.py index 855ed7fd..c987c70a 100644 --- a/kdb-bot/src/bot_api/model/discord/__init__.py +++ b/kdb-bot/src/bot_api/model/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.model.discord" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/route/__init__.py b/kdb-bot/src/bot_api/route/__init__.py index ac67fc4e..f9448643 100644 --- a/kdb-bot/src/bot_api/route/__init__.py +++ b/kdb-bot/src/bot_api/route/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.route" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/service/__init__.py b/kdb-bot/src/bot_api/service/__init__.py index 9a507608..c4386130 100644 --- a/kdb-bot/src/bot_api/service/__init__.py +++ b/kdb-bot/src/bot_api/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_api/transformer/__init__.py b/kdb-bot/src/bot_api/transformer/__init__.py index 82f1b302..706ccb1d 100644 --- a/kdb-bot/src/bot_api/transformer/__init__.py +++ b/kdb-bot/src/bot_api/transformer/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_api.transformer" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/__init__.py b/kdb-bot/src/bot_core/__init__.py index 47e3456f..711a03a8 100644 --- a/kdb-bot/src/bot_core/__init__.py +++ b/kdb-bot/src/bot_core/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py index d54823d8..44e7d2a3 100644 --- a/kdb-bot/src/bot_core/abc/__init__.py +++ b/kdb-bot/src/bot_core/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.abc" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/abc/client_utils_abc.py b/kdb-bot/src/bot_core/abc/client_utils_abc.py index 6f393128..ecb67321 100644 --- a/kdb-bot/src/bot_core/abc/client_utils_abc.py +++ b/kdb-bot/src/bot_core/abc/client_utils_abc.py @@ -7,8 +7,8 @@ from cpl_query.extension import List from discord.ext.commands import Context from bot_data.model.auto_role_rule import AutoRoleRule +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User -from modules.base.configuration.base_server_settings import BaseServerSettings class ClientUtilsABC(ABC): @@ -53,7 +53,7 @@ class ClientUtilsABC(ABC): self, created_at: datetime, user: User, - settings: BaseServerSettings, + settings: ServerConfig, is_reaction: bool = False, ) -> bool: pass diff --git a/kdb-bot/src/bot_core/bot-core.json b/kdb-bot/src/bot_core/bot-core.json index 4f134ed1..37915e47 100644 --- a/kdb-bot/src/bot_core/bot-core.json +++ b/kdb-bot/src/bot_core/bot-core.json @@ -3,8 +3,8 @@ "Name": "bot-core", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/src/bot_core/configuration/__init__.py b/kdb-bot/src/bot_core/configuration/__init__.py index 3405013e..0516cc00 100644 --- a/kdb-bot/src/bot_core/configuration/__init__.py +++ b/kdb-bot/src/bot_core/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.configuration" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/configuration/bot_logging_settings.py b/kdb-bot/src/bot_core/configuration/bot_logging_settings.py index b69e2c73..81f4968f 100644 --- a/kdb-bot/src/bot_core/configuration/bot_logging_settings.py +++ b/kdb-bot/src/bot_core/configuration/bot_logging_settings.py @@ -1,32 +1,25 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console, ForegroundColorEnum +from cpl_core.utils.json_processor import JSONProcessor from cpl_query.extension import List from bot_core.configuration.file_logging_settings import FileLoggingSettings class BotLoggingSettings(ConfigurationModelABC): - def __init__(self): + def __init__(self, **kwargs: dict): ConfigurationModelABC.__init__(self) self._files: List[FileLoggingSettings] = List(FileLoggingSettings) + if kwargs is not None: + self._files_from_dict(kwargs) + @property def files(self) -> List[FileLoggingSettings]: return self._files - def from_dict(self, settings: dict): - try: - files = List(FileLoggingSettings) - for s in settings: - st = FileLoggingSettings() - settings[s]["Key"] = s - st.from_dict(settings[s]) - files.append(st) - self._files = files - except Exception as e: - Console.set_foreground_color(ForegroundColorEnum.red) - Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") - Console.set_foreground_color(ForegroundColorEnum.default) + def _files_from_dict(self, settings: dict): + files = List(FileLoggingSettings) + for s in settings: + settings[s]["Key"] = s + files.append(JSONProcessor.process(FileLoggingSettings, settings[s])) + self._files = files diff --git a/kdb-bot/src/bot_core/configuration/bot_settings.py b/kdb-bot/src/bot_core/configuration/bot_settings.py deleted file mode 100644 index 5fb5e0ee..00000000 --- a/kdb-bot/src/bot_core/configuration/bot_settings.py +++ /dev/null @@ -1,62 +0,0 @@ -import traceback - -from cpl_core.configuration import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - -from bot_core.configuration.server_settings import ServerSettings - - -class BotSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._servers: List[ServerSettings] = List(ServerSettings) - self._technicians: List[int] = List(int) - self._wait_for_restart = 2 - self._wait_for_shutdown = 2 - self._cache_max_messages = 1000 - - @property - def servers(self) -> List[ServerSettings]: - return self._servers - - @property - def technicians(self) -> List[int]: - return self._technicians - - @property - def wait_for_restart(self) -> int: - return self._wait_for_restart - - @property - def wait_for_shutdown(self) -> int: - return self._wait_for_shutdown - - @property - def cache_max_messages(self) -> int: - return self._cache_max_messages - - def from_dict(self, settings: dict): - try: - self._technicians = settings["Technicians"] - self._wait_for_restart = settings["WaitForRestart"] - self._wait_for_shutdown = settings["WaitForShutdown"] - settings.pop("Technicians") - settings.pop("WaitForRestart") - settings.pop("WaitForShutdown") - - if "CacheMaxMessages" in settings: - self._cache_max_messages = settings["CacheMaxMessages"] - settings.pop("CacheMaxMessages") - - servers = List(ServerSettings) - for s in settings: - st = ServerSettings() - settings[s]["Id"] = s - st.from_dict(settings[s]) - servers.append(st) - self._servers = servers - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py index aeb18a99..2ef3f70f 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py @@ -3,13 +3,14 @@ from enum import Enum class FeatureFlagsEnum(Enum): # modules + achievements_module = "AchievementsModule" api_module = "ApiModule" - admin_module = "AdminModule" auto_role_module = "AutoRoleModule" base_module = "BaseModule" boot_log_module = "BootLogModule" core_module = "CoreModule" core_extension_module = "CoreExtensionModule" + config_module = "ConfigModule" data_module = "DataModule" database_module = "DatabaseModule" level_module = "LevelModule" diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py index 777e256b..f132cc03 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py @@ -1,33 +1,48 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum class FeatureFlagsSettings(ConfigurationModelABC): - def __init__(self): + _flags = { + # modules + FeatureFlagsEnum.achievements_module.value: False, # 14.06.2023 #268 + FeatureFlagsEnum.api_module.value: False, # 13.10.2022 #70 + FeatureFlagsEnum.auto_role_module.value: False, # 03.10.2022 #54 + FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.boot_log_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.core_module.value: True, # 03.10.2022 #56 + FeatureFlagsEnum.core_extension_module.value: True, # 03.10.2022 #56 + FeatureFlagsEnum.data_module.value: True, # 03.10.2022 #56 + FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48 + FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.config_module.value: True, # 19.07.2023 #127 + # features + FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70 + FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56 + FeatureFlagsEnum.version_in_presence.value: False, # 21.03.2023 #253 + } + + def __init__(self, **kwargs: dict): ConfigurationModelABC.__init__(self) - self._flags = { - # modules - FeatureFlagsEnum.api_module.value: False, # 13.10.2022 #70 - FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48 - FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54 - FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48 - FeatureFlagsEnum.boot_log_module.value: True, # 02.10.2022 #48 - FeatureFlagsEnum.core_module.value: True, # 03.10.2022 #56 - FeatureFlagsEnum.core_extension_module.value: True, # 03.10.2022 #56 - FeatureFlagsEnum.data_module.value: True, # 03.10.2022 #56 - FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48 - FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48 - FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48 - # features - FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70 - FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56 - FeatureFlagsEnum.version_in_presence.value: False, # 21.03.2023 #253 - } + if len(kwargs.keys()) == 0: + return + + for flag in [f.value for f in FeatureFlagsEnum]: + self._load_flag(kwargs, FeatureFlagsEnum(flag)) + + @classmethod + def get_flag_from_dict(cls, flags: dict, key: FeatureFlagsEnum) -> bool: + def get_flag(): + if key.value not in cls._flags: + return False + return cls._flags[key.value] + + if key.value not in flags: + return get_flag() + return flags[key.value] def get_flag(self, key: FeatureFlagsEnum) -> bool: if key.value not in self._flags: @@ -39,11 +54,3 @@ class FeatureFlagsSettings(ConfigurationModelABC): return self._flags[key.value] = bool(settings[key.value]) - - def from_dict(self, settings: dict): - try: - for flag in [f.value for f in FeatureFlagsEnum]: - self._load_flag(settings, FeatureFlagsEnum(flag)) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_core/configuration/file_logging_settings.py b/kdb-bot/src/bot_core/configuration/file_logging_settings.py index 58d461b9..46028aa5 100644 --- a/kdb-bot/src/bot_core/configuration/file_logging_settings.py +++ b/kdb-bot/src/bot_core/configuration/file_logging_settings.py @@ -1,23 +1,19 @@ -import traceback - -from cpl_core.console import Console -from cpl_core.logging import LoggingSettings +from cpl_core.logging import LoggingSettings, LoggingLevelEnum class FileLoggingSettings(LoggingSettings): - def __init__(self): - LoggingSettings.__init__(self) + def __init__( + self, + key: str, + path: str = None, + filename: str = None, + console_log_level: LoggingLevelEnum = None, + file_log_level: LoggingLevelEnum = None, + ): + LoggingSettings.__init__(self, path, filename, console_log_level, file_log_level) - self._key = "" + self._key = key @property def key(self) -> str: return self._key - - def from_dict(self, settings: dict): - try: - self._key = settings["Key"] - super().from_dict(settings) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_core/configuration/server_settings.py b/kdb-bot/src/bot_core/configuration/server_settings.py deleted file mode 100644 index 92b73d29..00000000 --- a/kdb-bot/src/bot_core/configuration/server_settings.py +++ /dev/null @@ -1,28 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console - - -class ServerSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._id: int = 0 - self._message_delete_timer: int = 0 - - @property - def id(self) -> int: - return self._id - - @property - def message_delete_timer(self) -> int: - return self._message_delete_timer - - def from_dict(self, settings: dict): - try: - self._id = int(settings["Id"]) - self._message_delete_timer = int(settings["MessageDeleteTimer"]) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/bot_core/core_extension/__init__.py b/kdb-bot/src/bot_core/core_extension/__init__.py index 74b3bcc5..2c9ac468 100644 --- a/kdb-bot/src/bot_core/core_extension/__init__.py +++ b/kdb-bot/src/bot_core/core_extension/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.core_extension" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/core_module.py b/kdb-bot/src/bot_core/core_module.py index 001dc36c..fcb06333 100644 --- a/kdb-bot/src/bot_core/core_module.py +++ b/kdb-bot/src/bot_core/core_module.py @@ -11,6 +11,8 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.events.core_on_ready_event import CoreOnReadyEvent from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_core.service.client_utils_service import ClientUtilsService +from bot_core.service.config_service import ConfigService +from bot_core.service.data_integrity_service import DataIntegrityService from bot_core.service.message_service import MessageService @@ -22,8 +24,10 @@ class CoreModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(ConfigService) services.add_transient(MessageServiceABC, MessageService) services.add_transient(ClientUtilsABC, ClientUtilsService) + services.add_transient(DataIntegrityService) # pipes services.add_transient(DateTimeOffsetPipe) diff --git a/kdb-bot/src/bot_core/events/__init__.py b/kdb-bot/src/bot_core/events/__init__.py index b0e09ec6..3ba79156 100644 --- a/kdb-bot/src/bot_core/events/__init__.py +++ b/kdb-bot/src/bot_core/events/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.events" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/exception/__init__.py b/kdb-bot/src/bot_core/exception/__init__.py index 9111d7f8..deb1be18 100644 --- a/kdb-bot/src/bot_core/exception/__init__.py +++ b/kdb-bot/src/bot_core/exception/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.exception" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/helper/__init__.py b/kdb-bot/src/bot_core/helper/__init__.py index 1fa47e94..7d3252df 100644 --- a/kdb-bot/src/bot_core/helper/__init__.py +++ b/kdb-bot/src/bot_core/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.helper" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/logging/__init__.py b/kdb-bot/src/bot_core/logging/__init__.py index b1d60372..ab2e0e97 100644 --- a/kdb-bot/src/bot_core/logging/__init__.py +++ b/kdb-bot/src/bot_core/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.logging" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/pipes/__init__.py b/kdb-bot/src/bot_core/pipes/__init__.py index 2ff936d6..65872495 100644 --- a/kdb-bot/src/bot_core/pipes/__init__.py +++ b/kdb-bot/src/bot_core/pipes/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.pipes" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_core/service/__init__.py b/kdb-bot/src/bot_core/service/__init__.py index 2168af85..03879445 100644 --- a/kdb-bot/src/bot_core/service/__init__.py +++ b/kdb-bot/src/bot_core/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_core.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") 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 01dafdda..104c5267 100644 --- a/kdb-bot/src/bot_core/service/client_utils_service.py +++ b/kdb-bot/src/bot_core/service/client_utils_service.py @@ -25,9 +25,9 @@ from bot_data.abc.user_message_count_per_hour_repository_abc import ( UserMessageCountPerHourRepositoryABC, ) from bot_data.model.auto_role_rule import AutoRoleRule +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User from bot_data.model.user_message_count_per_hour import UserMessageCountPerHour -from modules.base.configuration.base_server_settings import BaseServerSettings class ClientUtilsService(ClientUtilsABC): @@ -139,7 +139,7 @@ class ClientUtilsService(ClientUtilsABC): self, created_at: datetime, user: User, - settings: BaseServerSettings, + settings: ServerConfig, is_reaction: bool = False, ) -> bool: umcph = None diff --git a/kdb-bot/src/bot_core/service/config_service.py b/kdb-bot/src/bot_core/service/config_service.py new file mode 100644 index 00000000..2b9d9196 --- /dev/null +++ b/kdb-bot/src/bot_core/service/config_service.py @@ -0,0 +1,50 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceProviderABC + +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC +from bot_data.model.server import Server +from bot_data.model.technician_config import TechnicianConfig +from bot_data.service.server_config_seeder import ServerConfigSeeder +from bot_data.service.technician_config_seeder import TechnicianConfigSeeder +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class ConfigService: + def __init__( + self, + config: ConfigurationABC, + services: ServiceProviderABC, + technician_config_repo: TechnicianConfigRepositoryABC, + server_config_repo: ServerConfigRepositoryABC, + tech_seeder: TechnicianConfigSeeder, + server_seeder: ServerConfigSeeder, + ): + self._config = config + self._services = services + self._technician_config_repo = technician_config_repo + self._server_config_repo = server_config_repo + + self._tech_seeder = tech_seeder + self._server_seeder = server_seeder + + async def reload_technician_config(self): + if not self._technician_config_repo.does_technician_config_exists(): + await self._tech_seeder.seed() + + technician_config = self._technician_config_repo.get_technician_config() + self._config.add_configuration(TechnicianConfig, technician_config) + self._config.add_configuration(FeatureFlagsSettings, FeatureFlagsSettings(**technician_config.feature_flags)) + + async def reload_server_config(self, server: Server): + if not self._server_config_repo.does_server_config_exists(server.id): + await self._server_seeder.seed() + + server_config = self._server_config_repo.get_server_config_by_server(server.id) + self._config.add_configuration( + f"{type(server_config).__name__}_{server_config.server.discord_id}", server_config + ) + + permissions: PermissionServiceABC = self._services.get_service(PermissionServiceABC) + permissions.on_ready() diff --git a/kdb-bot/src/bot_core/service/data_integrity_service.py b/kdb-bot/src/bot_core/service/data_integrity_service.py new file mode 100644 index 00000000..946d6d29 --- /dev/null +++ b/kdb-bot/src/bot_core/service/data_integrity_service.py @@ -0,0 +1,396 @@ +from datetime import datetime, timedelta +from typing import Union + +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.logging.database_logger import DatabaseLogger +from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe +from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_repository_abc import ( + UserJoinedVoiceChannelRepositoryABC, +) +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.server import Server +from bot_data.model.server_config import ServerConfig +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 bot_data.service.seeder_service import SeederService +from bot_data.service.user_repository_service import ServerRepositoryABC +from modules.achievements.achievement_service import AchievementService + + +class DataIntegrityService: + def __init__( + self, + config: ConfigurationABC, + logger: DatabaseLogger, + seeder: SeederService, + bot: DiscordBotServiceABC, + db_context: DatabaseContextABC, + server_repo: ServerRepositoryABC, + user_repo: UserRepositoryABC, + client_repo: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + user_joins: UserJoinedServerRepositoryABC, + user_joins_vc: UserJoinedVoiceChannelRepositoryABC, + user_joined_gs: UserJoinedGameServerRepositoryABC, + achievement_service: AchievementService, + dtp: DateTimeOffsetPipe, + ): + self._config = config + + self._logger = logger + self._seeder = seeder + self._bot = bot + self._db_context = db_context + self._servers = server_repo + self._users = user_repo + self._clients = client_repo + self._known_users = known_users + self._user_joins = user_joins + self._user_joins_vc = user_joins_vc + self._user_joined_gs = user_joined_gs + self._achievements = achievement_service + self._dtp = dtp + + self._is_for_shutdown = False + + def _check_known_users(self): + self._logger.debug(__name__, f"Start checking KnownUsers table, {len(self._bot.users)}") + for u in self._bot.users: + u: discord.User = u + try: + if u.bot: + self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") + continue + + user = self._known_users.find_user_by_discord_id(u.id) + if user is not None: + continue + + self._logger.warn(__name__, f"Unknown user: {u.id}") + self._logger.debug(__name__, f"Add user: {u.id}") + self._known_users.add_user(KnownUser(u.id)) + self._db_context.save_changes() + + user = self._known_users.find_user_by_discord_id(u.id) + if user is None: + self._logger.fatal(__name__, f"Cannot add user: {u.id}") + + self._logger.debug(__name__, f"Added user: {u.id}") + except Exception as e: + self._logger.error(__name__, f"Cannot get user", e) + + def _check_servers(self): + self._logger.debug(__name__, f"Start checking Servers table") + for g in self._bot.guilds: + g: discord.Guild = g + try: + server = self._servers.find_server_by_discord_id(g.id) + if server is not None: + continue + + self._logger.warn(__name__, f"Server not found in database: {g.id}") + self._logger.debug(__name__, f"Add server: {g.id}") + self._servers.add_server(Server(g.id)) + self._db_context.save_changes() + + server = self._servers.find_server_by_discord_id(g.id) + if server is None: + self._logger.fatal(__name__, f"Cannot add server: {g.id}") + + self._logger.debug(__name__, f"Added server: {g.id}") + except Exception as e: + self._logger.error(__name__, f"Cannot get server", e) + + results = self._servers.get_servers() + if results is None or len(results) == 0: + self._logger.error(__name__, f"Table Servers is empty!") + + def _check_clients(self): + self._logger.debug(__name__, f"Start checking Clients table") + for g in self._bot.guilds: + g: discord.Guild = g + try: + server: Server = self._servers.find_server_by_discord_id(g.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {g.id}") + + client = self._clients.find_client_by_server_id(server.id) + if client is not None: + continue + + self._logger.warn( + __name__, + f"Client for server {g.id} not found in database: {self._bot.user.id}", + ) + self._logger.debug(__name__, f"Add client: {self._bot.user.id}") + self._clients.add_client(Client(self._bot.user.id, 0, 0, 0, 0, 0, server)) + self._db_context.save_changes() + + client = self._clients.find_client_by_server_id(server.id) + if client is None: + self._logger.fatal( + __name__, + f"Cannot add client {self._bot.user.id} for server {g.id}", + ) + + self._logger.debug(__name__, f"Added client: {g.id}") + except Exception as e: + self._logger.error(__name__, f"Cannot get client", e) + + results = self._servers.get_servers() + if results is None or len(results) == 0: + self._logger.error(__name__, f"Table Servers is empty!") + + def _check_users(self): + self._logger.debug(__name__, f"Start checking Users table") + for g in self._bot.guilds: + g: discord.Guild = g + + try: + server = self._servers.find_server_by_discord_id(g.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {g.id}") + + for u in g.members: + u: Union[discord.Member, discord.User] = u + if u.bot: + self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id) + if user is not None: + continue + + self._logger.warn(__name__, f"User not found in database: {u.id}") + self._logger.debug(__name__, f"Add user: {u.id}") + self._users.add_user(User(u.id, 0, 0, 0, server)) + self._db_context.save_changes() + + self._logger.debug(__name__, f"Added User: {u.id}") + except Exception as e: + self._logger.error(__name__, f"Cannot get User", e) + + results = self._users.get_users() + if results is None or len(results) == 0: + self._logger.error(__name__, f"Table Users is empty!") + + def _check_user_joins(self): + self._logger.debug(__name__, f"Start checking UserJoinedServers table") + for guild in self._bot.guilds: + guild: discord.Guild = guild + + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {guild.id}") + + try: + for u in guild.members: + u: discord.User = u + if u.bot: + self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id) + if user is None: + self._logger.fatal(__name__, f"User not found in database: {u.id}") + + join = self._user_joins.find_active_user_joined_server_by_user_id(user.id) + if join is not None: + continue + + m: discord.Member = u + self._logger.warn( + __name__, + f"Active UserJoinedServer not found in database: {guild.id}:{u.id}@{m.joined_at}", + ) + self._logger.debug( + __name__, + f"Add UserJoinedServer: {guild.id}:{u.id}@{m.joined_at}", + ) + self._user_joins.add_user_joined_server( + UserJoinedServer(user, self._dtp.transform(m.joined_at), None) + ) + self._db_context.save_changes() + + self._logger.debug(__name__, f"Added UserJoinedServer: {u.id}") + except Exception as e: + self._logger.error(__name__, f"Cannot get UserJoinedServer", e) + + results = self._users.get_users() + if results is None or len(results) == 0: + self._logger.error(__name__, f"Table Users is empty!") + + joins = self._user_joins.get_user_joined_servers() + for join in joins: + join: UserJoinedServer = join + if join.user.server.discord_id != guild.id: + continue + + if join.leaved_on is not None: + continue + + dc_user = guild.get_member(join.user.discord_id) + if dc_user is None: + self._logger.warn( + __name__, + f"User {join.user.discord_id} already left the server.", + ) + join.leaved_on = datetime.now() + self._user_joins.update_user_joined_server(join) + + self._db_context.save_changes() + + def _check_user_joins_vc(self): + self._logger.debug(__name__, f"Start checking UserJoinedVoiceChannel table") + for guild in self._bot.guilds: + guild: discord.Guild = guild + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") + + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {guild.id}") + + try: + # close open voice states + for member in guild.members: + if member.bot: + self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") + continue + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) + if user is None: + self._logger.fatal(__name__, f"User not found in database: {member.id}") + + joins = self._user_joins_vc.find_active_user_joined_voice_channels_by_user_id(user.id) + if joins is None or len(joins) == 0: + continue + + for join in joins: + self._logger.warn( + __name__, + f"Active UserJoinedVoiceChannel found in database: {guild.id}:{member.id}@{join.joined_on}", + ) + join.leaved_on = datetime.now() + + if ( + (join.leaved_on - join.joined_on).total_seconds() / 60 / 60 + ) > settings.max_voice_state_hours: + join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours) + + self._user_joins_vc.update_user_joined_voice_channel(join) + + if self._is_for_shutdown: + user.xp += round(join.time * settings.xp_per_ontime_hour) + self._users.update_user(user) + + self._db_context.save_changes() + if self._is_for_shutdown: + return + + # add open voice states + for member in guild.members: + if member.bot: + self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") + continue + + if member.voice is None or member.voice.channel.id in settings.afk_channel_ids: + continue + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) + if user is None: + self._logger.fatal(__name__, f"User not found in database: {member.id}") + + join = UserJoinedVoiceChannel(user, member.voice.channel.id, datetime.now()) + self._user_joins_vc.add_user_joined_voice_channel(join) + self._db_context.save_changes() + + except Exception as e: + self._logger.error(__name__, f"Cannot get UserJoinedVoiceChannel", e) + + def _check_user_joined_gs(self): + self._logger.debug(__name__, f"Start checking UserJoinedGameServer table") + for guild in self._bot.guilds: + guild: discord.Guild = guild + + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {guild.id}") + + try: + for member in guild.members: + if member.bot: + self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") + continue + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) + if user is None: + self._logger.fatal(__name__, f"User not found in database: {member.id}") + + joins = self._user_joined_gs.find_active_user_joined_game_servers_by_user_id(user.id) + if joins is None or len(joins) == 0: + continue + + for join in joins: + self._logger.warn( + __name__, + f"Active UserJoinedGameServer found in database: {guild.id}:{member.id}@{join.joined_on}", + ) + join.leaved_on = datetime.now() + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") + + if ( + (join.leaved_on - join.joined_on).total_seconds() / 60 / 60 + ) > settings.max_voice_state_hours: + join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours) + + self._user_joined_gs.update_user_joined_game_server(join) + if self._is_for_shutdown: + user.xp += round(join.time * settings.xp_per_ontime_hour) + self._users.update_user(user) + + self._db_context.save_changes() + except Exception as e: + self._logger.error(__name__, f"Cannot get UserJoinedGameServer", e) + + async def _check_for_user_achievements(self): + self._logger.debug(__name__, f"Start checking UserGotAchievement table") + + for guild in self._bot.guilds: + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f"Server not found in database: {guild.id}") + + for member in guild.members: + if member.bot: + self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") + continue + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) + if user is None: + self._logger.fatal(__name__, f"User not found in database: {member.id}") + + await self._achievements.validate_achievements_for_user(user) + + async def check_data_integrity(self, is_for_shutdown=False): + if is_for_shutdown != self._is_for_shutdown: + self._is_for_shutdown = is_for_shutdown + + self._check_known_users() + self._check_servers() + self._check_clients() + self._check_users() + self._check_user_joins() + self._check_user_joins_vc() + self._check_user_joined_gs() + await self._check_for_user_achievements() diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 8b07cb79..71caa831 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -10,10 +10,10 @@ from discord import Interaction from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.server_settings import ServerSettings from bot_core.helper.log_message_helper import LogMessageHelper from bot_core.logging.message_logger import MessageLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.model.server_config import ServerConfig class MessageService(MessageServiceABC): @@ -33,7 +33,7 @@ class MessageService(MessageServiceABC): async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): self._logger.debug(__name__, f"Try to delete {messages.count()} messages") - server_st: ServerSettings = self._config.get_configuration(f"ServerSettings_{guild_id}") + server_st: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild_id}") await asyncio.sleep(server_st.message_delete_timer) for message in messages: await self.delete_message(message, mass_delete=True, without_tracking=without_tracking) @@ -50,7 +50,7 @@ class MessageService(MessageServiceABC): else None ) - server_st: ServerSettings = self._config.get_configuration(f"ServerSettings_{guild_id}") + server_st: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild_id}") if not mass_delete: await asyncio.sleep(server_st.message_delete_timer) self._logger.debug( diff --git a/kdb-bot/src/bot_data/__init__.py b/kdb-bot/src/bot_data/__init__.py index 05d65f88..c1f500c7 100644 --- a/kdb-bot/src/bot_data/__init__.py +++ b/kdb-bot/src/bot_data/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_data" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py index 924b1813..5d434b5f 100644 --- a/kdb-bot/src/bot_data/abc/__init__.py +++ b/kdb-bot/src/bot_data/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_data.abc" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_data/abc/achievement_repository_abc.py b/kdb-bot/src/bot_data/abc/achievement_repository_abc.py new file mode 100644 index 00000000..ff3014fe --- /dev/null +++ b/kdb-bot/src/bot_data/abc/achievement_repository_abc.py @@ -0,0 +1,52 @@ +from abc import ABC, abstractmethod + +from cpl_query.extension import List + +from bot_data.model.achievement import Achievement +from bot_data.model.user_got_achievement import UserGotAchievement + + +class AchievementRepositoryABC(ABC): + @abstractmethod + def __init__(self): + pass + + @abstractmethod + def get_achievements(self) -> List[Achievement]: + pass + + @abstractmethod + def get_achievement_by_id(self, id: int) -> Achievement: + pass + + @abstractmethod + def get_achievements_by_server_id(self, server_id: int) -> List[Achievement]: + pass + + @abstractmethod + def get_achievements_by_user_id(self, user_id: int) -> List[Achievement]: + pass + + @abstractmethod + def get_user_got_achievements_by_achievement_id(self, achievement_id: int) -> List[Achievement]: + pass + + @abstractmethod + def add_achievement(self, achievement: Achievement): + pass + + @abstractmethod + def update_achievement(self, achievement: Achievement): + pass + + @abstractmethod + def delete_achievement(self, achievement: Achievement): + pass + + @abstractmethod + def add_user_got_achievement(self, join: UserGotAchievement): + pass + + @abstractmethod + def delete_user_got_achievement(self, join: UserGotAchievement): + pass diff --git a/kdb-bot/src/bot_data/abc/data_seeder_abc.py b/kdb-bot/src/bot_data/abc/data_seeder_abc.py index e49dc486..1907f25e 100644 --- a/kdb-bot/src/bot_data/abc/data_seeder_abc.py +++ b/kdb-bot/src/bot_data/abc/data_seeder_abc.py @@ -7,5 +7,5 @@ class DataSeederABC(ABC): pass @abstractmethod - def seed(self): + async def seed(self): pass diff --git a/kdb-bot/src/bot_data/abc/server_config_repository_abc.py b/kdb-bot/src/bot_data/abc/server_config_repository_abc.py new file mode 100644 index 00000000..f66fdc89 --- /dev/null +++ b/kdb-bot/src/bot_data/abc/server_config_repository_abc.py @@ -0,0 +1,59 @@ +from abc import ABC, abstractmethod + +from bot_data.model.server_afk_channel_ids_config import ServerAFKChannelIdsConfig +from bot_data.model.server_config import ServerConfig +from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig + + +class ServerConfigRepositoryABC(ABC): + @abstractmethod + def __init__(self): + pass + + @abstractmethod + def does_server_config_exists(self, server_id: int) -> bool: + pass + + @abstractmethod + def get_server_config_by_server(self, server_id: int) -> ServerConfig: + pass + + @abstractmethod + def get_server_config_by_id(self, config_id: int) -> ServerConfig: + pass + + @abstractmethod + def add_server_config(self, server_config: ServerConfig): + pass + + @abstractmethod + def update_server_config(self, server_config: ServerConfig): + pass + + @abstractmethod + def delete_server_config(self, server_config: ServerConfig): + pass + + @abstractmethod + def add_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + pass + + @abstractmethod + def update_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + pass + + @abstractmethod + def delete_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + pass + + @abstractmethod + def add_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + pass + + @abstractmethod + def update_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + pass + + @abstractmethod + def delete_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + pass diff --git a/kdb-bot/src/bot_data/abc/technician_config_repository_abc.py b/kdb-bot/src/bot_data/abc/technician_config_repository_abc.py new file mode 100644 index 00000000..25c88d96 --- /dev/null +++ b/kdb-bot/src/bot_data/abc/technician_config_repository_abc.py @@ -0,0 +1,55 @@ +from abc import ABC, abstractmethod + +from bot_data.model.technician_config import TechnicianConfig +from bot_data.model.technician_id_config import TechnicianIdConfig +from bot_data.model.technician_ping_url_config import TechnicianPingUrlConfig + + +class TechnicianConfigRepositoryABC(ABC): + @abstractmethod + def __init__(self): + pass + + @abstractmethod + def does_technician_config_exists(self) -> bool: + pass + + @abstractmethod + def get_technician_config(self) -> TechnicianConfig: + pass + + @abstractmethod + def add_technician_config(self, technician_config: TechnicianConfig): + pass + + @abstractmethod + def update_technician_config(self, technician_config: TechnicianConfig): + pass + + @abstractmethod + def delete_technician_config(self, technician_config: TechnicianConfig): + pass + + @abstractmethod + def add_technician_id_config(self, technician_id: TechnicianIdConfig): + pass + + @abstractmethod + def update_technician_id_config(self, technician_id: TechnicianIdConfig): + pass + + @abstractmethod + def delete_technician_id_config(self, technician_id: TechnicianIdConfig): + pass + + @abstractmethod + def add_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + pass + + @abstractmethod + def update_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + pass + + @abstractmethod + def delete_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + pass diff --git a/kdb-bot/src/bot_data/bot-data.json b/kdb-bot/src/bot_data/bot-data.json index fd32f120..a824e526 100644 --- a/kdb-bot/src/bot_data/bot-data.json +++ b/kdb-bot/src/bot_data/bot-data.json @@ -3,8 +3,8 @@ "Name": "bot-data", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index 899151d6..9c35713e 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -5,14 +5,18 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.data_seeder_abc import DataSeederABC from bot_data.abc.game_server_repository_abc import GameServerRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC from bot_data.abc.user_game_ident_repository_abc import UserGameIdentRepositoryABC from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC @@ -24,6 +28,7 @@ from bot_data.abc.user_message_count_per_hour_repository_abc import ( ) from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC +from bot_data.service.achievements_repository_service import AchievementRepositoryService from bot_data.service.api_key_repository_service import ApiKeyRepositoryService from bot_data.service.auth_user_repository_service import AuthUserRepositoryService from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService @@ -33,7 +38,11 @@ from bot_data.service.game_server_repository_service import GameServerRepository from bot_data.service.known_user_repository_service import KnownUserRepositoryService from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.seeder_service import SeederService +from bot_data.service.server_config_repository_service import ServerConfigRepositoryService +from bot_data.service.server_config_seeder import ServerConfigSeeder from bot_data.service.server_repository_service import ServerRepositoryService +from bot_data.service.technician_config_repository_service import TechnicianConfigRepositoryService +from bot_data.service.technician_config_seeder import TechnicianConfigSeeder from bot_data.service.user_game_ident_repository_service import UserGameIdentRepositoryService from bot_data.service.user_joined_game_server_repository_service import UserJoinedGameServerRepositoryService from bot_data.service.user_joined_server_repository_service import ( @@ -77,5 +86,10 @@ class DataModule(ModuleABC): ) services.add_transient(GameServerRepositoryABC, GameServerRepositoryService) services.add_transient(UserGameIdentRepositoryABC, UserGameIdentRepositoryService) + services.add_transient(AchievementRepositoryABC, AchievementRepositoryService) + services.add_transient(TechnicianConfigRepositoryABC, TechnicianConfigRepositoryService) + services.add_transient(ServerConfigRepositoryABC, ServerConfigRepositoryService) services.add_transient(SeederService) + services.add_transient(DataSeederABC, TechnicianConfigSeeder) + services.add_transient(DataSeederABC, ServerConfigSeeder) diff --git a/kdb-bot/src/bot_data/db_context.py b/kdb-bot/src/bot_data/db_context.py index 46eff3bb..fb5e9f55 100644 --- a/kdb-bot/src/bot_data/db_context.py +++ b/kdb-bot/src/bot_data/db_context.py @@ -11,6 +11,7 @@ class DBContext(DatabaseContext): self._logger = logger DatabaseContext.__init__(self) + self._fails = 0 def connect(self, database_settings: DatabaseSettings): try: @@ -32,7 +33,11 @@ class DBContext(DatabaseContext): try: return super(DBContext, self).select(statement) except Exception as e: + if self._fails >= 3: + self._logger.fatal(__name__, f"Database error caused by {statement}", e) + self._logger.error(__name__, f"Database error caused by {statement}", e) + self._fails += 1 try: time.sleep(0.5) return self.select(statement) diff --git a/kdb-bot/src/bot_data/migration/__init__.py b/kdb-bot/src/bot_data/migration/__init__.py index 19f96ce2..3c3215db 100644 --- a/kdb-bot/src/bot_data/migration/__init__.py +++ b/kdb-bot/src/bot_data/migration/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_data.migration" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_data/migration/achievements_migration.py b/kdb-bot/src/bot_data/migration/achievements_migration.py new file mode 100644 index 00000000..f3a454d3 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/achievements_migration.py @@ -0,0 +1,127 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class AchievementsMigration(MigrationABC): + name = "1.1.0_AchievementsMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `Achievements` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `Name` VARCHAR(255) NOT NULL, + `Description` VARCHAR(255) NOT NULL, + `Attribute` VARCHAR(255) NOT NULL, + `Operator` VARCHAR(255) NOT NULL, + `Value` VARCHAR(255) NOT NULL, + `ServerId` BIGINT, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `AchievementsHistory` + ( + `Id` BIGINT(20) NOT NULL, + `Name` VARCHAR(255) NOT NULL, + `Description` VARCHAR(255) NOT NULL, + `Attribute` VARCHAR(255) NOT NULL, + `Operator` VARCHAR(255) NOT NULL, + `Value` VARCHAR(255) NOT NULL, + `ServerId` BIGINT, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `UserGotAchievements` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `UserId` BIGINT, + `AchievementId` BIGINT, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`UserId`) REFERENCES `Users`(`UserId`), + FOREIGN KEY (`AchievementId`) REFERENCES `Achievements`(`Id`) + ); + """ + ) + ) + + # A join table history between users and achievements is not necessary. + + self._cursor.execute(str(f"""ALTER TABLE Users ADD MessageCount BIGINT NOT NULL DEFAULT 0 AFTER XP;""")) + self._cursor.execute(str(f"""ALTER TABLE Users ADD ReactionCount BIGINT NOT NULL DEFAULT 0 AFTER XP;""")) + self._cursor.execute(str(f"""ALTER TABLE UsersHistory ADD MessageCount BIGINT NOT NULL DEFAULT 0 AFTER XP;""")) + self._cursor.execute(str(f"""ALTER TABLE UsersHistory ADD ReactionCount BIGINT NOT NULL DEFAULT 0 AFTER XP;""")) + + self._cursor.execute(str(f"""DROP TRIGGER IF EXISTS `TR_AchievementsUpdate`;""")) + self._cursor.execute( + str( + f""" + CREATE TRIGGER `TR_AchievementsUpdate` + AFTER UPDATE + ON `Achievements` + FOR EACH ROW + BEGIN + INSERT INTO `AchievementsHistory` ( + `Id`, `Name`, `Description`, `Attribute`, `Operator`, `Value`, `ServerId`, `DateFrom`, `DateTo` + ) + VALUES ( + OLD.Id, OLD.Name, OLD.Description, OLD.Attribute, OLD.Operator, OLD.Value, OLD.ServerId, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6) + ); + END; + """ + ) + ) + + self._cursor.execute(str(f"""DROP TRIGGER IF EXISTS `TR_AchievementsDelete`;""")) + + self._cursor.execute( + str( + f""" + CREATE TRIGGER `TR_AchievementsDelete` + AFTER DELETE + ON `Achievements` + FOR EACH ROW + BEGIN + INSERT INTO `AchievementsHistory` ( + `Id`, `Name`, `Description`, `Attribute`, `Operator`, `Value`, `ServerId`, `Deleted`, `DateFrom`, `DateTo` + ) + VALUES ( + OLD.Id, OLD.Name, OLD.Description, OLD.Attribute, OLD.Operator, OLD.Value, OLD.ServerId, TRUE, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6) + ); + END; + """ + ) + ) + + def downgrade(self): + self._cursor.execute("DROP TABLE `Achievements`;") + + self._cursor.execute(str(f"""ALTER TABLE Users DROP COLUMN MessageCount;""")) + self._cursor.execute(str(f"""ALTER TABLE Users DROP COLUMN ReactionCount;""")) diff --git a/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py b/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py new file mode 100644 index 00000000..f9046565 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py @@ -0,0 +1,29 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class ConfigFeatureFlagsMigration(MigrationABC): + name = "1.1.0_ConfigFeatureFlagsMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + self._cursor.execute( + str("""ALTER TABLE CFG_Technician ADD FeatureFlags JSON NULL DEFAULT ('{}') AFTER CacheMaxMessages;""") + ) + + self._cursor.execute( + str("""ALTER TABLE CFG_Server ADD FeatureFlags JSON NULL DEFAULT ('{}') AFTER LoginMessageChannelId;""") + ) + + def downgrade(self): + self._logger.debug(__name__, "Running downgrade") + self._cursor.execute("ALTER TABLE CFG_Technician DROP COLUMN FeatureFlags;") + self._cursor.execute("ALTER TABLE CFG_Server DROP COLUMN FeatureFlags;") diff --git a/kdb-bot/src/bot_data/migration/config_migration.py b/kdb-bot/src/bot_data/migration/config_migration.py new file mode 100644 index 00000000..556b396f --- /dev/null +++ b/kdb-bot/src/bot_data/migration/config_migration.py @@ -0,0 +1,155 @@ +import os + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class ConfigMigration(MigrationABC): + name = "1.1.0_ConfigMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def _exec(self, file: str): + path = f"{os.path.dirname(os.path.realpath(__file__))}/db_history_scripts" + sql = open(f"{path}/{file}").read() + + for statement in sql.split("\n\n"): + self._cursor.execute(statement + ";") + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + self._server_upgrade() + self._technician_upgrade() + + self._exec("config/server.sql") + self._exec("config/server_afk_channels.sql") + self._exec("config/server_team_roles.sql") + self._exec("config/technician.sql") + self._exec("config/technician_ids.sql") + self._exec("config/technician_ping_urls.sql") + + def _server_upgrade(self): + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_Server` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `MessageDeleteTimer` BIGINT NOT NULL DEFAULT 6, + `NotificationChatId` BIGINT NOT NULL, + `MaxVoiceStateHours` BIGINT NOT NULL DEFAULT 6, + `XpPerMessage` BIGINT NOT NULL DEFAULT 1, + `XpPerReaction` BIGINT NOT NULL DEFAULT 1, + `MaxMessageXpPerHour` BIGINT NOT NULL DEFAULT 20, + `XpPerOntimeHour` BIGINT NOT NULL DEFAULT 10, + `XpPerEventParticipation` BIGINT NOT NULL DEFAULT 10, + `XpPerAchievement` BIGINT NOT NULL DEFAULT 10, + `AFKCommandChannelId` BIGINT NOT NULL, + `HelpVoiceChannelId` BIGINT NOT NULL, + `TeamChannelId` BIGINT NOT NULL, + `LoginMessageChannelId` BIGINT NOT NULL, + `ServerId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_ServerAFKChannelIds` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `ChannelId` BIGINT NOT NULL, + `ServerId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_ServerTeamRoleIds` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `RoleId` BIGINT NOT NULL, + `TeamMemberType` ENUM('Moderator', 'Admin') NOT NULL, + `ServerId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """ + ) + ) + + def _technician_upgrade(self): + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_Technician` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `HelpCommandReferenceUrl` VARCHAR(255) NOT NULL, + `WaitForRestart` BIGINT NOT NULL DEFAULT 8, + `WaitForShutdown` BIGINT NOT NULL DEFAULT 8, + `CacheMaxMessages` BIGINT NOT NULL DEFAULT 1000000, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`) + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_TechnicianPingUrls` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `URL` VARCHAR(255) NOT NULL, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`) + ); + """ + ) + ) + + self._cursor.execute( + str( + f""" + CREATE TABLE IF NOT EXISTS `CFG_TechnicianIds` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `TechnicianId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6), + `LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY(`Id`) + ); + """ + ) + ) + + def downgrade(self): + self._logger.debug(__name__, "Running downgrade") + self._server_downgrade() + self._technician_downgrade() + + def _server_downgrade(self): + self._cursor.execute("DROP TABLE `CFG_Server`;") + + def _technician_downgrade(self): + self._cursor.execute("DROP TABLE `CFG_Technician`;") + self._cursor.execute("DROP TABLE `CFG_TechnicianPingUrls`;") + self._cursor.execute("DROP TABLE `CFG_TechnicianIds`;") diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/server.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server.sql new file mode 100644 index 00000000..e9c327b8 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server.sql @@ -0,0 +1,117 @@ +CREATE TABLE IF NOT EXISTS `CFG_ServerHistory` +( + `Id` BIGINT(20) NOT NULL, + `MessageDeleteTimer` BIGINT NOT NULL DEFAULT 6, + `NotificationChatId` BIGINT NOT NULL, + `MaxVoiceStateHours` BIGINT NOT NULL DEFAULT 6, + `XpPerMessage` BIGINT NOT NULL DEFAULT 1, + `XpPerReaction` BIGINT NOT NULL DEFAULT 1, + `MaxMessageXpPerHour` BIGINT NOT NULL DEFAULT 20, + `XpPerOntimeHour` BIGINT NOT NULL DEFAULT 10, + `XpPerEventParticipation` BIGINT NOT NULL DEFAULT 10, + `XpPerAchievement` BIGINT NOT NULL DEFAULT 10, + `AFKCommandChannelId` BIGINT NOT NULL, + `HelpVoiceChannelId` BIGINT NOT NULL, + `TeamChannelId` BIGINT NOT NULL, + `LoginMessageChannelId` BIGINT NOT NULL, + `ServerId` BIGINT NOT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_ServerUpdate`; + +CREATE TRIGGER `TR_CFG_ServerUpdate` + AFTER UPDATE + ON `CFG_Server` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerHistory` ( + `Id`, + `MessageDeleteTimer`, + `NotificationChatId`, + `MaxVoiceStateHours`, + `XpPerMessage`, + `XpPerReaction`, + `MaxMessageXpPerHour`, + `XpPerOntimeHour`, + `XpPerEventParticipation`, + `XpPerAchievement`, + `AFKCommandChannelId`, + `HelpVoiceChannelId`, + `TeamChannelId`, + `LoginMessageChannelId`, + `ServerId`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.MessageDeleteTimer, + OLD.NotificationChatId, + OLD.MaxVoiceStateHours, + OLD.XpPerMessage, + OLD.XpPerReaction, + OLD.MaxMessageXpPerHour, + OLD.XpPerOntimeHour, + OLD.XpPerEventParticipation, + OLD.XpPerAchievement, + OLD.AFKCommandChannelId, + OLD.HelpVoiceChannelId, + OLD.TeamChannelId, + OLD.LoginMessageChannelId, + OLD.ServerId, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_ServerDelete`; + +CREATE TRIGGER `TR_CFG_ServerDelete` + AFTER DELETE + ON `CFG_Server` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerHistory` ( + `Id`, + `MessageDeleteTimer`, + `NotificationChatId`, + `MaxVoiceStateHours`, + `XpPerMessage`, + `XpPerReaction`, + `MaxMessageXpPerHour`, + `XpPerOntimeHour`, + `XpPerEventParticipation`, + `XpPerAchievement`, + `AFKCommandChannelId`, + `HelpVoiceChannelId`, + `TeamChannelId`, + `LoginMessageChannelId`, + `ServerId`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.MessageDeleteTimer, + OLD.NotificationChatId, + OLD.MaxVoiceStateHours, + OLD.XpPerMessage, + OLD.XpPerReaction, + OLD.MaxMessageXpPerHour, + OLD.XpPerOntimeHour, + OLD.XpPerEventParticipation, + OLD.XpPerAchievement, + OLD.AFKCommandChannelId, + OLD.HelpVoiceChannelId, + OLD.TeamChannelId, + OLD.LoginMessageChannelId, + OLD.ServerId, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_afk_channels.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_afk_channels.sql new file mode 100644 index 00000000..4dfd7e31 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_afk_channels.sql @@ -0,0 +1,57 @@ +CREATE TABLE IF NOT EXISTS `CFG_ServerAFKChannelIdsHistory` +( + `Id` BIGINT(20) NOT NULL, + `ChannelId` BIGINT NOT NULL, + `ServerId` BIGINT NOT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_ServerAFKChannelIdsUpdate`; + +CREATE TRIGGER `TR_CFG_ServerAFKChannelIdsUpdate` + AFTER UPDATE + ON `CFG_ServerAFKChannelIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerAFKChannelIdsHistory` ( + `Id`, + `ChannelId`, + `ServerId`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.ChannelId, + OLD.ServerId, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_ServerAFKChannelIdsDelete`; + +CREATE TRIGGER `TR_CFG_ServerAFKChannelIdsDelete` + AFTER DELETE + ON `CFG_ServerAFKChannelIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerAFKChannelIdsHistory` ( + `Id`, + `ChannelId`, + `ServerId`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.ChannelId, + OLD.ServerId, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_team_roles.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_team_roles.sql new file mode 100644 index 00000000..8733da4d --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/server_team_roles.sql @@ -0,0 +1,62 @@ +CREATE TABLE IF NOT EXISTS `CFG_ServerTeamRoleIdsHistory` +( + `Id` BIGINT(20) NOT NULL, + `RoleId` BIGINT NOT NULL, + `TeamMemberType` ENUM('Moderator', 'Admin') NOT NULL, + `ServerId` BIGINT NOT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_ServerTeamRoleIdsUpdate`; + +CREATE TRIGGER `TR_CFG_ServerTeamRoleIdsUpdate` + AFTER UPDATE + ON `CFG_ServerTeamRoleIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerTeamRoleIdsHistory` ( + `Id`, + `RoleId`, + `TeamMemberType`, + `ServerId`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.RoleId, + OLD.TeamMemberType, + OLD.ServerId, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_ServerTeamRoleIdsDelete`; + +CREATE TRIGGER `TR_CFG_ServerTeamRoleIdsDelete` + AFTER DELETE + ON `CFG_ServerTeamRoleIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_ServerTeamRoleIdsHistory` ( + `Id`, + `RoleId`, + `TeamMemberType`, + `ServerId`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.RoleId, + OLD.TeamMemberType, + OLD.ServerId, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician.sql new file mode 100644 index 00000000..50907c2f --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician.sql @@ -0,0 +1,67 @@ +CREATE TABLE IF NOT EXISTS `CFG_TechnicianHistory` +( + `Id` BIGINT(20) NOT NULL, + `HelpCommandReferenceUrl` VARCHAR(255) NOT NULL, + `WaitForRestart` BIGINT NOT NULL DEFAULT 8, + `WaitForShutdown` BIGINT NOT NULL DEFAULT 8, + `CacheMaxMessages` BIGINT NOT NULL DEFAULT 1000000, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianUpdate`; + +CREATE TRIGGER `TR_CFG_TechnicianUpdate` + AFTER UPDATE + ON `CFG_Technician` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianHistory` ( + `Id`, + `HelpCommandReferenceUrl`, + `WaitForRestart`, + `WaitForShutdown`, + `CacheMaxMessages`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.HelpCommandReferenceUrl, + OLD.WaitForRestart, + OLD.WaitForShutdown, + OLD.CacheMaxMessages, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianDelete`; + +CREATE TRIGGER `TR_CFG_TechnicianDelete` + AFTER DELETE + ON `CFG_Technician` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianHistory` ( + `Id`, + `HelpCommandReferenceUrl`, + `WaitForRestart`, + `WaitForShutdown`, + `CacheMaxMessages`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.HelpCommandReferenceUrl, + OLD.WaitForRestart, + OLD.WaitForShutdown, + OLD.CacheMaxMessages, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ids.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ids.sql new file mode 100644 index 00000000..251d4b30 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ids.sql @@ -0,0 +1,52 @@ +CREATE TABLE IF NOT EXISTS `CFG_TechnicianIdsHistory` +( + `Id` BIGINT(20) NOT NULL, + `TechnicianId` BIGINT NOT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianIdsUpdate`; + +CREATE TRIGGER `TR_CFG_TechnicianIdsUpdate` + AFTER UPDATE + ON `CFG_TechnicianIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianIdsHistory` ( + `Id`, + `TechnicianId`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.TechnicianId, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianIdsDelete`; + +CREATE TRIGGER `TR_CFG_TechnicianIdsDelete` + AFTER DELETE + ON `CFG_TechnicianIds` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianIdsHistory` ( + `Id`, + `TechnicianId`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.TechnicianId, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ping_urls.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ping_urls.sql new file mode 100644 index 00000000..d61c3cce --- /dev/null +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/config/technician_ping_urls.sql @@ -0,0 +1,52 @@ +CREATE TABLE IF NOT EXISTS `CFG_TechnicianPingUrlsHistory` +( + `Id` BIGINT(20) NOT NULL, + `URL` VARCHAR(255) NOT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL +); + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianPingUrlsUpdate`; + +CREATE TRIGGER `TR_CFG_TechnicianPingUrlsUpdate` + AFTER UPDATE + ON `CFG_TechnicianPingUrls` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianPingUrlsHistory` ( + `Id`, + `URL`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.URL, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; + +DROP TRIGGER IF EXISTS `TR_CFG_TechnicianPingUrlsDelete`; + +CREATE TRIGGER `TR_CFG_TechnicianPingUrlsDelete` + AFTER DELETE + ON `CFG_TechnicianPingUrls` + FOR EACH ROW +BEGIN + INSERT INTO `CFG_TechnicianPingUrlsHistory` ( + `Id`, + `URL`, + `Deleted`, + `DateFrom`, + `DateTo` + ) + VALUES ( + OLD.Id, + OLD.URL, + TRUE, + OLD.LastModifiedAt, + CURRENT_TIMESTAMP(6) + ); +END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/model/__init__.py b/kdb-bot/src/bot_data/model/__init__.py index 7ab3430e..b266d942 100644 --- a/kdb-bot/src/bot_data/model/__init__.py +++ b/kdb-bot/src/bot_data/model/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_data.model" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_data/model/achievement.py b/kdb-bot/src/bot_data/model/achievement.py new file mode 100644 index 00000000..ebce645d --- /dev/null +++ b/kdb-bot/src/bot_data/model/achievement.py @@ -0,0 +1,158 @@ +from datetime import datetime +from typing import Optional + +from cpl_core.database import TableABC +from cpl_core.dependency_injection import ServiceProviderABC + +from bot_data.model.server import Server + + +class Achievement(TableABC): + def __init__( + self, + name: str, + description: str, + attribute: str, + operator: str, + value: str, + server: Optional[Server], + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._name = name + self._description = description + self._attribute = attribute + + if self._is_operator_valid(operator): + raise ValueError("Operator invalid") + + self._operator = operator + self._value = value + self._server = server + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @ServiceProviderABC.inject + def _is_operator_valid(self, operator, service: ServiceProviderABC) -> bool: + from modules.achievements.achievement_service import AchievementService + + achievements: AchievementService = service.get_service(AchievementService) + return operator not in achievements.get_operators() + + @property + def id(self) -> int: + return self._id + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, value: str): + self._name = value + + @property + def description(self) -> str: + return self._description + + @description.setter + def description(self, value: str): + self._description = value + + @property + def attribute(self) -> str: + return self._attribute + + @attribute.setter + def attribute(self, value: str): + self._attribute = value + + @property + def operator(self) -> str: + return self._operator + + @operator.setter + def operator(self, value: str): + self._operator = value + + @property + def value(self) -> str: + return self._value + + @value.setter + def value(self, value: str): + self._value = value + + @property + def server(self) -> Server: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `Achievements`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `Achievements` + WHERE `Id` = {id}; + """ + ) + + @staticmethod + def get_select_by_server_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `Achievements` + WHERE `ServerId` = {id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `Achievements` ( + `Name`, `Description`, `Attribute`, `Operator`, `Value`, `ServerId` + ) VALUES ( + '{self._name}', + '{self._description}', + '{self._attribute}', + '{self._operator}', + '{self._value}', + {self._server.id} + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `Achievements` + SET `Name` = '{self._name}', + `Description` = '{self._description}', + `Attribute` = '{self._attribute}', + `Operator` = '{self._operator}', + `Value` = '{self._value}' + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `Achievements` + WHERE `Id` = {self._id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/achievement_history.py b/kdb-bot/src/bot_data/model/achievement_history.py new file mode 100644 index 00000000..ae8a9230 --- /dev/null +++ b/kdb-bot/src/bot_data/model/achievement_history.py @@ -0,0 +1,58 @@ +from bot_data.abc.history_table_abc import HistoryTableABC + + +class AchievementHistory(HistoryTableABC): + def __init__( + self, + name: str, + description: str, + attribute: str, + operator: str, + value: str, + server: int, + deleted: bool, + date_from: str, + date_to: str, + id=0, + ): + HistoryTableABC.__init__(self) + + self._id = id + self._name = name + self._description = description + self._attribute = attribute + self._operator = operator + self._value = value + self._server = server + + self._deleted = deleted + self._date_from = date_from + self._date_to = date_to + + @property + def id(self) -> int: + return self._id + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @property + def attribute(self) -> str: + return self._attribute + + @property + def operator(self) -> str: + return self._operator + + @property + def value(self) -> str: + return self._value + + @property + def server(self) -> int: + return self._server diff --git a/kdb-bot/src/bot_data/model/server_afk_channel_ids_config.py b/kdb-bot/src/bot_data/model/server_afk_channel_ids_config.py new file mode 100644 index 00000000..9b182426 --- /dev/null +++ b/kdb-bot/src/bot_data/model/server_afk_channel_ids_config.py @@ -0,0 +1,85 @@ +from datetime import datetime + +from cpl_core.database import TableABC + + +class ServerAFKChannelIdsConfig(TableABC): + def __init__( + self, + channel_id: int, + server_id: int, + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._channel_id = channel_id + self._server_id = server_id + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def channel_id(self) -> int: + return self._channel_id + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_ServerAFKChannelIds`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_ServerAFKChannelIds` + WHERE `Id` = {id}; + """ + ) + + @staticmethod + def get_select_by_server_id_string(server_id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_ServerAFKChannelIds` + WHERE `ServerId` = {server_id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_ServerAFKChannelIds` ( + `ChannelId`, + `ServerId` + ) VALUES ( + {self._channel_id}, + {self._server_id} + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_ServerAFKChannelIds` + SET `ChannelId` = {self._channel_id}, + `ServerId` = {self._server_id} + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_ServerAFKChannelIds` + WHERE `ChannelId` = {self._channel_id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/server_config.py b/kdb-bot/src/bot_data/model/server_config.py new file mode 100644 index 00000000..64c666d1 --- /dev/null +++ b/kdb-bot/src/bot_data/model/server_config.py @@ -0,0 +1,293 @@ +import json +from datetime import datetime + +from cpl_core.configuration import ConfigurationModelABC +from cpl_core.database import TableABC +from cpl_query.extension import List + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_data.model.server import Server +from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig + + +class ServerConfig(TableABC, ConfigurationModelABC): + def __init__( + self, + message_delete_timer: int, + notification_chat_id: int, + max_voice_state_hours: int, + xp_per_message: int, + xp_per_reaction: int, + max_message_xp_per_hour: int, + xp_per_ontime_hour: int, + xp_per_event_participation: int, + xp_per_achievement: int, + afk_command_channel_id: int, + help_voice_channel_id: int, + team_channel_id: int, + login_message_channel_id: int, + feature_flags: dict[FeatureFlagsEnum], + server: Server, + afk_channel_ids: List[int], + team_role_ids: List[ServerTeamRoleIdsConfig], + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._message_delete_timer = message_delete_timer + self._notification_chat_id = notification_chat_id + self._max_voice_state_hours = max_voice_state_hours + self._xp_per_message = xp_per_message + self._xp_per_reaction = xp_per_reaction + self._max_message_xp_per_hour = max_message_xp_per_hour + self._xp_per_ontime_hour = xp_per_ontime_hour + self._xp_per_event_participation = xp_per_event_participation + self._xp_per_achievement = xp_per_achievement + self._afk_command_channel_id = afk_command_channel_id + self._help_voice_channel_id = help_voice_channel_id + self._team_channel_id = team_channel_id + self._login_message_channel_id = login_message_channel_id + self._feature_flags = feature_flags + self._server = server + self._afk_channel_ids = afk_channel_ids + self._team_role_ids = team_role_ids + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def message_delete_timer(self) -> int: + return self._message_delete_timer + + @message_delete_timer.setter + def message_delete_timer(self, value: int): + self._message_delete_timer = value + + @property + def notification_chat_id(self) -> int: + return self._notification_chat_id + + @notification_chat_id.setter + def notification_chat_id(self, value: int): + self._notification_chat_id = value + + @property + def max_voice_state_hours(self) -> int: + return self._max_voice_state_hours + + @max_voice_state_hours.setter + def max_voice_state_hours(self, value: int): + self._max_voice_state_hours = value + + @property + def xp_per_message(self) -> int: + return self._xp_per_message + + @xp_per_message.setter + def xp_per_message(self, value: int): + self._xp_per_message = value + + @property + def xp_per_reaction(self) -> int: + return self._xp_per_reaction + + @xp_per_reaction.setter + def xp_per_reaction(self, value: int): + self._xp_per_reaction = value + + @property + def max_message_xp_per_hour(self) -> int: + return self._max_message_xp_per_hour + + @max_message_xp_per_hour.setter + def max_message_xp_per_hour(self, value: int): + self._max_message_xp_per_hour = value + + @property + def xp_per_ontime_hour(self) -> int: + return self._xp_per_ontime_hour + + @xp_per_ontime_hour.setter + def xp_per_ontime_hour(self, value: int): + self._xp_per_ontime_hour = value + + @property + def xp_per_event_participation(self) -> int: + return self._xp_per_event_participation + + @xp_per_event_participation.setter + def xp_per_event_participation(self, value: int): + self._xp_per_event_participation = value + + @property + def xp_per_achievement(self) -> int: + return self._xp_per_achievement + + @xp_per_achievement.setter + def xp_per_achievement(self, value: int): + self._xp_per_achievement = value + + @property + def afk_command_channel_id(self) -> int: + return self._afk_command_channel_id + + @afk_command_channel_id.setter + def afk_command_channel_id(self, value: int): + self._afk_command_channel_id = value + + @property + def help_voice_channel_id(self) -> int: + return self._help_voice_channel_id + + @help_voice_channel_id.setter + def help_voice_channel_id(self, value: int): + self._help_voice_channel_id = value + + @property + def team_channel_id(self) -> int: + return self._team_channel_id + + @team_channel_id.setter + def team_channel_id(self, value: int): + self._team_channel_id = value + + @property + def login_message_channel_id(self) -> int: + return self._login_message_channel_id + + @login_message_channel_id.setter + def login_message_channel_id(self, value: int): + self._login_message_channel_id = value + + @property + def feature_flags(self) -> dict[FeatureFlagsEnum]: + return self._feature_flags + + @feature_flags.setter + def feature_flags(self, value: dict[FeatureFlagsEnum]): + self._feature_flags = value + + @property + def afk_channel_ids(self) -> List[int]: + return self._afk_channel_ids + + @afk_channel_ids.setter + def afk_channel_ids(self, value: List[int]): + self._afk_channel_ids = value + + @property + def team_role_ids(self) -> List[ServerTeamRoleIdsConfig]: + return self._team_role_ids + + @team_role_ids.setter + def team_role_ids(self, value: List[ServerTeamRoleIdsConfig]): + self._team_role_ids = value + + @property + def server(self) -> Server: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_Server`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_Server` + WHERE `Id` = {id}; + """ + ) + + @staticmethod + def get_select_by_server_id_string(server_id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_Server` + WHERE `ServerId` = {server_id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_Server` ( + `MessageDeleteTimer`, + `NotificationChatId`, + `MaxVoiceStateHours`, + `XpPerMessage`, + `XpPerReaction`, + `MaxMessageXpPerHour`, + `XpPerOntimeHour`, + `XpPerEventParticipation`, + `XpPerAchievement`, + `AFKCommandChannelId`, + `HelpVoiceChannelId`, + `TeamChannelId`, + `LoginMessageChannelId`, + `FeatureFlags`, + `ServerId` + ) VALUES ( + {self._message_delete_timer}, + {self._notification_chat_id}, + {self._max_voice_state_hours}, + {self._xp_per_message}, + {self._xp_per_reaction}, + {self._max_message_xp_per_hour}, + {self._xp_per_ontime_hour}, + {self._xp_per_event_participation}, + {self._xp_per_achievement}, + {self._afk_command_channel_id}, + {self._help_voice_channel_id}, + {self._team_channel_id}, + {self._login_message_channel_id}, + '{json.dumps(self._feature_flags)}', + {self._server.id} + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_Server` + SET `MessageDeleteTimer` = {self._message_delete_timer}, + `NotificationChatId` = {self._notification_chat_id}, + `MaxVoiceStateHours` = {self._max_voice_state_hours}, + `XpPerMessage` = {self._xp_per_message}, + `XpPerReaction` = {self._xp_per_reaction}, + `MaxMessageXpPerHour` = {self._max_message_xp_per_hour}, + `XpPerOntimeHour` = {self._xp_per_ontime_hour}, + `XpPerEventParticipation` = {self._xp_per_event_participation}, + `XpPerAchievement` = {self._xp_per_achievement}, + `AFKCommandChannelId` = {self._afk_command_channel_id}, + `HelpVoiceChannelId` = {self._help_voice_channel_id}, + `TeamChannelId` = {self._team_channel_id}, + `LoginMessageChannelId` = {self._login_message_channel_id}, + `FeatureFlags` = '{json.dumps(self._feature_flags)}', + `ServerId` = {self._server.id} + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_Server` + WHERE `Id` = {self._id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/server_config_history.py b/kdb-bot/src/bot_data/model/server_config_history.py new file mode 100644 index 00000000..a816e069 --- /dev/null +++ b/kdb-bot/src/bot_data/model/server_config_history.py @@ -0,0 +1,108 @@ +from bot_data.abc.history_table_abc import HistoryTableABC + + +class ServerConfigHistory(HistoryTableABC): + def __init__( + self, + message_delete_timer: int, + notification_chat_id: int, + max_voice_state_hours: int, + xp_per_message: int, + xp_per_reaction: int, + max_message_xp_per_hour: int, + xp_per_ontime_hour: int, + xp_per_event_participation: int, + xp_per_achievement: int, + afk_command_channel_id: int, + help_voice_channel_id: int, + team_channel_id: int, + login_message_channel_id: int, + feature_flags: dict[str], + server_id: int, + deleted: bool, + date_from: str, + date_to: str, + id=0, + ): + HistoryTableABC.__init__(self) + + self._id = id + self._message_delete_timer = message_delete_timer + self._notification_chat_id = notification_chat_id + self._max_voice_state_hours = max_voice_state_hours + self._xp_per_message = xp_per_message + self._xp_per_reaction = xp_per_reaction + self._max_message_xp_per_hour = max_message_xp_per_hour + self._xp_per_ontime_hour = xp_per_ontime_hour + self._xp_per_event_participation = xp_per_event_participation + self._xp_per_achievement = xp_per_achievement + self._afk_command_channel_id = afk_command_channel_id + self._help_voice_channel_id = help_voice_channel_id + self._team_channel_id = team_channel_id + self._login_message_channel_id = login_message_channel_id + self._feature_flags = feature_flags + self._server_id = server_id + + self._deleted = deleted + self._date_from = date_from + self._date_to = date_to + + @property + def message_delete_timer(self) -> int: + return self._message_delete_timer + + @property + def notification_chat_id(self) -> int: + return self._notification_chat_id + + @property + def max_voice_state_hours(self) -> int: + return self._max_voice_state_hours + + @property + def xp_per_message(self) -> int: + return self._xp_per_message + + @property + def xp_per_reaction(self) -> int: + return self._xp_per_reaction + + @property + def max_message_xp_per_hour(self) -> int: + return self._max_message_xp_per_hour + + @property + def xp_per_ontime_hour(self) -> int: + return self._xp_per_ontime_hour + + @property + def xp_per_event_participation(self) -> int: + return self._xp_per_event_participation + + @property + def xp_per_achievement(self) -> int: + return self._xp_per_achievement + + @property + def afk_command_channel_id(self) -> int: + return self._afk_command_channel_id + + @property + def help_voice_channel_id(self) -> int: + return self._help_voice_channel_id + + @property + def team_channel_id(self) -> int: + return self._team_channel_id + + @property + def login_message_channel_id(self) -> int: + return self._login_message_channel_id + + @property + def feature_flags(self) -> dict[str]: + return self._feature_flags + + @property + def server_id(self) -> int: + return self._server_id diff --git a/kdb-bot/src/bot_data/model/server_team_role_ids_config.py b/kdb-bot/src/bot_data/model/server_team_role_ids_config.py new file mode 100644 index 00000000..dfd870dd --- /dev/null +++ b/kdb-bot/src/bot_data/model/server_team_role_ids_config.py @@ -0,0 +1,100 @@ +from datetime import datetime + +from cpl_core.database import TableABC + +from bot_data.model.team_member_type_enum import TeamMemberTypeEnum + + +class ServerTeamRoleIdsConfig(TableABC): + def __init__( + self, + role_id: int, + team_member_type: TeamMemberTypeEnum, + server_id: int, + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._role_id = role_id + self._team_member_type = team_member_type + self._server_id = server_id + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def role_id(self) -> int: + return self._role_id + + @property + def team_member_type(self) -> TeamMemberTypeEnum: + return self._team_member_type + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_ServerTeamRoleIds`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_ServerTeamRoleIds` + WHERE `Id` = {id}; + """ + ) + + @staticmethod + def get_select_by_server_id_string(server_id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_ServerTeamRoleIds` + WHERE `ServerId` = {server_id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_ServerTeamRoleIds` ( + `RoleId`, + `TeamMemberType`, + `ServerId` + ) VALUES ( + {self._role_id}, + '{self._team_member_type.value}', + {self._server_id} + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_ServerTeamRoleIds` + SET `RoleId` = {self._role_id}, + `TeamMemberType` = '{self._team_member_type.value}', + `ServerId` = {self._server_id} + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_ServerTeamRoleIds` + WHERE `RoleId` = {self._role_id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/team_member_type_enum.py b/kdb-bot/src/bot_data/model/team_member_type_enum.py new file mode 100644 index 00000000..86b3b41d --- /dev/null +++ b/kdb-bot/src/bot_data/model/team_member_type_enum.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class TeamMemberTypeEnum(Enum): + moderator = "Moderator" + admin = "Admin" diff --git a/kdb-bot/src/bot_data/model/technician_config.py b/kdb-bot/src/bot_data/model/technician_config.py new file mode 100644 index 00000000..1390f4b8 --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_config.py @@ -0,0 +1,152 @@ +import json +from datetime import datetime + +from cpl_core.configuration import ConfigurationModelABC +from cpl_core.database import TableABC +from cpl_query.extension import List + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum + + +class TechnicianConfig(TableABC, ConfigurationModelABC): + def __init__( + self, + help_command_reference_url: str, + wait_for_restart: int, + wait_for_shutdown: int, + cache_max_messages: int, + feature_flags: dict[FeatureFlagsEnum], + technician_ids: List[int], + ping_urls: List[str], + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._help_command_reference_url = help_command_reference_url + self._wait_for_restart = wait_for_restart + self._wait_for_shutdown = wait_for_shutdown + self._cache_max_messages = cache_max_messages + self._feature_flags = feature_flags + self._technician_ids = technician_ids + self._ping_urls = ping_urls + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def help_command_reference_url(self) -> str: + return self._help_command_reference_url + + @help_command_reference_url.setter + def help_command_reference_url(self, value: str): + self._help_command_reference_url = value + + @property + def wait_for_restart(self) -> int: + return self._wait_for_restart + + @wait_for_restart.setter + def wait_for_restart(self, value: int): + self._wait_for_restart = value + + @property + def wait_for_shutdown(self) -> int: + return self._wait_for_shutdown + + @wait_for_shutdown.setter + def wait_for_shutdown(self, value: int): + self._wait_for_shutdown = value + + @property + def cache_max_messages(self) -> int: + return self._cache_max_messages + + @cache_max_messages.setter + def cache_max_messages(self, value: int): + self._cache_max_messages = value + + @property + def feature_flags(self) -> dict[FeatureFlagsEnum]: + return self._feature_flags + + @feature_flags.setter + def feature_flags(self, value: dict[FeatureFlagsEnum]): + self._feature_flags = value + + @property + def technician_ids(self) -> List[int]: + return self._technician_ids + + @technician_ids.setter + def technician_ids(self, value: List[int]): + self._technician_ids = value + + @property + def ping_urls(self) -> List[str]: + return self._ping_urls + + @ping_urls.setter + def ping_urls(self, value: List[str]): + self._ping_urls = value + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_Technician`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_Technician` + WHERE `Id` = {id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_Technician` ( + `HelpCommandReferenceUrl`, `WaitForRestart`, `WaitForShutdown`, `CacheMaxMessages`, `FeatureFlags` + ) VALUES ( + '{self._help_command_reference_url}', + {self._wait_for_restart}, + {self._wait_for_shutdown}, + {self._cache_max_messages}, + '{json.dumps(self._feature_flags)}' + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_Technician` + SET `HelpCommandReferenceUrl` = '{self._help_command_reference_url}', + `WaitForRestart` = {self._wait_for_restart}, + `WaitForShutdown` = {self._wait_for_shutdown}, + `CacheMaxMessages` = {self._cache_max_messages}, + `FeatureFlags` = '{json.dumps(self._feature_flags)}' + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_Technician` + WHERE `Id` = {self._id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/technician_config_history.py b/kdb-bot/src/bot_data/model/technician_config_history.py new file mode 100644 index 00000000..7575607f --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_config_history.py @@ -0,0 +1,58 @@ +from bot_data.abc.history_table_abc import HistoryTableABC + + +class TechnicianConfigHistory(HistoryTableABC): + def __init__( + self, + help_command_reference_url: str, + wait_for_restart: int, + wait_for_shutdown: int, + cache_max_messages: int, + deleted: bool, + date_from: str, + date_to: str, + id=0, + ): + HistoryTableABC.__init__(self) + + self._id = id + self._help_command_reference_url = help_command_reference_url + self._wait_for_restart = wait_for_restart + self._wait_for_shutdown = wait_for_shutdown + self._cache_max_messages = cache_max_messages + + self._deleted = deleted + self._date_from = date_from + self._date_to = date_to + + @property + def help_command_reference_url(self) -> str: + return self._help_command_reference_url + + @help_command_reference_url.setter + def help_command_reference_url(self, value: str): + self._help_command_reference_url = value + + @property + def wait_for_restart(self) -> int: + return self._wait_for_restart + + @wait_for_restart.setter + def wait_for_restart(self, value: int): + self._wait_for_restart = value + + @property + def wait_for_shutdown(self) -> int: + return self._wait_for_shutdown + + @wait_for_shutdown.setter + def wait_for_shutdown(self, value: int): + self._wait_for_shutdown = value + + @property + def cache_max_messages(self) -> int: + return self._cache_max_messages + + @cache_max_messages.setter + def cache_max_messages(self, value: int): + self._cache_max_messages = value diff --git a/kdb-bot/src/bot_data/model/technician_id_config.py b/kdb-bot/src/bot_data/model/technician_id_config.py new file mode 100644 index 00000000..6ba5f581 --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_id_config.py @@ -0,0 +1,79 @@ +from datetime import datetime + +from cpl_core.database import TableABC + + +class TechnicianIdConfig(TableABC): + def __init__( + self, + technician_id: str, + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._technician_id = technician_id + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def technician_id(self) -> str: + return self._technician_id + + @technician_id.setter + def technician_id(self, value: str): + self._technician_id = value + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_TechnicianIds`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_TechnicianIds` + WHERE `Id` = {id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_TechnicianIds` ( + `TechnicianId` + ) VALUES ( + '{self._technician_id}' + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_TechnicianIds` + SET `TechnicianId` = '{self._technician_id}' + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_TechnicianIds` + WHERE `TechnicianId` = {self._technician_id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/technician_id_config_history.py b/kdb-bot/src/bot_data/model/technician_id_config_history.py new file mode 100644 index 00000000..dbb6a1f9 --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_id_config_history.py @@ -0,0 +1,28 @@ +from bot_data.abc.history_table_abc import HistoryTableABC + + +class TechnicianIdConfigHistory(HistoryTableABC): + def __init__( + self, + technician_id: int, + deleted: bool, + date_from: str, + date_to: str, + id=0, + ): + HistoryTableABC.__init__(self) + + self._id = id + self._technician_id = technician_id + + self._deleted = deleted + self._date_from = date_from + self._date_to = date_to + + @property + def technician_id(self) -> int: + return self._technician_id + + @technician_id.setter + def technician_id(self, value: int): + self._technician_id = value diff --git a/kdb-bot/src/bot_data/model/technician_ping_url_config.py b/kdb-bot/src/bot_data/model/technician_ping_url_config.py new file mode 100644 index 00000000..31983624 --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_ping_url_config.py @@ -0,0 +1,79 @@ +from datetime import datetime + +from cpl_core.database import TableABC + + +class TechnicianPingUrlConfig(TableABC): + def __init__( + self, + ping_url: str, + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._ping_url = ping_url + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def ping_url(self) -> str: + return self._ping_url + + @ping_url.setter + def ping_url(self, value: str): + self._ping_url = value + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `CFG_TechnicianPingUrls`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `CFG_TechnicianPingUrls` + WHERE `Id` = {id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `CFG_TechnicianPingUrls` ( + `URL` + ) VALUES ( + '{self._ping_url}' + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `CFG_TechnicianPingUrls` + SET `URL` = '{self._ping_url}' + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `CFG_TechnicianPingUrls` + WHERE `URL` = '{self._ping_url}'; + """ + ) diff --git a/kdb-bot/src/bot_data/model/technician_ping_url_config_history.py b/kdb-bot/src/bot_data/model/technician_ping_url_config_history.py new file mode 100644 index 00000000..9483f981 --- /dev/null +++ b/kdb-bot/src/bot_data/model/technician_ping_url_config_history.py @@ -0,0 +1,28 @@ +from bot_data.abc.history_table_abc import HistoryTableABC + + +class TechnicianPingUrlConfigHistory(HistoryTableABC): + def __init__( + self, + url: str, + deleted: bool, + date_from: str, + date_to: str, + id=0, + ): + HistoryTableABC.__init__(self) + + self._id = id + self._url = url + + self._deleted = deleted + self._date_from = date_from + self._date_to = date_to + + @property + def url(self) -> str: + return self._url + + @url.setter + def url(self, value: str): + self._url = value diff --git a/kdb-bot/src/bot_data/model/user.py b/kdb-bot/src/bot_data/model/user.py index 7472e689..57e1cc67 100644 --- a/kdb-bot/src/bot_data/model/user.py +++ b/kdb-bot/src/bot_data/model/user.py @@ -15,6 +15,8 @@ class User(TableABC): self, dc_id: int, xp: int, + reaction_count: int, + message_count: int, server: Optional[Server], created_at: datetime = None, modified_at: datetime = None, @@ -23,6 +25,8 @@ class User(TableABC): self._user_id = id self._discord_id = dc_id self._xp = xp + self._reaction_count = reaction_count + self._message_count = message_count self._server = server TableABC.__init__(self) @@ -59,6 +63,22 @@ class User(TableABC): def xp(self, value: int): self._xp = value + @property + def message_count(self) -> int: + return self._message_count + + @message_count.setter + def message_count(self, value: int): + self._message_count = value + + @property + def reaction_count(self) -> int: + return self._reaction_count + + @reaction_count.setter + def reaction_count(self, value: int): + self._reaction_count = value + @property @ServiceProviderABC.inject def ontime(self, services: ServiceProviderABC) -> float: @@ -151,10 +171,12 @@ class User(TableABC): return str( f""" INSERT INTO `Users` ( - `DiscordId`, `XP`, `ServerId` + `DiscordId`, `XP`, `MessageCount`, `ReactionCount`, `ServerId` ) VALUES ( {self._discord_id}, {self._xp}, + {self._message_count}, + {self._reaction_count}, {self._server.id} ); """ @@ -165,7 +187,9 @@ class User(TableABC): return str( f""" UPDATE `Users` - SET `XP` = {self._xp} + SET `XP` = {self._xp}, + `MessageCount` = {self._message_count}, + `ReactionCount` = {self._reaction_count} WHERE `UserId` = {self._user_id}; """ ) diff --git a/kdb-bot/src/bot_data/model/user_got_achievement.py b/kdb-bot/src/bot_data/model/user_got_achievement.py new file mode 100644 index 00000000..d2c37cf5 --- /dev/null +++ b/kdb-bot/src/bot_data/model/user_got_achievement.py @@ -0,0 +1,105 @@ +from datetime import datetime +from typing import Optional + +from cpl_core.database import TableABC + +from bot_data.model.achievement import Achievement +from bot_data.model.user import User + + +class UserGotAchievement(TableABC): + def __init__( + self, + user: Optional[User], + achievement: Optional[Achievement], + created_at: datetime = None, + modified_at: datetime = None, + id=0, + ): + self._id = id + self._user = user + self._achievement = achievement + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def user(self) -> User: + return self._user + + @property + def achievement(self) -> Achievement: + return self._achievement + + @staticmethod + def get_select_all_string() -> str: + return str( + f""" + SELECT * FROM `UserGotAchievements`; + """ + ) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `UserGotAchievements` + WHERE `Id` = {id}; + """ + ) + + @staticmethod + def get_select_by_user_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `UserGotAchievements` + WHERE `UserId` = {id}; + """ + ) + + @staticmethod + def get_select_by_achievement_id_string(id: int) -> str: + return str( + f""" + SELECT * FROM `UserGotAchievements` + WHERE `AchievementId` = {id}; + """ + ) + + @property + def insert_string(self) -> str: + return str( + f""" + INSERT INTO `UserGotAchievements` ( + `UserId`, `AchievementId` + ) VALUES ( + {self._user.id}, + {self._achievement.id} + ); + """ + ) + + @property + def udpate_string(self) -> str: + return str( + f""" + UPDATE `UserGotAchievements` + SET `UserId` = '{self._user.id}', + `AchievementId` = '{self._achievement.id}' + WHERE `Id` = {self._id}; + """ + ) + + @property + def delete_string(self) -> str: + return str( + f""" + DELETE FROM `UserGotAchievements` + WHERE `Id` = {self._id}; + """ + ) diff --git a/kdb-bot/src/bot_data/model/user_history.py b/kdb-bot/src/bot_data/model/user_history.py index d3b88ca7..2bf39954 100644 --- a/kdb-bot/src/bot_data/model/user_history.py +++ b/kdb-bot/src/bot_data/model/user_history.py @@ -9,6 +9,8 @@ class UserHistory(HistoryTableABC): self, dc_id: int, xp: int, + message_count: int, + reaction_count: int, server: int, deleted: bool, date_from: str, @@ -20,6 +22,8 @@ class UserHistory(HistoryTableABC): self._user_id = id self._discord_id = dc_id self._xp = xp + self._message_count = message_count + self._reaction_count = reaction_count self._server = server self._deleted = deleted @@ -38,6 +42,14 @@ class UserHistory(HistoryTableABC): def xp(self) -> int: return self._xp + @property + def message_count(self) -> int: + return self._message_count + + @property + def reaction_count(self) -> int: + return self._reaction_count + @property def server(self) -> int: return self._server diff --git a/kdb-bot/src/bot_data/service/__init__.py b/kdb-bot/src/bot_data/service/__init__.py index f26bd1e5..3f35d784 100644 --- a/kdb-bot/src/bot_data/service/__init__.py +++ b/kdb-bot/src/bot_data/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_data.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_data/service/achievements_repository_service.py b/kdb-bot/src/bot_data/service/achievements_repository_service.py new file mode 100644 index 00000000..b848fb5a --- /dev/null +++ b/kdb-bot/src/bot_data/service/achievements_repository_service.py @@ -0,0 +1,123 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.achievement import Achievement +from bot_data.model.user_got_achievement import UserGotAchievement + + +class AchievementRepositoryService(AchievementRepositoryABC): + def __init__( + self, + logger: DatabaseLogger, + db_context: DatabaseContextABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + ): + self._logger = logger + self._context = db_context + + self._servers = servers + self._users = users + + AchievementRepositoryABC.__init__(self) + + def _from_result(self, result: tuple): + return Achievement( + result[1], + result[2], + result[3], + result[4], + result[5], + self._servers.get_server_by_id(result[6]), + result[7], + result[8], + id=result[0], + ) + + def _join_from_result(self, result: tuple): + return UserGotAchievement( + self._users.get_user_by_id(result[1]), + self.get_achievement_by_id(result[2]), + result[3], + result[4], + id=result[0], + ) + + def get_achievements(self) -> List[Achievement]: + achievements = List(Achievement) + self._logger.trace(__name__, f"Send SQL command: {Achievement.get_select_all_string()}") + results = self._context.select(Achievement.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f"Get user with id {result[0]}") + achievements.append(self._from_result(result)) + + return achievements + + def get_achievement_by_id(self, id: int) -> Achievement: + self._logger.trace(__name__, f"Send SQL command: {Achievement.get_select_by_id_string(id)}") + result = self._context.select(Achievement.get_select_by_id_string(id))[0] + + return self._from_result(result) + + def get_achievements_by_server_id(self, server_id: int) -> List[Achievement]: + achievements = List(Achievement) + self._logger.trace(__name__, f"Send SQL command: {Achievement.get_select_by_server_id_string(server_id)}") + results = self._context.select(Achievement.get_select_by_server_id_string(server_id)) + for result in results: + self._logger.trace(__name__, f"Get user with id {result[0]}") + achievements.append(self._from_result(result)) + + return achievements + + def get_achievements_by_user_id(self, user_id: int) -> List[Achievement]: + achievements = List(Achievement) + achievements_joins = List(UserGotAchievement) + self._logger.trace(__name__, f"Send SQL command: {UserGotAchievement.get_select_by_user_id_string(user_id)}") + results = self._context.select(UserGotAchievement.get_select_by_user_id_string(user_id)) + for result in results: + self._logger.trace(__name__, f"Got UserGotAchievement with id {result[0]}") + achievements_joins.append(self._join_from_result(result)) + + for achievements_join in achievements_joins: + results = self._context.select(Achievement.get_select_by_id_string(achievements_join.achievement.id)) + for result in results: + self._logger.trace(__name__, f"Got Achievement with id {result[0]}") + achievements.append(self._from_result(result)) + + return achievements + + def get_user_got_achievements_by_achievement_id(self, achievement_id: int) -> List[Achievement]: + achievements_joins = List(UserGotAchievement) + self._logger.trace( + __name__, f"Send SQL command: {UserGotAchievement.get_select_by_achievement_id_string(achievement_id)}" + ) + results = self._context.select(UserGotAchievement.get_select_by_achievement_id_string(achievement_id)) + for result in results: + self._logger.trace(__name__, f"Got UserGotAchievement with id {result[0]}") + achievements_joins.append(self._join_from_result(result)) + + return achievements_joins + + def add_achievement(self, achievement: Achievement): + self._logger.trace(__name__, f"Send SQL command: {achievement.insert_string}") + self._context.cursor.execute(achievement.insert_string) + + def update_achievement(self, achievement: Achievement): + self._logger.trace(__name__, f"Send SQL command: {achievement.udpate_string}") + self._context.cursor.execute(achievement.udpate_string) + + def delete_achievement(self, achievement: Achievement): + self._logger.trace(__name__, f"Send SQL command: {achievement.delete_string}") + self._context.cursor.execute(achievement.delete_string) + + def add_user_got_achievement(self, join: UserGotAchievement): + self._logger.trace(__name__, f"Send SQL command: {join.insert_string}") + self._context.cursor.execute(join.insert_string) + + def delete_user_got_achievement(self, join: UserGotAchievement): + self._logger.trace(__name__, f"Send SQL command: {join.delete_string}") + self._context.cursor.execute(join.delete_string) diff --git a/kdb-bot/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py index 295e3c2f..87b39169 100644 --- a/kdb-bot/src/bot_data/service/auth_user_repository_service.py +++ b/kdb-bot/src/bot_data/service/auth_user_repository_service.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import Optional from cpl_core.database.context import DatabaseContextABC @@ -100,9 +101,17 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): ): crit_sort_direction = criteria.sort_direction.lower() if crit_sort_direction == "desc" or crit_sort_direction == "descending": - query = query.order_by_descending(lambda x: getattr(x, criteria.sort_column)) + query = query.order_by_descending( + lambda x: getattr(x, criteria.sort_column) + if not isinstance(getattr(x, criteria.sort_column), Enum) + else getattr(x, criteria.sort_column).value + ) else: - query = query.order_by(lambda x: getattr(x, criteria.sort_column)) + query = query.order_by( + lambda x: getattr(x, criteria.sort_column) + if not isinstance(getattr(x, criteria.sort_column), Enum) + else getattr(x, criteria.sort_column).value + ) result = FilteredResult() result.total_count = query.count() diff --git a/kdb-bot/src/bot_data/service/cache_service.py b/kdb-bot/src/bot_data/service/cache_service.py index 31dcd73a..50d05e7e 100644 --- a/kdb-bot/src/bot_data/service/cache_service.py +++ b/kdb-bot/src/bot_data/service/cache_service.py @@ -5,12 +5,21 @@ from bot_data.model.server import Server class CacheService: def __init__(self): - self._cached_server = List(Server) + self._cached_servers = List(Server) @property def cached_server(self) -> List[Server]: - return self._cached_server + return self._cached_servers - @cached_server.setter - def cached_server(self, value: List[Server]): - self._cached_server = value + def add_server(self, server: Server): + if self._cached_servers.where(lambda x: x.id == server.id).count() > 0: + return + self._cached_servers.add(server) + + def add_servers(self, servers: List[Server]): + new_ids = servers.select(lambda x: x.id) + for s in self._cached_servers: + if s.id in new_ids: + return + + self._cached_servers.extend(servers) diff --git a/kdb-bot/src/bot_data/service/server_config_repository_service.py b/kdb-bot/src/bot_data/service/server_config_repository_service.py new file mode 100644 index 00000000..33f011ea --- /dev/null +++ b/kdb-bot/src/bot_data/service/server_config_repository_service.py @@ -0,0 +1,128 @@ +import json + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_afk_channel_ids_config import ServerAFKChannelIdsConfig +from bot_data.model.server_config import ServerConfig +from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig +from bot_data.model.team_member_type_enum import TeamMemberTypeEnum + + +class ServerConfigRepositoryService(ServerConfigRepositoryABC): + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC): + ServerConfigRepositoryABC.__init__(self) + + self._logger = logger + self._context = db_context + self._servers = servers + + def _get_team_role_ids(self, server_id: int) -> List[ServerTeamRoleIdsConfig]: + ids = List(ServerTeamRoleIdsConfig) + self._logger.trace( + __name__, f"Send SQL command: {ServerTeamRoleIdsConfig.get_select_by_server_id_string(server_id)}" + ) + results = self._context.select(ServerTeamRoleIdsConfig.get_select_by_server_id_string(server_id)) + for result in results: + self._logger.trace(__name__, f"Got ServerTeamRoleIdsConfig with id {result[0]}") + ids.append( + ServerTeamRoleIdsConfig( + result[1], TeamMemberTypeEnum(result[2]), result[3], result[4], result[5], id=result[0] + ) + ) + + return ids + + def _get_afk_channel_ids(self, server_id: int) -> List[int]: + urls = List(int) + self._logger.trace( + __name__, + f"Send SQL command: {ServerAFKChannelIdsConfig.get_select_by_server_id_string(server_id)}", + ) + results = self._context.select(ServerAFKChannelIdsConfig.get_select_by_server_id_string(server_id)) + for result in results: + self._logger.trace(__name__, f"Got ServerAFKChannelIdsConfig with id {result[0]}") + urls.append(result[1]) + + return urls + + def _from_result(self, result: tuple) -> ServerConfig: + return ServerConfig( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + result[7], + result[8], + result[9], + result[10], + result[11], + result[12], + result[13], + json.loads(result[14]), + self._servers.get_server_by_id(result[15]), + self._get_afk_channel_ids(result[15]), + self._get_team_role_ids(result[15]), + result[16], + result[17], + id=result[0], + ) + + def does_server_config_exists(self, server_id: int) -> bool: + self._logger.trace(__name__, f"Send SQL command: {ServerConfig.get_select_by_server_id_string(server_id)}") + result = self._context.select(ServerConfig.get_select_by_server_id_string(server_id)) + + return len(result) > 0 + + def get_server_config_by_server(self, server_id: int) -> ServerConfig: + self._logger.trace(__name__, f"Send SQL command: {ServerConfig.get_select_by_server_id_string(server_id)}") + result = self._context.select(ServerConfig.get_select_by_server_id_string(server_id))[0] + + return self._from_result(result) + + def get_server_config_by_id(self, config_id: int) -> ServerConfig: + self._logger.trace(__name__, f"Send SQL command: {ServerConfig.get_select_by_id_string(config_id)}") + result = self._context.select(ServerConfig.get_select_by_id_string(config_id))[0] + + return self._from_result(result) + + def add_server_config(self, server_config: ServerConfig): + self._logger.trace(__name__, f"Send SQL command: {server_config.insert_string}") + self._context.cursor.execute(server_config.insert_string) + + def update_server_config(self, server_config: ServerConfig): + self._logger.trace(__name__, f"Send SQL command: {server_config.udpate_string}") + self._context.cursor.execute(server_config.udpate_string) + + def delete_server_config(self, server_config: ServerConfig): + self._logger.trace(__name__, f"Send SQL command: {server_config.delete_string}") + self._context.cursor.execute(server_config.delete_string) + + def add_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_team_role_id.insert_string}") + self._context.cursor.execute(server_team_role_id.insert_string) + + def update_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_team_role_id.udpate_string}") + self._context.cursor.execute(server_team_role_id.udpate_string) + + def delete_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_team_role_id.delete_string}") + self._context.cursor.execute(server_team_role_id.delete_string) + + def add_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_afk_channel.insert_string}") + self._context.cursor.execute(server_afk_channel.insert_string) + + def update_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_afk_channel.udpate_string}") + self._context.cursor.execute(server_afk_channel.udpate_string) + + def delete_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig): + self._logger.trace(__name__, f"Send SQL command: {server_afk_channel.delete_string}") + self._context.cursor.execute(server_afk_channel.delete_string) diff --git a/kdb-bot/src/bot_data/service/server_config_seeder.py b/kdb-bot/src/bot_data/service/server_config_seeder.py new file mode 100644 index 00000000..926c09b4 --- /dev/null +++ b/kdb-bot/src/bot_data/service/server_config_seeder.py @@ -0,0 +1,63 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.container import Guild +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.data_seeder_abc import DataSeederABC +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_config import ServerConfig + + +class ServerConfigSeeder(DataSeederABC): + def __init__( + self, + logger: DatabaseLogger, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + server_config: ServerConfigRepositoryABC, + db: DatabaseContextABC, + ): + DataSeederABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._server_config = server_config + self._db = db + + async def seed(self): + try: + for guild in self._bot.guilds: + guild: Guild = guild + server = self._servers.get_server_by_discord_id(guild.id) + if self._server_config.does_server_config_exists(server.id): + continue + + config = ServerConfig( + 6, + guild.system_channel.id, + 6, + 1, + 1, + 20, + 10, + 10, + 10, + guild.system_channel.id, + guild.system_channel.id, + guild.system_channel.id, + guild.system_channel.id, + {}, + server, + [], + [], + ) + + self._server_config.add_server_config(config) + + self._db.save_changes() + self._logger.debug(__name__, "Seeded technician config") + + except Exception as e: + self._logger.error(__name__, f"Seeding technician config failed", e) diff --git a/kdb-bot/src/bot_data/service/server_repository_service.py b/kdb-bot/src/bot_data/service/server_repository_service.py index 2d158fad..924cc036 100644 --- a/kdb-bot/src/bot_data/service/server_repository_service.py +++ b/kdb-bot/src/bot_data/service/server_repository_service.py @@ -26,7 +26,7 @@ class ServerRepositoryService(ServerRepositoryABC): for result in results: servers.append(Server(result[1], result[2], result[3], id=result[0])) - self._cache.cached_server = List(Server, servers) + self._cache.add_servers(servers) return servers def get_filtered_servers(self, criteria: ServerSelectCriteria) -> FilteredResult: @@ -56,13 +56,14 @@ class ServerRepositoryService(ServerRepositoryABC): def get_server_by_id(self, server_id: int) -> Server: cs = self._cache.cached_server.where(lambda x: x.id == server_id).single_or_default() + if cs is not None: return cs self._logger.trace(__name__, f"Send SQL command: {Server.get_select_by_id_string(server_id)}") result = self._context.select(Server.get_select_by_id_string(server_id))[0] server = Server(result[1], result[2], result[3], id=result[0]) - self._cache.cached_server.add(server) + self._cache.add_server(server) return server def get_server_by_discord_id(self, discord_id: int) -> Server: diff --git a/kdb-bot/src/bot_data/service/technician_config_repository_service.py b/kdb-bot/src/bot_data/service/technician_config_repository_service.py new file mode 100644 index 00000000..fe32eded --- /dev/null +++ b/kdb-bot/src/bot_data/service/technician_config_repository_service.py @@ -0,0 +1,100 @@ +import json + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC +from bot_data.model.technician_config import TechnicianConfig +from bot_data.model.technician_id_config import TechnicianIdConfig +from bot_data.model.technician_ping_url_config import TechnicianPingUrlConfig + + +class TechnicianConfigRepositoryService(TechnicianConfigRepositoryABC): + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC): + TechnicianConfigRepositoryABC.__init__(self) + + self._logger = logger + self._context = db_context + + def _get_technician_ids(self) -> List[int]: + ids = List(int) + self._logger.trace(__name__, f"Send SQL command: {TechnicianIdConfig.get_select_all_string()}") + results = self._context.select(TechnicianIdConfig.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f"Got TechnicianId with id {result[0]}") + ids.append(result[1]) + + return ids + + def _get_technician_ping_urls(self) -> List[str]: + urls = List(str) + self._logger.trace(__name__, f"Send SQL command: {TechnicianPingUrlConfig.get_select_all_string()}") + results = self._context.select(TechnicianPingUrlConfig.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f"Got TechnicianPingUrl with id {result[0]}") + urls.append(result[1]) + + return urls + + def _from_result(self, result: tuple) -> TechnicianConfig: + return TechnicianConfig( + result[1], + result[2], + result[3], + result[4], + json.loads(result[5]), + self._get_technician_ids(), + self._get_technician_ping_urls(), + result[6], + result[7], + id=result[0], + ) + + def does_technician_config_exists(self) -> bool: + self._logger.trace(__name__, f"Send SQL command: {TechnicianConfig.get_select_all_string()}") + result = self._context.select(TechnicianConfig.get_select_all_string()) + + return len(result) > 0 + + def get_technician_config(self) -> TechnicianConfig: + self._logger.trace(__name__, f"Send SQL command: {TechnicianConfig.get_select_all_string()}") + result = self._context.select(TechnicianConfig.get_select_all_string())[0] + + return self._from_result(result) + + def add_technician_config(self, technician_config: TechnicianConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_config.insert_string}") + self._context.cursor.execute(technician_config.insert_string) + + def update_technician_config(self, technician_config: TechnicianConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_config.udpate_string}") + self._context.cursor.execute(technician_config.udpate_string) + + def delete_technician_config(self, technician_config: TechnicianConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_config.delete_string}") + self._context.cursor.execute(technician_config.delete_string) + + def add_technician_id_config(self, technician_id: TechnicianIdConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_id.insert_string}") + self._context.cursor.execute(technician_id.insert_string) + + def update_technician_id_config(self, technician_id: TechnicianIdConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_id.udpate_string}") + self._context.cursor.execute(technician_id.udpate_string) + + def delete_technician_id_config(self, technician_id: TechnicianIdConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_id.delete_string}") + self._context.cursor.execute(technician_id.delete_string) + + def add_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_ping_url.insert_string}") + self._context.cursor.execute(technician_ping_url.insert_string) + + def update_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_ping_url.udpate_string}") + self._context.cursor.execute(technician_ping_url.udpate_string) + + def delete_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig): + self._logger.trace(__name__, f"Send SQL command: {technician_ping_url.delete_string}") + self._context.cursor.execute(technician_ping_url.delete_string) diff --git a/kdb-bot/src/bot_data/service/technician_config_seeder.py b/kdb-bot/src/bot_data/service/technician_config_seeder.py new file mode 100644 index 00000000..209b3e02 --- /dev/null +++ b/kdb-bot/src/bot_data/service/technician_config_seeder.py @@ -0,0 +1,50 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.data_seeder_abc import DataSeederABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC +from bot_data.model.technician_config import TechnicianConfig +from bot_data.model.technician_id_config import TechnicianIdConfig +from bot_data.model.technician_ping_url_config import TechnicianPingUrlConfig + + +class TechnicianConfigSeeder(DataSeederABC): + def __init__( + self, + logger: DatabaseLogger, + technician_config: TechnicianConfigRepositoryABC, + db: DatabaseContextABC, + ): + DataSeederABC.__init__(self) + + self._logger = logger + self._technician_config = technician_config + self._db = db + + async def seed(self): + try: + if self._technician_config.does_technician_config_exists(): + return + + config = TechnicianConfig( + "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle", + 8, + 8, + 1000000, + {}, + List(int, [240160344557879316]), + List(str, ["www.google.com", "www.sh-edraft.de", "www.keksdose-gaming.de"]), + ) + + self._technician_config.add_technician_config(config) + for technician in config.technician_ids: + self._technician_config.add_technician_id_config(TechnicianIdConfig(technician)) + + for url in config.ping_urls: + self._technician_config.add_technician_ping_url_config(TechnicianPingUrlConfig(url)) + + self._db.save_changes() + self._logger.debug(__name__, "Seeded technician config") + except Exception as e: + self._logger.error(__name__, f"Seeding technician config failed", e) diff --git a/kdb-bot/src/bot_data/service/user_repository_service.py b/kdb-bot/src/bot_data/service/user_repository_service.py index adc9685c..73387908 100644 --- a/kdb-bot/src/bot_data/service/user_repository_service.py +++ b/kdb-bot/src/bot_data/service/user_repository_service.py @@ -27,9 +27,11 @@ class UserRepositoryService(UserRepositoryABC): return User( result[1], result[2], - self._servers.get_server_by_id(result[3]), + result[3], result[4], - result[5], + self._servers.get_server_by_id(result[5]), + result[6], + result[7], id=result[0], ) diff --git a/kdb-bot/src/bot_graphql/__init__.py b/kdb-bot/src/bot_graphql/__init__.py index 4c330c85..96186550 100644 --- a/kdb-bot/src/bot_graphql/__init__.py +++ b/kdb-bot/src/bot_graphql/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/abc/__init__.py b/kdb-bot/src/bot_graphql/abc/__init__.py index 2c834d6f..455ce81e 100644 --- a/kdb-bot/src/bot_graphql/abc/__init__.py +++ b/kdb-bot/src/bot_graphql/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql.abc" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/abc/query_abc.py b/kdb-bot/src/bot_graphql/abc/query_abc.py index 9c8f3ef1..22db1896 100644 --- a/kdb-bot/src/bot_graphql/abc/query_abc.py +++ b/kdb-bot/src/bot_graphql/abc/query_abc.py @@ -2,12 +2,14 @@ from typing import Callable from ariadne import ObjectType from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.type import T from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from bot_api.exception.service_error_code_enum import ServiceErrorCode from bot_api.exception.service_exception import ServiceException from bot_api.route.route import Route +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_user import AuthUser from bot_data.model.auto_role import AutoRole @@ -16,6 +18,7 @@ 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.server_config import ServerConfig from bot_data.model.user import User from bot_data.model.user_joined_game_server import UserJoinedGameServer from bot_data.model.user_joined_server import UserJoinedServer @@ -65,8 +68,12 @@ class QueryABC(ObjectType): self.set_field(f"{name}s", wrapper) self.set_field(f"{name}Count", lambda *args: wrapper(*args).count()) - @ServiceProviderABC.inject - def _can_user_see_element(self, user: AuthUser, element, services: ServiceProviderABC) -> bool: + def _can_user_see_element(self, user: AuthUser, element: T) -> bool: + @ServiceProviderABC.inject + def get_services(services: ServiceProviderABC) -> ServiceProviderABC: + return services + + services = get_services() permissions: PermissionService = services.get_service(PermissionService) bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) @@ -82,12 +89,12 @@ class QueryABC(ObjectType): break elif type(element) == AutoRoleRule: - element: AutoRole = element + element: AutoRoleRule = element for u in user.users: u: User = u guild = bot.get_guild(u.server.discord_id) member = guild.get_member(u.discord_id) - if permissions.is_member_moderator(member) and u.server.id == element.server.id: + if permissions.is_member_moderator(member) and u.server.id == element.auto_role.server.id: access = True break @@ -157,6 +164,24 @@ class QueryABC(ObjectType): access = True break + elif type(element) == ServerConfig: + for u in user.users: + u: User = u + guild = bot.get_guild(u.server.discord_id) + member = guild.get_member(u.discord_id) + if permissions.is_member_technician(member): + access = True + break + + elif type(element) == dict and "key" in element and element["key"] in [e.value for e in FeatureFlagsEnum]: + for u in user.users: + u: User = u + guild = bot.get_guild(u.server.discord_id) + member = guild.get_member(u.discord_id) + if permissions.is_member_technician(member): + access = True + break + return access @ServiceProviderABC.inject diff --git a/kdb-bot/src/bot_graphql/bot-graphql.json b/kdb-bot/src/bot_graphql/bot-graphql.json index bab3a067..fbb23a9a 100644 --- a/kdb-bot/src/bot_graphql/bot-graphql.json +++ b/kdb-bot/src/bot_graphql/bot-graphql.json @@ -3,8 +3,8 @@ "Name": "bot-data", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", @@ -16,10 +16,10 @@ "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [ - "cpl-core>=1.0.7" + "cpl-core==2022.12.0" ], "DevDependencies": [ - "cpl-cli>=1.0.7" + "cpl-core==2022.12.0" ], "PythonVersion": ">=3.10.4", "PythonPath": {}, diff --git a/kdb-bot/src/bot_graphql/filter/__init__.py b/kdb-bot/src/bot_graphql/filter/__init__.py index 509824dc..fefc81b2 100644 --- a/kdb-bot/src/bot_graphql/filter/__init__.py +++ b/kdb-bot/src/bot_graphql/filter/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql.filter" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/filter/achievement_filter.py b/kdb-bot/src/bot_graphql/filter/achievement_filter.py new file mode 100644 index 00000000..674af502 --- /dev/null +++ b/kdb-bot/src/bot_graphql/filter/achievement_filter.py @@ -0,0 +1,67 @@ +from cpl_query.extension import List + +from bot_data.model.user import User +from bot_graphql.abc.filter_abc import FilterABC + + +class AchievementFilter(FilterABC): + def __init__(self): + FilterABC.__init__(self) + + self._id = None + self._name = None + self._description = None + self._attribute = None + self._operator = None + self._value = None + self._server = None + + def from_dict(self, values: dict): + if "id" in values: + self._id = int(values["id"]) + + if "name" in values: + self._name = values["name"] + + if "description" in values: + self._description = values["description"] + + if "attribute" in values: + self._attribute = values["attribute"] + + if "operator" in values: + self._operator = values["operator"] + + if "value" in values: + self._value = values["value"] + + if "server" in values: + from bot_graphql.filter.server_filter import ServerFilter + + self._server: ServerFilter = self._services.get_service(ServerFilter) + self._server.from_dict(values["server"]) + + def filter(self, query: List[User]) -> List[User]: + if self._id is not None: + query = query.where(lambda x: x.id == self._id) + + if self._name is not None: + query = query.where(lambda x: x.name == self._name or self._name in x.name) + + if self._description is not None: + query = query.where(lambda x: x.description == self._description or self._description in x.description) + + if self._attribute is not None: + query = query.where(lambda x: x.attribute == self._attribute or self._attribute in x.attribute) + + if self._operator is not None: + query = query.where(lambda x: x.operator == self._operator) + + if self._value is not None: + query = query.where(lambda x: x.value == self._value) + + if self._server is not None: + servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.id) + query = query.where(lambda x: x.server.id in servers) + + return query diff --git a/kdb-bot/src/bot_graphql/graphql/achievement.gql b/kdb-bot/src/bot_graphql/graphql/achievement.gql new file mode 100644 index 00000000..df16dd4e --- /dev/null +++ b/kdb-bot/src/bot_graphql/graphql/achievement.gql @@ -0,0 +1,64 @@ +type AchievementAttribute { + name: String + type: String + + createdAt: String + modifiedAt: String +} + +type Achievement implements TableWithHistoryQuery { + id: ID + name: String + description: String + attribute: String + operator: String + value: String + + server: Server + + createdAt: String + modifiedAt: String + + history: [AchievementHistory] +} + +type AchievementHistory implements HistoryTableQuery { + id: ID + name: String + description: String + attribute: String + operator: String + value: String + + server: ID + + deleted: Boolean + dateFrom: String + dateTo: String +} + +input AchievementFilter { + id: ID + name: String + description: String + attribute: String + operator: String + value: String + server: ServerFilter +} + +type AchievementMutation { + createAchievement(input: AchievementInput!): Achievement + updateAchievement(input: AchievementInput!): Achievement + deleteAchievement(id: ID): Achievement +} + +input AchievementInput { + id: ID + name: String + description: String + attribute: String + operator: String + value: String + serverId: ID +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/autoRole.gql b/kdb-bot/src/bot_graphql/graphql/autoRole.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/autoRole.gql rename to kdb-bot/src/bot_graphql/graphql/autoRole.gql diff --git a/kdb-bot/src/bot_graphql/model/autoRoleRule.gql b/kdb-bot/src/bot_graphql/graphql/autoRoleRule.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/autoRoleRule.gql rename to kdb-bot/src/bot_graphql/graphql/autoRoleRule.gql diff --git a/kdb-bot/src/bot_graphql/model/base.gql b/kdb-bot/src/bot_graphql/graphql/base.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/base.gql rename to kdb-bot/src/bot_graphql/graphql/base.gql diff --git a/kdb-bot/src/bot_graphql/model/client.gql b/kdb-bot/src/bot_graphql/graphql/client.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/client.gql rename to kdb-bot/src/bot_graphql/graphql/client.gql diff --git a/kdb-bot/src/bot_graphql/graphql/discord.gql b/kdb-bot/src/bot_graphql/graphql/discord.gql new file mode 100644 index 00000000..0c880a3f --- /dev/null +++ b/kdb-bot/src/bot_graphql/graphql/discord.gql @@ -0,0 +1,53 @@ +type Discord { + guilds(filter: GuildFilter): [Guild] + users(filter: DiscordUserFilter): [DiscordUser] +} + +type Guild { + id: ID + name: String + + channels(filter: ChannelFilter): [Channel] + roles: [Role] + emojis: [Emoji] +} + +input GuildFilter { + id: ID + name: String +} + +type Channel { + id: String + name: String + type: String +} + +input ChannelFilter { + id: String + name: String + type: String +} + +type Role { + id: String + name: String +} + +type DiscordUser { + id: String + name: String + bot: Boolean +} + +input DiscordUserFilter { + id: ID + name: String + bot: Boolean +} + +type Emoji { + id: String + name: String + url: String +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/graphql/featureFlags.gql b/kdb-bot/src/bot_graphql/graphql/featureFlags.gql new file mode 100644 index 00000000..1eed1031 --- /dev/null +++ b/kdb-bot/src/bot_graphql/graphql/featureFlags.gql @@ -0,0 +1,9 @@ +type FeatureFlag { + key: String + value: Boolean +} + +input FeatureFlagInput { + key: String + value: Boolean +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/knownUser.gql b/kdb-bot/src/bot_graphql/graphql/knownUser.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/knownUser.gql rename to kdb-bot/src/bot_graphql/graphql/knownUser.gql diff --git a/kdb-bot/src/bot_graphql/model/level.gql b/kdb-bot/src/bot_graphql/graphql/level.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/level.gql rename to kdb-bot/src/bot_graphql/graphql/level.gql diff --git a/kdb-bot/src/bot_graphql/model/mutation.gql b/kdb-bot/src/bot_graphql/graphql/mutation.gql similarity index 60% rename from kdb-bot/src/bot_graphql/model/mutation.gql rename to kdb-bot/src/bot_graphql/graphql/mutation.gql index 9631fd8d..aa1d8cd6 100644 --- a/kdb-bot/src/bot_graphql/model/mutation.gql +++ b/kdb-bot/src/bot_graphql/graphql/mutation.gql @@ -4,4 +4,7 @@ type Mutation { level: LevelMutation user: UserMutation userJoinedGameServer: UserJoinedGameServerMutation + achievement: AchievementMutation + technicianConfig: TechnicianConfigMutation + serverConfig: ServerConfigMutation } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/query.gql b/kdb-bot/src/bot_graphql/graphql/query.gql similarity index 75% rename from kdb-bot/src/bot_graphql/model/query.gql rename to kdb-bot/src/bot_graphql/graphql/query.gql index 69b5a74a..af2d298e 100644 --- a/kdb-bot/src/bot_graphql/model/query.gql +++ b/kdb-bot/src/bot_graphql/graphql/query.gql @@ -17,6 +17,9 @@ type Query { serverCount: Int servers(filter: ServerFilter, page: Page, sort: Sort): [Server] + gameServerCount: Int + gameServers: [GameServer] + userJoinedServerCount: Int userJoinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [UserJoinedServer] @@ -29,5 +32,12 @@ type Query { userCount: Int users(filter: UserFilter, page: Page, sort: Sort): [User] - guilds(filter: GuildFilter): [Guild] + achievementCount: Int + achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] + achievementAttributes: [AchievementAttribute] + achievementOperators: [String] + + technicianConfig: TechnicianConfig + possibleFeatureFlags: [String] + discord: Discord } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/server.gql b/kdb-bot/src/bot_graphql/graphql/server.gql similarity index 72% rename from kdb-bot/src/bot_graphql/model/server.gql rename to kdb-bot/src/bot_graphql/graphql/server.gql index db94dc89..be754bdb 100644 --- a/kdb-bot/src/bot_graphql/model/server.gql +++ b/kdb-bot/src/bot_graphql/graphql/server.gql @@ -1,3 +1,12 @@ +type GameServer { + id: ID + name: String + server: Server + + createdAt: String + modifiedAt: String +} + type Server implements TableWithHistoryQuery { id: ID discordId: String @@ -13,9 +22,17 @@ type Server implements TableWithHistoryQuery { levelCount: Int levels(filter: LevelFilter, page: Page, sort: Sort): [Level] + gameServerCount: Int + gameServers: [GameServer] + userCount: Int users(filter: UserFilter, page: Page, sort: Sort): [User] + achievementCount: Int + achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] + + config: ServerConfig + createdAt: String modifiedAt: String diff --git a/kdb-bot/src/bot_graphql/graphql/serverConfig.gql b/kdb-bot/src/bot_graphql/graphql/serverConfig.gql new file mode 100644 index 00000000..1734114a --- /dev/null +++ b/kdb-bot/src/bot_graphql/graphql/serverConfig.gql @@ -0,0 +1,99 @@ +type ServerConfig implements TableWithHistoryQuery { + id: ID + messageDeleteTimer: Int + notificationChatId: String + maxVoiceStateHours: Int + xpPerMessage: Int + xpPerReaction: Int + maxMessageXpPerHour: Int + xpPerOntimeHour: Int + xpPerEventParticipation: Int + xpPerAchievement: Int + afkCommandChannelId: String + helpVoiceChannelId: String + teamChannelId: String + loginMessageChannelId: String + featureFlagCount: Int + featureFlags: [FeatureFlag] + + afkChannelIds: [String] + moderatorRoleIds: [String] + adminRoleIds: [String] + + server: Server + + createdAt: String + modifiedAt: String + + history: [ServerConfigHistory] +} + +type ServerConfigHistory implements HistoryTableQuery { + id: ID + messageDeleteTimer: Int + notificationChatId: String + maxVoiceStateHours: Int + xpPerMessage: Int + xpPerReaction: Int + maxMessageXpPerHour: Int + xpPerOntimeHour: Int + xpPerEventParticipation: Int + xpPerAchievement: Int + afkCommandChannelId: String + helpVoiceChannelId: String + teamChannelId: String + loginMessageChannelId: String + featureFlagCount: Int + featureFlags: [FeatureFlag] + + serverId: ID + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type ServerAFKChannelIdsConfigHistory implements HistoryTableQuery { + id: ID + channelId: String + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type ServerTeamRoleIdsConfigHistory implements HistoryTableQuery { + id: ID + roleId: String + teamMemberType: String + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type ServerConfigMutation { + updateServerConfig(input: ServerConfigInput!): ServerConfig +} + +input ServerConfigInput { + id: ID + messageDeleteTimer: Int + notificationChatId: String + maxVoiceStateHours: Int + xpPerMessage: Int + xpPerReaction: Int + maxMessageXpPerHour: Int + xpPerOntimeHour: Int + xpPerEventParticipation: Int + xpPerAchievement: Int + afkCommandChannelId: String + helpVoiceChannelId: String + teamChannelId: String + loginMessageChannelId: String + featureFlags: [FeatureFlagInput] + + afkChannelIds: [String] + moderatorRoleIds: [String] + adminRoleIds: [String] +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/graphql/technicianConfig.gql b/kdb-bot/src/bot_graphql/graphql/technicianConfig.gql new file mode 100644 index 00000000..dd91ceab --- /dev/null +++ b/kdb-bot/src/bot_graphql/graphql/technicianConfig.gql @@ -0,0 +1,65 @@ +type TechnicianConfig implements TableWithHistoryQuery { + id: ID + helpCommandReferenceUrl: String + waitForRestart: Int + waitForShutdown: Int + cacheMaxMessages: Int + featureFlagCount: Int + featureFlags: [FeatureFlag] + pingURLs: [String] + technicianIds: [String] + + createdAt: String + modifiedAt: String + + history: [TechnicianConfigHistory] + pingURLHistory: [TechnicianPingUrlConfigHistory] + technicianIdHistory: [TechnicianIdConfigHistory] +} + +type TechnicianConfigHistory implements HistoryTableQuery { + id: ID + helpCommandReferenceUrl: String + waitForRestart: Int + waitForShutdown: Int + cacheMaxMessages: Int + featureFlagCount: Int + featureFlags: [FeatureFlag] + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type TechnicianPingUrlConfigHistory implements HistoryTableQuery { + id: ID + url: String + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type TechnicianIdConfigHistory implements HistoryTableQuery { + id: ID + technicianId: String + + deleted: Boolean + dateFrom: String + dateTo: String +} + +type TechnicianConfigMutation { + updateTechnicianConfig(input: TechnicianConfigInput!): TechnicianConfig +} + +input TechnicianConfigInput { + id: ID + helpCommandReferenceUrl: String + waitForRestart: Int + waitForShutdown: Int + cacheMaxMessages: Int + featureFlags: [FeatureFlagInput] + pingURLs: [String] + technicianIds: [String] +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/user.gql b/kdb-bot/src/bot_graphql/graphql/user.gql similarity index 88% rename from kdb-bot/src/bot_graphql/model/user.gql rename to kdb-bot/src/bot_graphql/graphql/user.gql index e7f03f38..38db3573 100644 --- a/kdb-bot/src/bot_graphql/model/user.gql +++ b/kdb-bot/src/bot_graphql/graphql/user.gql @@ -3,6 +3,8 @@ type User implements TableWithHistoryQuery { discordId: String name: String xp: Int + messageCount: Int + reactionCount: Int ontime: Float level: Level @@ -15,6 +17,9 @@ type User implements TableWithHistoryQuery { userJoinedGameServerCount: Int userJoinedGameServers(filter: UserJoinedGameServerFilter, page: Page, sort: Sort): [UserJoinedGameServer] + achievementCount: Int + achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] + server: Server leftServer: Boolean diff --git a/kdb-bot/src/bot_graphql/model/userJoinedGameServer.gql b/kdb-bot/src/bot_graphql/graphql/userJoinedGameServer.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/userJoinedGameServer.gql rename to kdb-bot/src/bot_graphql/graphql/userJoinedGameServer.gql diff --git a/kdb-bot/src/bot_graphql/model/userJoinedServer.gql b/kdb-bot/src/bot_graphql/graphql/userJoinedServer.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/userJoinedServer.gql rename to kdb-bot/src/bot_graphql/graphql/userJoinedServer.gql diff --git a/kdb-bot/src/bot_graphql/model/userJoinedVoiceChannel.gql b/kdb-bot/src/bot_graphql/graphql/userJoinedVoiceChannel.gql similarity index 100% rename from kdb-bot/src/bot_graphql/model/userJoinedVoiceChannel.gql rename to kdb-bot/src/bot_graphql/graphql/userJoinedVoiceChannel.gql diff --git a/kdb-bot/src/bot_graphql/graphql_module.py b/kdb-bot/src/bot_graphql/graphql_module.py index 5ef7df09..1236a7b2 100644 --- a/kdb-bot/src/bot_graphql/graphql_module.py +++ b/kdb-bot/src/bot_graphql/graphql_module.py @@ -8,6 +8,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.service.seeder_service import SeederService from bot_graphql.abc.filter_abc import FilterABC from bot_graphql.abc.query_abc import QueryABC +from bot_graphql.filter.achievement_filter import AchievementFilter from bot_graphql.filter.auto_role_filter import AutoRoleFilter from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter from bot_graphql.filter.client_filter import ClientFilter @@ -19,23 +20,41 @@ from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter from bot_graphql.graphql_service import GraphQLService from bot_graphql.mutation import Mutation +from bot_graphql.mutations.achievement_mutation import AchievementMutation from bot_graphql.mutations.auto_role_mutation import AutoRoleMutation from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation from bot_graphql.mutations.level_mutation import LevelMutation +from bot_graphql.mutations.server_config_mutation import ServerConfigMutation +from bot_graphql.mutations.technician_config_mutation import TechnicianConfigMutation from bot_graphql.mutations.user_joined_game_server_mutation import UserJoinedGameServerMutation from bot_graphql.mutations.user_mutation import UserMutation +from bot_graphql.queries.achievement_attribute_query import AchievementAttributeQuery +from bot_graphql.queries.achievement_history_query import AchievementHistoryQuery +from bot_graphql.queries.achievement_query import AchievementQuery from bot_graphql.queries.auto_role_history_query import AutoRoleHistoryQuery from bot_graphql.queries.auto_role_query import AutoRoleQuery from bot_graphql.queries.auto_role_rule_history_query import AutoRoleRuleHistoryQuery from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery from bot_graphql.queries.client_history_query import ClientHistoryQuery from bot_graphql.queries.client_query import ClientQuery +from bot_graphql.queries.discord.channel_query import ChannelQuery +from bot_graphql.queries.discord.discord_query import DiscordQuery +from bot_graphql.queries.discord.discord_user_query import DiscordUserQuery +from bot_graphql.queries.discord.emoji_query import EmojiQuery +from bot_graphql.queries.discord.guild_query import GuildQuery +from bot_graphql.queries.discord.role_query import RoleQuery +from bot_graphql.queries.game_server_query import GameServerQuery from bot_graphql.queries.known_user_history_query import KnownUserHistoryQuery from bot_graphql.queries.known_user_query import KnownUserQuery from bot_graphql.queries.level_history_query import LevelHistoryQuery from bot_graphql.queries.level_query import LevelQuery +from bot_graphql.queries.server_config_query import ServerConfigQuery from bot_graphql.queries.server_history_query import ServerHistoryQuery from bot_graphql.queries.server_query import ServerQuery +from bot_graphql.queries.technician_config_history_query import TechnicianConfigHistoryQuery +from bot_graphql.queries.technician_config_query import TechnicianConfigQuery +from bot_graphql.queries.technician_id_config_history_query import TechnicianIdConfigHistoryQuery +from bot_graphql.queries.technician_ping_url_config_history_query import TechnicianPingUrlConfigHistoryQuery from bot_graphql.queries.user_history_query import UserHistoryQuery from bot_graphql.queries.user_joined_game_server_history_query import UserJoinedGameServerHistoryQuery from bot_graphql.queries.user_joined_game_server_query import UserJoinedGameServerQuery @@ -62,6 +81,9 @@ class GraphQLModule(ModuleABC): services.add_singleton(Mutation) # queries + services.add_transient(QueryABC, AchievementAttributeQuery) + services.add_transient(QueryABC, AchievementQuery) + services.add_transient(QueryABC, AchievementHistoryQuery) services.add_transient(QueryABC, AutoRoleHistoryQuery) services.add_transient(QueryABC, AutoRoleQuery) services.add_transient(QueryABC, AutoRoleRuleHistoryQuery) @@ -72,8 +94,14 @@ class GraphQLModule(ModuleABC): services.add_transient(QueryABC, KnownUserQuery) services.add_transient(QueryABC, LevelHistoryQuery) services.add_transient(QueryABC, LevelQuery) + services.add_transient(QueryABC, ServerConfigQuery) services.add_transient(QueryABC, ServerHistoryQuery) services.add_transient(QueryABC, ServerQuery) + services.add_transient(QueryABC, TechnicianConfigQuery) + services.add_transient(QueryABC, TechnicianConfigHistoryQuery) + services.add_transient(QueryABC, TechnicianPingUrlConfigHistoryQuery) + services.add_transient(QueryABC, TechnicianIdConfigHistoryQuery) + services.add_transient(QueryABC, GameServerQuery) services.add_transient(QueryABC, UserHistoryQuery) services.add_transient(QueryABC, UserQuery) services.add_transient(QueryABC, UserJoinedServerHistoryQuery) @@ -83,6 +111,13 @@ class GraphQLModule(ModuleABC): services.add_transient(QueryABC, UserJoinedGameServerHistoryQuery) services.add_transient(QueryABC, UserJoinedGameServerQuery) + services.add_transient(QueryABC, DiscordQuery) + services.add_transient(QueryABC, GuildQuery) + services.add_transient(QueryABC, ChannelQuery) + services.add_transient(QueryABC, RoleQuery) + services.add_transient(QueryABC, EmojiQuery) + services.add_transient(QueryABC, DiscordUserQuery) + # filters services.add_transient(FilterABC, AutoRoleFilter) services.add_transient(FilterABC, AutoRoleRuleFilter) @@ -90,6 +125,7 @@ class GraphQLModule(ModuleABC): services.add_transient(FilterABC, LevelFilter) services.add_transient(FilterABC, ServerFilter) services.add_transient(FilterABC, UserFilter) + services.add_transient(FilterABC, AchievementFilter) services.add_transient(FilterABC, UserJoinedServerFilter) services.add_transient(FilterABC, UserJoinedVoiceChannelFilter) services.add_transient(FilterABC, UserJoinedGameServerFilter) @@ -99,6 +135,9 @@ class GraphQLModule(ModuleABC): services.add_transient(QueryABC, AutoRoleRuleMutation) services.add_transient(QueryABC, LevelMutation) services.add_transient(QueryABC, UserMutation) + services.add_transient(QueryABC, AchievementMutation) services.add_transient(QueryABC, UserJoinedGameServerMutation) + services.add_transient(QueryABC, TechnicianConfigMutation) + services.add_transient(QueryABC, ServerConfigMutation) services.add_transient(SeederService) diff --git a/kdb-bot/src/modules/permission/configuration/__init__.py b/kdb-bot/src/bot_graphql/model/__init__.py similarity index 76% rename from kdb-bot/src/modules/permission/configuration/__init__.py rename to kdb-bot/src/bot_graphql/model/__init__.py index 60e3d7f5..c427041d 100644 --- a/kdb-bot/src/modules/permission/configuration/__init__.py +++ b/kdb-bot/src/bot_graphql/model/__init__.py @@ -11,11 +11,11 @@ Discord bot for the Keksdose discord Server """ -__title__ = "modules.permission.configuration" +__title__ = "bot_graphql.model" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/model/discord.gql b/kdb-bot/src/bot_graphql/model/discord.gql deleted file mode 100644 index 1218b790..00000000 --- a/kdb-bot/src/bot_graphql/model/discord.gql +++ /dev/null @@ -1,29 +0,0 @@ -type Guild { - id: ID - name: String - - channels: [Channel] - roles: [Role] - emojis: [Emoji] -} - -input GuildFilter { - id: ID -} - -type Channel { - id: String - name: String - type: String -} - -type Role { - id: String - name: String -} - -type Emoji { - id: String - name: String - url: String -} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/discord.py b/kdb-bot/src/bot_graphql/model/discord.py new file mode 100644 index 00000000..353e7d8f --- /dev/null +++ b/kdb-bot/src/bot_graphql/model/discord.py @@ -0,0 +1,16 @@ +from cpl_query.extension import List +from discord import Guild, User + + +class Discord: + def __init__(self, guilds: List[Guild], users: List[User]): + self._guilds = guilds + self._users = users + + @property + def guilds(self) -> List[Guild]: + return self._guilds + + @property + def users(self) -> List[User]: + return self._users diff --git a/kdb-bot/src/bot_graphql/mutation.py b/kdb-bot/src/bot_graphql/mutation.py index 56bfa7c5..4631e321 100644 --- a/kdb-bot/src/bot_graphql/mutation.py +++ b/kdb-bot/src/bot_graphql/mutation.py @@ -1,9 +1,11 @@ from ariadne import MutationType -from bot_data.model.user_joined_game_server import UserJoinedGameServer +from bot_graphql.mutations.achievement_mutation import AchievementMutation from bot_graphql.mutations.auto_role_mutation import AutoRoleMutation from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation from bot_graphql.mutations.level_mutation import LevelMutation +from bot_graphql.mutations.server_config_mutation import ServerConfigMutation +from bot_graphql.mutations.technician_config_mutation import TechnicianConfigMutation from bot_graphql.mutations.user_joined_game_server_mutation import UserJoinedGameServerMutation from bot_graphql.mutations.user_mutation import UserMutation @@ -15,7 +17,10 @@ class Mutation(MutationType): auto_role_rule_mutation: AutoRoleRuleMutation, level_mutation: LevelMutation, user_mutation: UserMutation, + achievement_mutation: AchievementMutation, user_joined_game_server: UserJoinedGameServerMutation, + technician_config: TechnicianConfigMutation, + server_config: ServerConfigMutation, ): MutationType.__init__(self) @@ -23,4 +28,7 @@ class Mutation(MutationType): self.set_field("autoRoleRule", lambda *_: auto_role_rule_mutation) self.set_field("level", lambda *_: level_mutation) self.set_field("user", lambda *_: user_mutation) + self.set_field("achievement", lambda *_: achievement_mutation) self.set_field("userJoinedGameServer", lambda *_: user_joined_game_server) + self.set_field("technicianConfig", lambda *_: technician_config) + self.set_field("serverConfig", lambda *_: server_config) diff --git a/kdb-bot/src/bot_graphql/mutations/__init__.py b/kdb-bot/src/bot_graphql/mutations/__init__.py index 2951fe8f..b53c0a0d 100644 --- a/kdb-bot/src/bot_graphql/mutations/__init__.py +++ b/kdb-bot/src/bot_graphql/mutations/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql.mutations" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/mutations/achievement_mutation.py b/kdb-bot/src/bot_graphql/mutations/achievement_mutation.py new file mode 100644 index 00000000..40975a38 --- /dev/null +++ b/kdb-bot/src/bot_graphql/mutations/achievement_mutation.py @@ -0,0 +1,87 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.achievement import Achievement +from bot_data.model.user_role_enum import UserRoleEnum +from bot_graphql.abc.query_abc import QueryABC +from modules.permission.service.permission_service import PermissionService + + +class AchievementMutation(QueryABC): + def __init__( + self, + servers: ServerRepositoryABC, + achievements: AchievementRepositoryABC, + bot: DiscordBotServiceABC, + db: DatabaseContextABC, + permissions: PermissionService, + ): + QueryABC.__init__(self, "AchievementMutation") + + self._servers = servers + self._achievements = achievements + self._bot = bot + self._db = db + self._permissions = permissions + + self.set_field("createAchievement", self.resolve_create_achievement) + self.set_field("updateAchievement", self.resolve_update_achievement) + self.set_field("deleteAchievement", self.resolve_delete_achievement) + + def resolve_create_achievement(self, *_, input: dict): + server = self._servers.get_server_by_id(input["serverId"]) + self._can_user_mutate_data(server, UserRoleEnum.admin) + + achievement = Achievement( + input["name"], + input["description"], + input["attribute"], + input["operator"], + input["value"], + server, + ) + self._achievements.add_achievement(achievement) + self._db.save_changes() + + def get_new_achievement(a: Achievement): + return ( + a.name == achievement.name + and a.description == achievement.description + and a.attribute == achievement.attribute + and a.operator == achievement.operator + and a.value == achievement.value + and a.server.id == server.id + ) + + return self._achievements.get_achievements_by_server_id(achievement.server.id).where(get_new_achievement).last() + + def resolve_update_achievement(self, *_, input: dict): + achievement = self._achievements.get_achievement_by_id(input["id"]) + self._can_user_mutate_data(achievement.server, UserRoleEnum.moderator) + + achievement.name = input["name"] if "name" in input else achievement.name + achievement.description = input["description"] if "description" in input else achievement.description + achievement.attribute = input["attribute"] if "attribute" in input else achievement.attribute + achievement.operator = input["operator"] if "operator" in input else achievement.operator + achievement.value = input["value"] if "value" in input else achievement.value + + self._achievements.update_achievement(achievement) + self._db.save_changes() + + achievement = self._achievements.get_achievement_by_id(input["id"]) + return achievement + + def resolve_delete_achievement(self, *_, id: int): + achievement = self._achievements.get_achievement_by_id(id) + self._can_user_mutate_data(achievement.server, UserRoleEnum.admin) + + joins = self._achievements.get_user_got_achievements_by_achievement_id(id) + for join in joins: + self._achievements.delete_user_got_achievement(join) + + self._achievements.delete_achievement(achievement) + self._db.save_changes() + + return achievement diff --git a/kdb-bot/src/bot_graphql/mutations/level_mutation.py b/kdb-bot/src/bot_graphql/mutations/level_mutation.py index 47f61e7c..215538b7 100644 --- a/kdb-bot/src/bot_graphql/mutations/level_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/level_mutation.py @@ -50,6 +50,7 @@ class LevelMutation(QueryABC): and l.color == level.color and l.min_xp == level.min_xp and l.permissions == level.permissions + and l.server.id == server.id ) self._bot.loop.create_task(self._level_seeder.seed()) diff --git a/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py new file mode 100644 index 00000000..4a56d9f0 --- /dev/null +++ b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py @@ -0,0 +1,178 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List + +from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route +from bot_core.service.config_service import ConfigService +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_afk_channel_ids_config import ServerAFKChannelIdsConfig +from bot_data.model.server_config import ServerConfig +from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig +from bot_data.model.team_member_type_enum import TeamMemberTypeEnum +from bot_data.model.user_role_enum import UserRoleEnum +from bot_graphql.abc.query_abc import QueryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class ServerConfigMutation(QueryABC): + def __init__( + self, + logger: ApiLogger, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + server_configs: ServerConfigRepositoryABC, + db: DatabaseContextABC, + config_service: ConfigService, + permissions: PermissionServiceABC, + ): + QueryABC.__init__(self, "ServerConfigMutation") + + self._logger = logger + self._bot = bot + self._servers = servers + self._server_configs = server_configs + self._db = db + self._config_service = config_service + self._permissions = permissions + + self.set_field("updateServerConfig", self.resolve_update_server_config) + + def resolve_update_server_config(self, *_, input: dict): + if "id" not in input: + raise ValueError("Id not set") + + server_config = self._server_configs.get_server_config_by_id(int(input["id"])) + self._can_user_mutate_data(Route.get_user().users[0].server, UserRoleEnum.technician) + + server_config.message_delete_timer = ( + input["messageDeleteTimer"] if "messageDeleteTimer" in input else server_config.message_delete_timer + ) + server_config.notification_chat_id = ( + input["notificationChatId"] if "notificationChatId" in input else server_config.notification_chat_id + ) + server_config.max_voice_state_hours = ( + input["maxVoiceStateHours"] if "maxVoiceStateHours" in input else server_config.max_voice_state_hours + ) + server_config.xp_per_message = ( + input["xpPerMessage"] if "xpPerMessage" in input else server_config.xp_per_message + ) + server_config.xp_per_reaction = ( + input["xpPerReaction"] if "xpPerReaction" in input else server_config.xp_per_reaction + ) + server_config.max_message_xp_per_hour = ( + input["maxMessageXpPerHour"] if "maxMessageXpPerHour" in input else server_config.max_message_xp_per_hour + ) + server_config.xp_per_ontime_hour = ( + input["xpPerOntimeHour"] if "xpPerOntimeHour" in input else server_config.xp_per_ontime_hour + ) + server_config.xp_per_event_participation = ( + input["xpPerEventParticipation"] + if "xpPerEventParticipation" in input + else server_config.xp_per_event_participation + ) + server_config.xp_per_achievement = ( + input["xpPerAchievement"] if "xpPerAchievement" in input else server_config.xp_per_achievement + ) + server_config.afk_command_channel_id = ( + input["afkCommandChannelId"] if "afkCommandChannelId" in input else server_config.afk_command_channel_id + ) + server_config.help_voice_channel_id = ( + input["helpVoiceChannelId"] if "helpVoiceChannelId" in input else server_config.help_voice_channel_id + ) + server_config.team_channel_id = ( + input["teamChannelId"] if "teamChannelId" in input else server_config.team_channel_id + ) + server_config.login_message_channel_id = ( + input["loginMessageChannelId"] + if "loginMessageChannelId" in input + else server_config.login_message_channel_id + ) + server_config.feature_flags = ( + dict(zip([x["key"] for x in input["featureFlags"]], [x["value"] for x in input["featureFlags"]])) + if "featureFlags" in input + else server_config.feature_flags + ) + server_config.afk_channel_ids = ( + List(int).extend([int(x) for x in input["afkChannelIds"]]) + if "afkChannelIds" in input + else server_config.afk_channel_ids + ) + + team_role_ids = List(ServerTeamRoleIdsConfig) + if "moderatorRoleIds" in input: + team_role_ids.extend( + [ + ServerTeamRoleIdsConfig(x, TeamMemberTypeEnum.moderator, server_config.server.id) + for x in input["moderatorRoleIds"] + ] + ) + else: + team_role_ids.extend( + server_config.team_role_ids.where(lambda x: x.team_member_type == TeamMemberTypeEnum.moderator) + ) + + if "adminRoleIds" in input: + team_role_ids.extend( + [ + ServerTeamRoleIdsConfig(x, TeamMemberTypeEnum.admin, server_config.server.id) + for x in input["adminRoleIds"] + ] + ) + else: + team_role_ids.extend( + server_config.team_role_ids.where(lambda x: x.team_member_type == TeamMemberTypeEnum.admin) + ) + + self._server_configs.update_server_config(server_config) + if "afkChannelIds" in input: + self._update_afk_channel_ids(server_config) + + if "moderatorRoleIds" in input or "adminRoleIds" in input: + server_config.team_role_ids = team_role_ids + self._update_team_role_ids(server_config) + + self._db.save_changes() + return server_config + + def _update_afk_channel_ids(self, new_config: ServerConfig): + old_config = self._server_configs.get_server_config_by_server(new_config.server.id) + for channel_id in old_config.afk_channel_ids: + if channel_id in new_config.afk_channel_ids: + continue + + self._server_configs.delete_server_afk_channel_config( + ServerAFKChannelIdsConfig(channel_id, new_config.server.id) + ) + + for channel_id in new_config.afk_channel_ids: + if channel_id in old_config.afk_channel_ids: + continue + + self._server_configs.add_server_afk_channel_config( + ServerAFKChannelIdsConfig(channel_id, new_config.server.id) + ) + + def _update_team_role_ids(self, new_config: ServerConfig): + old_config = self._server_configs.get_server_config_by_server(new_config.server.id) + for role_id in old_config.team_role_ids: + if role_id.role_id in new_config.team_role_ids.select(lambda x: int(x.role_id)): + continue + + self._server_configs.delete_server_team_role_id_config(role_id) + + for role_id in new_config.team_role_ids: + guild = self._bot.get_guild(new_config.server.discord_id) + role = guild.get_role(int(role_id.role_id)) + if role is None: + raise ValueError(f"Invalid roleId") + + for role_id in new_config.team_role_ids: + if role_id.role_id in old_config.team_role_ids.select(lambda x: str(x.role_id)): + continue + + self._server_configs.add_server_team_role_id_config(role_id) + + self._bot.loop.create_task(self._config_service.reload_server_config(new_config.server)) + self._permissions.on_ready() diff --git a/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py b/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py new file mode 100644 index 00000000..5af875ee --- /dev/null +++ b/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py @@ -0,0 +1,117 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List + +from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route +from bot_core.service.config_service import ConfigService +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC +from bot_data.model.technician_config import TechnicianConfig +from bot_data.model.technician_id_config import TechnicianIdConfig +from bot_data.model.technician_ping_url_config import TechnicianPingUrlConfig +from bot_data.model.user_role_enum import UserRoleEnum +from bot_graphql.abc.query_abc import QueryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class TechnicianConfigMutation(QueryABC): + def __init__( + self, + logger: ApiLogger, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + technician_configs: TechnicianConfigRepositoryABC, + db: DatabaseContextABC, + permissions: PermissionServiceABC, + config_service: ConfigService, + ): + QueryABC.__init__(self, "TechnicianConfigMutation") + + self._logger = logger + self._bot = bot + self._servers = servers + self._technician_configs = technician_configs + self._db = db + self._permissions = permissions + self._config_service = config_service + + self.set_field("updateTechnicianConfig", self.resolve_update_technician_config) + + def resolve_update_technician_config(self, *_, input: dict): + technician_config = self._technician_configs.get_technician_config() + self._can_user_mutate_data(Route.get_user().users[0].server, UserRoleEnum.technician) + + technician_config.help_command_reference_url = ( + input["helpCommandReferenceUrl"] + if "helpCommandReferenceUrl" in input + else technician_config.help_command_reference_url + ) + technician_config.wait_for_restart = ( + input["waitForRestart"] if "waitForRestart" in input else technician_config.wait_for_restart + ) + technician_config.wait_for_shutdown = ( + input["waitForShutdown"] if "waitForShutdown" in input else technician_config.wait_for_shutdown + ) + technician_config.cache_max_messages = ( + input["cacheMaxMessages"] if "cacheMaxMessages" in input else technician_config.cache_max_messages + ) + technician_config.feature_flags = ( + dict(zip([x["key"] for x in input["featureFlags"]], [x["value"] for x in input["featureFlags"]])) + if "featureFlags" in input + else technician_config.feature_flags + ) + technician_config.ping_urls = ( + List(str, input["pingURLs"]) if "pingURLs" in input else technician_config.ping_urls + ) + technician_config.technician_ids = ( + List(int).extend([int(x) for x in input["technicianIds"]]) + if "technicianIds" in input + else technician_config.technician_ids + ) + + self._technician_configs.update_technician_config(technician_config) + if "pingURLs" in input: + self._update_ping_urls(technician_config) + + if "technicianIds" in input: + self._update_technician_ids(technician_config) + + self._db.save_changes() + return technician_config + + def _update_ping_urls(self, new_config: TechnicianConfig): + old_config = self._technician_configs.get_technician_config() + for url in old_config.ping_urls: + if url in new_config.ping_urls: + continue + + self._technician_configs.delete_technician_ping_url_config(TechnicianPingUrlConfig(url)) + + for url in new_config.ping_urls: + if url in old_config.ping_urls: + continue + + self._technician_configs.add_technician_ping_url_config(TechnicianPingUrlConfig(url)) + + def _update_technician_ids(self, new_config: TechnicianConfig): + old_config = self._technician_configs.get_technician_config() + for technician_id in old_config.technician_ids: + if technician_id in new_config.technician_ids: + continue + + self._technician_configs.delete_technician_id_config(TechnicianIdConfig(technician_id)) + + for technician_id in new_config.technician_ids: + user = self._bot.get_user(technician_id) + if user is None: + raise ValueError(f"Invalid technicianId") + + for technician_id in new_config.technician_ids: + if technician_id in old_config.technician_ids: + continue + + self._technician_configs.add_technician_id_config(TechnicianIdConfig(technician_id)) + + self._bot.loop.create_task(self._config_service.reload_technician_config()) + self._permissions.on_ready() diff --git a/kdb-bot/src/bot_graphql/mutations/user_joined_game_server_mutation.py b/kdb-bot/src/bot_graphql/mutations/user_joined_game_server_mutation.py index ae0a5c04..62fef010 100644 --- a/kdb-bot/src/bot_graphql/mutations/user_joined_game_server_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/user_joined_game_server_mutation.py @@ -2,6 +2,7 @@ import hashlib from datetime import datetime from typing import Optional +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.service import DiscordBotServiceABC @@ -15,19 +16,18 @@ from bot_data.abc.user_game_ident_repository_abc import UserGameIdentRepositoryA from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.api_key import ApiKey +from bot_data.model.server_config import ServerConfig from bot_data.model.user_joined_game_server import UserJoinedGameServer from bot_data.model.user_role_enum import UserRoleEnum from bot_graphql.abc.query_abc import QueryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings from modules.permission.service.permission_service import PermissionService class UserJoinedGameServerMutation(QueryABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, servers: ServerRepositoryABC, users: UserRepositoryABC, user_joined_game_servers: UserJoinedGameServerRepositoryABC, @@ -41,8 +41,8 @@ class UserJoinedGameServerMutation(QueryABC): ): QueryABC.__init__(self, "UserJoinedGameServerMutation") + self._config = config self._logger = logger - self._base_helper = base_helper self._servers = servers self._users = users self._user_joined_game_servers = user_joined_game_servers @@ -116,7 +116,7 @@ class UserJoinedGameServerMutation(QueryABC): return active.leaved_on = datetime.now() - settings: BaseServerSettings = self._base_helper.get_config(user.server.discord_id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{user.server.discord_id}") ontime = round((active.leaved_on - active.joined_on).total_seconds() / 3600, 2) old_xp = user.xp diff --git a/kdb-bot/src/bot_graphql/queries/__init__.py b/kdb-bot/src/bot_graphql/queries/__init__.py index 743145ac..b4d357d3 100644 --- a/kdb-bot/src/bot_graphql/queries/__init__.py +++ b/kdb-bot/src/bot_graphql/queries/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql.queries" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/queries/achievement_attribute_query.py b/kdb-bot/src/bot_graphql/queries/achievement_attribute_query.py new file mode 100644 index 00000000..f9b77505 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/achievement_attribute_query.py @@ -0,0 +1,11 @@ +from bot_graphql.abc.data_query_abc import DataQueryABC + + +class AchievementAttributeQuery(DataQueryABC): + def __init__( + self, + ): + DataQueryABC.__init__(self, "AchievementAttribute") + + self.set_field("name", lambda x, *_: x.name) + self.set_field("type", lambda x, *_: x.type) diff --git a/kdb-bot/src/bot_graphql/queries/achievement_history_query.py b/kdb-bot/src/bot_graphql/queries/achievement_history_query.py new file mode 100644 index 00000000..d8db3fd3 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/achievement_history_query.py @@ -0,0 +1,14 @@ +from bot_graphql.abc.history_query_abc import HistoryQueryABC + + +class AchievementHistoryQuery(HistoryQueryABC): + def __init__(self): + HistoryQueryABC.__init__(self, "Achievement") + + self.set_field("id", lambda x, *_: x.id) + self.set_field("name", lambda x, *_: x.name) + self.set_field("description", lambda x, *_: x.description) + self.set_field("attribute", lambda x, *_: x.attribute) + self.set_field("operator", lambda x, *_: x.operator) + self.set_field("value", lambda x, *_: x.value) + self.set_field("server", lambda x, *_: x.server) diff --git a/kdb-bot/src/bot_graphql/queries/achievement_query.py b/kdb-bot/src/bot_graphql/queries/achievement_query.py new file mode 100644 index 00000000..ab72fde0 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/achievement_query.py @@ -0,0 +1,20 @@ +from cpl_core.database.context import DatabaseContextABC + +from bot_data.model.achievement_history import AchievementHistory +from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC + + +class AchievementQuery(DataQueryWithHistoryABC): + def __init__( + self, + db: DatabaseContextABC, + ): + DataQueryWithHistoryABC.__init__(self, "Achievement", "AchievementsHistory", AchievementHistory, db) + + self.set_field("id", lambda x, *_: x.id) + self.set_field("name", lambda x, *_: x.name) + self.set_field("description", lambda x, *_: x.description) + self.set_field("attribute", lambda x, *_: x.attribute) + self.set_field("operator", lambda x, *_: x.operator) + self.set_field("value", lambda x, *_: x.value) + self.set_field("server", lambda x, *_: x.server) diff --git a/kdb-bot/src/bot_graphql/queries/client_query.py b/kdb-bot/src/bot_graphql/queries/client_query.py index e5cd4c68..6e69a369 100644 --- a/kdb-bot/src/bot_graphql/queries/client_query.py +++ b/kdb-bot/src/bot_graphql/queries/client_query.py @@ -40,7 +40,7 @@ class ClientQuery(DataQueryWithHistoryABC): @staticmethod def resolve_received_message_count(client: Client, *_): - return client.received_command_count + return client.received_message_count @staticmethod def resolve_deleted_message_count(client: Client, *_): diff --git a/kdb-bot/src/bot_graphql/queries/discord/__init__.py b/kdb-bot/src/bot_graphql/queries/discord/__init__.py index 554d6cf3..758ed54d 100644 --- a/kdb-bot/src/bot_graphql/queries/discord/__init__.py +++ b/kdb-bot/src/bot_graphql/queries/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = "bot_graphql.queries.discord" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/bot_graphql/queries/discord/channel_query.py b/kdb-bot/src/bot_graphql/queries/discord/channel_query.py index 56807d5e..b40f1b07 100644 --- a/kdb-bot/src/bot_graphql/queries/discord/channel_query.py +++ b/kdb-bot/src/bot_graphql/queries/discord/channel_query.py @@ -1,10 +1,10 @@ -from bot_graphql.abc.data_query_abc import DataQueryABC +from bot_graphql.abc.query_abc import QueryABC -class ChannelQuery(DataQueryABC): +class ChannelQuery(QueryABC): def __init__(self): - DataQueryABC.__init__(self, "Channel") + QueryABC.__init__(self, "Channel") self.set_field("id", lambda c, *_: c.id) self.set_field("name", lambda c, *_: c.name) - self.set_field("type", lambda c, *_: type(c)) + self.set_field("type", lambda c, *_: type(c).__name__) diff --git a/kdb-bot/src/bot_graphql/queries/discord/discord_query.py b/kdb-bot/src/bot_graphql/queries/discord/discord_query.py new file mode 100644 index 00000000..a5b01f24 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/discord/discord_query.py @@ -0,0 +1,48 @@ +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List + +from bot_graphql.abc.query_abc import QueryABC + + +class DiscordQuery(QueryABC): + def __init__( + self, + bot: DiscordBotServiceABC, + ): + QueryABC.__init__(self, "Discord") + + self._bot = bot + + self.set_field("guilds", self._resolve_guilds) + self.set_field("users", self._resolve_users) + + def _resolve_guilds(self, *_, filter=None): + guilds = self._bot.guilds + + if filter is None: + return guilds + + if "id" in filter: + guilds = self._bot.guilds.where(lambda g: g.id == int(filter["id"])) + + if "name" in filter: + guilds = self._bot.guilds.where(lambda g: g.name == filter["name"]) + + return guilds + + def _resolve_users(self, *_, filter=None): + users = List(any).extend(self._bot.users) + + if filter is None: + return users + + if "id" in filter: + users = users.where(lambda g: g.id == int(filter["id"])) + + if "name" in filter: + users = users.where(lambda g: g.name == filter["name"]) + + if "bot" in filter: + users = users.where(lambda g: g.bot == bool(filter["bot"])) + + return users diff --git a/kdb-bot/src/bot_graphql/queries/discord/discord_user_query.py b/kdb-bot/src/bot_graphql/queries/discord/discord_user_query.py new file mode 100644 index 00000000..5a9b61f4 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/discord/discord_user_query.py @@ -0,0 +1,10 @@ +from bot_graphql.abc.query_abc import QueryABC + + +class DiscordUserQuery(QueryABC): + def __init__(self): + QueryABC.__init__(self, "DiscordUser") + + self.set_field("id", lambda r, *_: r.id) + self.set_field("name", lambda r, *_: r.name) + self.set_field("bot", lambda r, *_: r.bot) diff --git a/kdb-bot/src/bot_graphql/queries/discord/emoji_query.py b/kdb-bot/src/bot_graphql/queries/discord/emoji_query.py index 3c776a9d..853572d0 100644 --- a/kdb-bot/src/bot_graphql/queries/discord/emoji_query.py +++ b/kdb-bot/src/bot_graphql/queries/discord/emoji_query.py @@ -1,9 +1,9 @@ -from bot_graphql.abc.data_query_abc import DataQueryABC +from bot_graphql.abc.query_abc import QueryABC -class RoleQuery(DataQueryABC): +class EmojiQuery(QueryABC): def __init__(self): - DataQueryABC.__init__(self, "Emoji") + QueryABC.__init__(self, "Emoji") self.set_field("id", lambda e, *_: e.id) self.set_field("name", lambda e, *_: e.name) diff --git a/kdb-bot/src/bot_graphql/queries/discord/guild_query.py b/kdb-bot/src/bot_graphql/queries/discord/guild_query.py index f963e999..95b87b08 100644 --- a/kdb-bot/src/bot_graphql/queries/discord/guild_query.py +++ b/kdb-bot/src/bot_graphql/queries/discord/guild_query.py @@ -1,12 +1,38 @@ -from bot_graphql.abc.data_query_abc import DataQueryABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List +from discord import Guild + +from bot_graphql.abc.query_abc import QueryABC -class GuildQuery(DataQueryABC): - def __init__(self): - DataQueryABC.__init__(self, "Guild") +class GuildQuery(QueryABC): + def __init__( + self, + bot: DiscordBotServiceABC, + ): + QueryABC.__init__(self, "Guild") + + self._bot = bot self.set_field("id", lambda g, *_: g.id) self.set_field("name", lambda g, *_: g.name) - self.set_field("channels", lambda g, *_: g.channels) + self.set_field("channels", self._resolve_channels) self.set_field("roles", lambda g, *_: g.roles) self.set_field("emojis", lambda g, *_: g.emojis) + + def _resolve_channels(self, g: Guild, *_, filter=None): + users = List(any).extend(g.channels) + + if filter is None: + return users + + if "id" in filter: + users = users.where(lambda c: c.id == int(filter["id"])) + + if "name" in filter: + users = users.where(lambda c: c.id == filter["name"]) + + if "type" in filter: + users = users.where(lambda c: type(c).__name__ == filter["type"]) + + return users diff --git a/kdb-bot/src/bot_graphql/queries/discord/role_query.py b/kdb-bot/src/bot_graphql/queries/discord/role_query.py index 6880ed4f..f7efae9e 100644 --- a/kdb-bot/src/bot_graphql/queries/discord/role_query.py +++ b/kdb-bot/src/bot_graphql/queries/discord/role_query.py @@ -1,9 +1,9 @@ -from bot_graphql.abc.data_query_abc import DataQueryABC +from bot_graphql.abc.query_abc import QueryABC -class RoleQuery(DataQueryABC): +class RoleQuery(QueryABC): def __init__(self): - DataQueryABC.__init__(self, "Role") + QueryABC.__init__(self, "Role") self.set_field("id", lambda r, *_: r.id) self.set_field("name", lambda r, *_: r.name) diff --git a/kdb-bot/src/bot_graphql/queries/game_server_query.py b/kdb-bot/src/bot_graphql/queries/game_server_query.py new file mode 100644 index 00000000..8b04b3b9 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/game_server_query.py @@ -0,0 +1,10 @@ +from bot_graphql.abc.data_query_abc import DataQueryABC + + +class GameServerQuery(DataQueryABC): + def __init__(self): + DataQueryABC.__init__(self, "GameServer") + + self.set_field("id", lambda x, *_: x.id) + self.set_field("name", lambda x, *_: x.name) + self.set_field("server", lambda x, *_: x.server) diff --git a/kdb-bot/src/bot_graphql/queries/server_config_query.py b/kdb-bot/src/bot_graphql/queries/server_config_query.py new file mode 100644 index 00000000..73e61828 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/server_config_query.py @@ -0,0 +1,44 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_data.model.server_config_history import ServerConfigHistory +from bot_data.model.team_member_type_enum import TeamMemberTypeEnum +from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC + + +class ServerConfigQuery(DataQueryWithHistoryABC): + def __init__(self, db: DatabaseContextABC): + DataQueryWithHistoryABC.__init__(self, "ServerConfig", "CFG_ServerHistory", ServerConfigHistory, db) + + self.set_field("id", lambda config, *_: config.id) + self.set_field("messageDeleteTimer", lambda config, *_: config.message_delete_timer) + self.set_field("notificationChatId", lambda config, *_: config.notification_chat_id) + self.set_field("maxVoiceStateHours", lambda config, *_: config.max_voice_state_hours) + self.set_field("xpPerMessage", lambda config, *_: config.xp_per_message) + self.set_field("xpPerReaction", lambda config, *_: config.xp_per_reaction) + self.set_field("maxMessageXpPerHour", lambda config, *_: config.max_message_xp_per_hour) + self.set_field("xpPerOntimeHour", lambda config, *_: config.xp_per_ontime_hour) + self.set_field("xpPerEventParticipation", lambda config, *_: config.xp_per_event_participation) + self.set_field("xpPerAchievement", lambda config, *_: config.xp_per_achievement) + self.set_field("afkCommandChannelId", lambda config, *_: config.afk_command_channel_id) + self.set_field("helpVoiceChannelId", lambda config, *_: config.help_voice_channel_id) + self.set_field("teamChannelId", lambda config, *_: config.team_channel_id) + self.set_field("loginMessageChannelId", lambda config, *_: config.login_message_channel_id) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) + self.set_field("afkChannelIds", lambda config, *_: config.afk_channel_ids) + self.set_field( + "moderatorRoleIds", + lambda config, *_: config.team_role_ids.where( + lambda x: x.team_member_type == TeamMemberTypeEnum.moderator + ).select(lambda x: x.role_id), + ) + self.set_field( + "adminRoleIds", + lambda config, *_: config.team_role_ids.where( + lambda x: x.team_member_type == TeamMemberTypeEnum.admin + ).select(lambda x: x.role_id), + ) + self.set_field("server", lambda config, *_: config.server) diff --git a/kdb-bot/src/bot_graphql/queries/server_query.py b/kdb-bot/src/bot_graphql/queries/server_query.py index 39012d49..08c686b5 100644 --- a/kdb-bot/src/bot_graphql/queries/server_query.py +++ b/kdb-bot/src/bot_graphql/queries/server_query.py @@ -1,16 +1,19 @@ from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.game_server_repository_abc import GameServerRepositoryABC from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.server import Server from bot_data.model.server_history import ServerHistory -from bot_graphql.abc.data_query_abc import DataQueryABC from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC +from bot_graphql.filter.achievement_filter import AchievementFilter from bot_graphql.filter.auto_role_filter import AutoRoleFilter from bot_graphql.filter.client_filter import ClientFilter from bot_graphql.filter.level_filter import LevelFilter @@ -25,9 +28,12 @@ class ServerQuery(DataQueryWithHistoryABC): auto_roles: AutoRoleRepositoryABC, clients: ClientRepositoryABC, levels: LevelRepositoryABC, + game_servers: GameServerRepositoryABC, users: UserRepositoryABC, ujs: UserJoinedServerRepositoryABC, ujvs: UserJoinedVoiceChannelRepositoryABC, + achievements: AchievementRepositoryABC, + server_configs: ServerConfigRepositoryABC, ): DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db) @@ -54,6 +60,11 @@ class ServerQuery(DataQueryWithHistoryABC): ) self.add_collection("level", lambda server, *_: self._levels.get_levels_by_server_id(server.id), LevelFilter) self.add_collection("user", lambda server, *_: self._users.get_users_by_server_id(server.id), UserFilter) + self.add_collection("gameServer", lambda server, *_: game_servers.get_game_servers_by_server_id(server.id)) + self.add_collection( + "achievement", lambda server, *_: achievements.get_achievements_by_server_id(server.id), AchievementFilter + ) + self.set_field("config", lambda server, *_: server_configs.get_server_config_by_server(server.id)) @staticmethod def resolve_id(server: Server, *_): diff --git a/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py b/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py new file mode 100644 index 00000000..a63f88f8 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py @@ -0,0 +1,17 @@ +from cpl_query.extension import List + +from bot_graphql.abc.history_query_abc import HistoryQueryABC + + +class TechnicianConfigHistoryQuery(HistoryQueryABC): + def __init__(self): + HistoryQueryABC.__init__(self, "TechnicianConfig") + + self.set_field("helpCommandReferenceUrl", lambda config, *_: config.help_command_reference_url) + self.set_field("waitForRestart", lambda config, *_: config.wait_for_restart) + self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) + self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) diff --git a/kdb-bot/src/bot_graphql/queries/technician_config_query.py b/kdb-bot/src/bot_graphql/queries/technician_config_query.py new file mode 100644 index 00000000..2d8bb8f9 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/technician_config_query.py @@ -0,0 +1,61 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_data.abc.table_with_id_abc import TableWithIdABC +from bot_data.model.technician_config_history import TechnicianConfigHistory +from bot_data.model.technician_id_config_history import TechnicianIdConfigHistory +from bot_data.model.technician_ping_url_config_history import TechnicianPingUrlConfigHistory +from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC + + +class TechnicianConfigQuery(DataQueryWithHistoryABC): + def __init__(self, db: DatabaseContextABC): + DataQueryWithHistoryABC.__init__(self, "TechnicianConfig", "CFG_TechnicianHistory", TechnicianConfigHistory, db) + + self.set_field("helpCommandReferenceUrl", lambda config, *_: config.help_command_reference_url) + self.set_field("waitForRestart", lambda config, *_: config.wait_for_restart) + self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) + self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) + self.set_field("pingURLs", lambda config, *_: config.ping_urls) + self.set_field("technicianIds", lambda config, *_: config.technician_ids) + + self.set_field("pingURLHistory", self.resolve_ping_url_history) + self.set_field("technicianIdHistory", self.resolve_technician_id_history) + + def resolve_ping_url_history(self, entry: TableWithIdABC, *_): + history = List(TechnicianPingUrlConfigHistory) + + results = self._db.select( + f""" + SELECT * + FROM CFG_TechnicianPingUrlsHistory + WHERE Id = {entry.id} + ORDER BY DateTo DESC; + """ + ) + + for result in results: + history.add(TechnicianPingUrlConfigHistory(*result[1:], result[0])) + + return history + + def resolve_technician_id_history(self, entry: TableWithIdABC, *_): + history = List(TechnicianIdConfigHistory) + + results = self._db.select( + f""" + SELECT * + FROM CFG_TechnicianIdsHistory + WHERE Id = {entry.id} + ORDER BY DateTo DESC; + """ + ) + + for result in results: + history.add(TechnicianIdConfigHistory(*result[1:], result[0])) + + return history diff --git a/kdb-bot/src/bot_graphql/queries/technician_id_config_history_query.py b/kdb-bot/src/bot_graphql/queries/technician_id_config_history_query.py new file mode 100644 index 00000000..6ab0a3a3 --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/technician_id_config_history_query.py @@ -0,0 +1,8 @@ +from bot_graphql.abc.history_query_abc import HistoryQueryABC + + +class TechnicianIdConfigHistoryQuery(HistoryQueryABC): + def __init__(self): + HistoryQueryABC.__init__(self, "TechnicianIdConfig") + + self.set_field("technicianId", lambda config, *_: config.technicianId) diff --git a/kdb-bot/src/bot_graphql/queries/technician_ping_url_config_history_query.py b/kdb-bot/src/bot_graphql/queries/technician_ping_url_config_history_query.py new file mode 100644 index 00000000..4be66b6c --- /dev/null +++ b/kdb-bot/src/bot_graphql/queries/technician_ping_url_config_history_query.py @@ -0,0 +1,8 @@ +from bot_graphql.abc.history_query_abc import HistoryQueryABC + + +class TechnicianPingUrlConfigHistoryQuery(HistoryQueryABC): + def __init__(self): + HistoryQueryABC.__init__(self, "TechnicianPingUrlConfig") + + self.set_field("url", lambda config, *_: config.ping_url) diff --git a/kdb-bot/src/bot_graphql/queries/user_query.py b/kdb-bot/src/bot_graphql/queries/user_query.py index 9e924f5f..9bb4a48b 100644 --- a/kdb-bot/src/bot_graphql/queries/user_query.py +++ b/kdb-bot/src/bot_graphql/queries/user_query.py @@ -2,12 +2,14 @@ from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC from bot_core.abc.client_utils_abc import ClientUtilsABC +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.model.user import User from bot_data.model.user_history import UserHistory from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC +from bot_graphql.filter.achievement_filter import AchievementFilter from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter @@ -26,6 +28,7 @@ class UserQuery(DataQueryWithHistoryABC): ujvs: UserJoinedVoiceChannelRepositoryABC, user_joined_game_server: UserJoinedGameServerRepositoryABC, permissions: PermissionServiceABC, + achievements: AchievementRepositoryABC, ): DataQueryWithHistoryABC.__init__(self, "User", "UsersHistory", UserHistory, db) @@ -36,11 +39,14 @@ class UserQuery(DataQueryWithHistoryABC): self._ujs = ujs self._ujvs = ujvs self._permissions = permissions + self._achievements = achievements self.set_field("id", self.resolve_id) self.set_field("discordId", self.resolve_discord_id) self.set_field("name", self.resolve_name) self.set_field("xp", self.resolve_xp) + self.set_field("messageCount", lambda x, *_: x.message_count) + self.set_field("reactionCount", lambda x, *_: x.reaction_count) self.set_field("ontime", self.resolve_ontime) self.set_field("level", self.resolve_level) self.add_collection( @@ -58,6 +64,10 @@ class UserQuery(DataQueryWithHistoryABC): lambda user, *_: self._user_joined_game_server.get_user_joined_game_servers_by_user_id(user.id), UserJoinedGameServerFilter, ) + self.add_collection( + "achievement", lambda user, *_: achievements.get_achievements_by_user_id(user.id), AchievementFilter + ) + self.set_field("server", self.resolve_server) self.set_field("leftServer", self.resolve_left_server) diff --git a/kdb-bot/src/bot_graphql/query.py b/kdb-bot/src/bot_graphql/query.py index 8133e4dc..69e5553e 100644 --- a/kdb-bot/src/bot_graphql/query.py +++ b/kdb-bot/src/bot_graphql/query.py @@ -1,15 +1,21 @@ from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.game_server_repository_abc import GameServerRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_graphql.abc.query_abc import QueryABC +from bot_graphql.filter.achievement_filter import AchievementFilter from bot_graphql.filter.auto_role_filter import AutoRoleFilter from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter from bot_graphql.filter.client_filter import ClientFilter @@ -19,6 +25,8 @@ from bot_graphql.filter.user_filter import UserFilter from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter +from bot_graphql.model.discord import Discord +from modules.achievements.achievement_service import AchievementService class Query(QueryABC): @@ -30,21 +38,24 @@ class Query(QueryABC): known_users: KnownUserRepositoryABC, levels: LevelRepositoryABC, servers: ServerRepositoryABC, + game_servers: GameServerRepositoryABC, user_joined_servers: UserJoinedServerRepositoryABC, user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC, user_joined_game_server: UserJoinedGameServerRepositoryABC, users: UserRepositoryABC, + achievements: AchievementRepositoryABC, + achievement_service: AchievementService, + technician_config: TechnicianConfigRepositoryABC, ): QueryABC.__init__(self, "Query") - self._bot = bot - self.add_collection("autoRole", lambda *_: auto_roles.get_auto_roles(), AutoRoleFilter) self.add_collection("autoRoleRule", lambda *_: auto_roles.get_auto_role_rules(), AutoRoleRuleFilter) self.add_collection("client", lambda *_: clients.get_clients(), ClientFilter) self.add_collection("knownUser", lambda *_: known_users.get_users()) self.add_collection("level", lambda *_: levels.get_levels(), LevelFilter) self.add_collection("server", lambda *_: servers.get_servers(), ServerFilter) + self.add_collection("gameServer", lambda *_: game_servers.get_game_servers()) self.add_collection( "userJoinedServer", lambda *_: user_joined_servers.get_user_joined_servers(), UserJoinedServerFilter ) @@ -59,11 +70,10 @@ class Query(QueryABC): UserJoinedGameServerFilter, ) self.add_collection("user", lambda *_: users.get_users(), UserFilter) + self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter) + self.set_field("technicianConfig", lambda *_: technician_config.get_technician_config()) - self.set_field("guilds", self._resolve_guilds) - - def _resolve_guilds(self, *_, filter=None): - if filter is None or "id" not in filter: - return self._bot.guilds - - return self._bot.guilds.where(lambda g: g.id == int(filter["id"])) + self.set_field("achievementAttributes", lambda *_: achievement_service.get_attributes()) + self.set_field("achievementOperators", lambda *_: achievement_service.get_operators()) + self.set_field("possibleFeatureFlags", lambda *_: [e.value for e in FeatureFlagsEnum]) + self.set_field("discord", lambda *_: Discord(bot.guilds, List(any).extend(bot.users))) diff --git a/kdb-bot/src/bot_graphql/schema.py b/kdb-bot/src/bot_graphql/schema.py index d2972a78..020efbc8 100644 --- a/kdb-bot/src/bot_graphql/schema.py +++ b/kdb-bot/src/bot_graphql/schema.py @@ -10,7 +10,7 @@ from bot_graphql.query import Query class Schema: def __init__(self, query: Query, mutation: Mutation, queries: list[QueryABC]): - type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "model/")) + type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "graphql/")) self._schema = make_executable_schema(type_defs, query, mutation, *queries) @property diff --git a/kdb-bot/src/modules/base/configuration/__init__.py b/kdb-bot/src/modules/achievements/__init__.py similarity index 75% rename from kdb-bot/src/modules/base/configuration/__init__.py rename to kdb-bot/src/modules/achievements/__init__.py index bbec5538..94b4d74a 100644 --- a/kdb-bot/src/modules/base/configuration/__init__.py +++ b/kdb-bot/src/modules/achievements/__init__.py @@ -11,16 +11,16 @@ Discord bot for the Keksdose discord Server """ -__title__ = "modules.base.configuration" +__title__ = "modules.achievements" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple -# imports: +# imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/achievements/achievement_attribute_resolver.py b/kdb-bot/src/modules/achievements/achievement_attribute_resolver.py new file mode 100644 index 00000000..f08f1579 --- /dev/null +++ b/kdb-bot/src/modules/achievements/achievement_attribute_resolver.py @@ -0,0 +1,50 @@ +from typing import List + +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.game_server_repository_abc import GameServerRepositoryABC +from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.user import User + + +class AchievementAttributeResolver: + def __init__( + self, + auto_roles: AutoRoleRepositoryABC, + clients: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + levels: LevelRepositoryABC, + servers: ServerRepositoryABC, + game_servers: GameServerRepositoryABC, + user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC, + user_joined_game_server: UserJoinedGameServerRepositoryABC, + users: UserRepositoryABC, + achievements: AchievementRepositoryABC, + ): + self._auto_roles = auto_roles + self._clients = clients + self._known_users = known_users + self._levels = levels + self._servers = servers + self._game_servers = game_servers + self._user_joined_servers = user_joined_servers + self._user_joined_voice_channels = user_joined_voice_channels + self._user_joined_game_server = user_joined_game_server + self._users = users + self._achievements = achievements + + def get_played_on_game_server(self, user: User) -> List[str]: + joins = self._user_joined_game_server.get_user_joined_game_servers_by_user_id(user.id) + return joins.select(lambda x: x.game_server.name) + + def get_last_ontime_hours(self, user: User) -> int: + ujvs = self._user_joined_voice_channels.get_user_joined_voice_channels_by_user_id(user.id) + return int(str(ujvs.max(lambda join: (join.leaved_on - join.joined_on).total_seconds() / 3600))) diff --git a/kdb-bot/src/modules/achievements/achievement_service.py b/kdb-bot/src/modules/achievements/achievement_service.py new file mode 100644 index 00000000..d7b2a455 --- /dev/null +++ b/kdb-bot/src/modules/achievements/achievement_service.py @@ -0,0 +1,122 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List +from cpl_translation import TranslatePipe + +from bot_core.service.message_service import MessageService +from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.achievement import Achievement +from bot_data.model.server_config import ServerConfig +from bot_data.model.user import User +from bot_data.model.user_got_achievement import UserGotAchievement +from modules.achievements.achievement_attribute_resolver import AchievementAttributeResolver +from modules.achievements.model.achievement_attribute import AchievementAttribute + + +class AchievementService: + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + achievements: AchievementRepositoryABC, + users: UserRepositoryABC, + db: DatabaseContextABC, + message_service: MessageService, + resolver: AchievementAttributeResolver, + t: TranslatePipe, + ): + self._config = config + self._logger = logger + self._bot = bot + self._achievements = achievements + self._users = users + self._db = db + self._message_service = message_service + self._t = t + + self._attributes = List(AchievementAttribute) + + self._attributes.extend( + [ + AchievementAttribute("xp", lambda user: user.xp, "number"), + AchievementAttribute("message_count", lambda user: user.message_count, "number"), + AchievementAttribute("reaction_count", lambda user: user.reaction_count, "number"), + AchievementAttribute("ontime", lambda user: user.ontime, "number"), + AchievementAttribute("level", lambda user: user.level, "Level"), + # special cases + AchievementAttribute( + "played_on_game_server", lambda user: resolver.get_played_on_game_server(user), "GameServer" + ), + AchievementAttribute( + "last_single_ontime_hours", lambda user: resolver.get_last_ontime_hours(user), "number" + ), + ] + ) + + self._operators = { + "==": lambda value, expected_value: value == expected_value, + "!=": lambda value, expected_value: value != expected_value, + "<=": lambda value, expected_value: self._num(value) <= self._num(expected_value), + ">=": lambda value, expected_value: self._num(value) >= self._num(expected_value), + "<": lambda value, expected_value: self._num(value) < self._num(expected_value), + ">": lambda value, expected_value: self._num(value) > self._num(expected_value), + "contains": lambda value, expected_value: expected_value in value, + } + + @staticmethod + def _num(s: str): + try: + return int(s) + except ValueError: + return float(s) + + def add_achievement_attribute(self, atr: AchievementAttribute): + self._attributes.add(atr) + + def get_operators(self) -> list[str]: + return [x for x in self._operators.keys()] + + def get_attributes(self) -> List[AchievementAttribute]: + return self._attributes + + def _match(self, value: any, operator: str, expected_value: str) -> bool: + return self._operators[operator](str(value), expected_value) + + def has_user_achievement_already(self, user: User, achievement: Achievement) -> bool: + user_achievements = self._achievements.get_achievements_by_user_id(user.id) + return user_achievements.where(lambda x: x.name == achievement.name).count() > 0 + + def has_user_achievement(self, user: User, achievement: Achievement) -> bool: + attribute: AchievementAttribute = self._attributes.where(lambda x: x.name == achievement.attribute).single() + return self._match(attribute.resolve(user), achievement.operator, achievement.value) + + async def validate_achievements_for_user(self, user: User): + achievements = self._achievements.get_achievements_by_server_id(user.server.id) + for achievement in achievements: + if self.has_user_achievement_already(user, achievement) or not self.has_user_achievement(user, achievement): + continue + + self._achievements.add_user_got_achievement(UserGotAchievement(user, achievement, user.server)) + self._db.save_changes() + self._give_user_xp(user) + await self._send_achievement_notification(user.server.discord_id, user.discord_id, achievement.name) + + def _give_user_xp(self, user: User): + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{user.server.discord_id}") + user.xp += settings.xp_per_achievement + self._users.update_user(user) + self._db.save_changes() + + async def _send_achievement_notification(self, guild_id: int, member_id: int, achievement_name: str): + member = self._bot.get_guild(guild_id).get_member(member_id) + + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild_id}") + await self._message_service.send_channel_message( + self._bot.get_channel(settings.notification_chat_id), + self._t.transform("modules.achievements.got_new_achievement").format(member.mention, achievement_name), + is_persistent=True, + ) diff --git a/kdb-bot/src/modules/achievements/achievements.json b/kdb-bot/src/modules/achievements/achievements.json new file mode 100644 index 00000000..14c3ad62 --- /dev/null +++ b/kdb-bot/src/modules/achievements/achievements.json @@ -0,0 +1,44 @@ +{ + "ProjectSettings": { + "Name": "achievements", + "Version": { + "Major": "1", + "Minor": "1", + "Micro": "0" + }, + "Author": "Sven Heidemann", + "AuthorEmail": "sven.heidemann@sh-edraft.de", + "Description": "Keksdose bot - achievements", + "LongDescription": "Discord bot for the Keksdose discord Server - achievements module", + "URL": "https://www.sh-edraft.de", + "CopyrightDate": "2023", + "CopyrightName": "sh-edraft.de", + "LicenseName": "MIT", + "LicenseDescription": "MIT, see LICENSE for more details.", + "Dependencies": [ + "cpl-core>=1.1.0" + ], + "DevDependencies": [ + "cpl-cli>=1.1.0" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": {}, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "achievements.main", + "EntryPoint": "achievements", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/achievements/achievements_module.py b/kdb-bot/src/modules/achievements/achievements_module.py new file mode 100644 index 00000000..3860feac --- /dev/null +++ b/kdb-bot/src/modules/achievements/achievements_module.py @@ -0,0 +1,34 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.achievements.achievement_attribute_resolver import AchievementAttributeResolver +from modules.achievements.achievement_service import AchievementService +from modules.achievements.commands.achievements_group import AchievementGroup +from modules.achievements.events.achievement_on_message_event import AchievementOnMessageEvent +from modules.achievements.events.achievement_on_reaction_add_event import AchievementOnReactionAddEvent +from modules.achievements.events.achievement_on_reaction_remove_event import AchievementOnReactionRemoveEvent +from modules.achievements.events.achievement_on_voice_state_update_event import AchievementOnVoiceStateUpdateEvent + + +class AchievementsModule(ModuleABC): + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.achievements_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(AchievementAttributeResolver) + services.add_transient(AchievementService) + + self._dc.add_command(AchievementGroup) + + self._dc.add_event(DiscordEventTypesEnum.on_message.value, AchievementOnMessageEvent) + self._dc.add_event(DiscordEventTypesEnum.on_reaction_add.value, AchievementOnReactionAddEvent) + self._dc.add_event(DiscordEventTypesEnum.on_reaction_remove.value, AchievementOnReactionRemoveEvent) + self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, AchievementOnVoiceStateUpdateEvent) diff --git a/kdb-bot/src/modules/boot_log/configuration/__init__.py b/kdb-bot/src/modules/achievements/commands/__init__.py similarity index 74% rename from kdb-bot/src/modules/boot_log/configuration/__init__.py rename to kdb-bot/src/modules/achievements/commands/__init__.py index cdde1580..582f74bb 100644 --- a/kdb-bot/src/modules/boot_log/configuration/__init__.py +++ b/kdb-bot/src/modules/achievements/commands/__init__.py @@ -11,16 +11,16 @@ Discord bot for the Keksdose discord Server """ -__title__ = "modules.boot_log.configuration" +__title__ = "modules.achievements.commands" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple -# imports: +# imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/achievements/commands/achievements_group.py b/kdb-bot/src/modules/achievements/commands/achievements_group.py new file mode 100644 index 00000000..a0c73ccb --- /dev/null +++ b/kdb-bot/src/modules/achievements/commands/achievements_group.py @@ -0,0 +1,71 @@ +import discord +from cpl_core.configuration import ConfigurationABC +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.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.helper.command_checks import CommandChecks +from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig +from modules.achievements.achievement_service import AchievementService + + +class AchievementGroup(DiscordCommandABC): + def __init__( + self, + config: ConfigurationABC, + logger: CommandLogger, + message_service: MessageServiceABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + achievement_service: AchievementService, + translate: TranslatePipe, + ): + DiscordCommandABC.__init__(self) + + self._config = config + self._logger = logger + self._message_service = message_service + self._bot = bot + self._servers = servers + self._users = users + self._achievement_service = achievement_service + self._t = translate + + @commands.hybrid_group() + @commands.guild_only() + async def achievement(self, ctx: Context): + pass + + @achievement.command() + @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() + async def check(self, ctx: Context, member: discord.Member): + self._logger.debug(__name__, f"Received command achievement check {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + + server = self._servers.get_server_by_discord_id(member.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) + await self._message_service.send_ctx_msg( + ctx, + self._t.transform("modules.achievements.commands.check"), + ) + await self._achievement_service.validate_achievements_for_user(user) + + self._logger.trace(__name__, f"Finished command achievement check") diff --git a/kdb-bot/src/modules/achievements/events/__init__.py b/kdb-bot/src/modules/achievements/events/__init__.py new file mode 100644 index 00000000..05a63192 --- /dev/null +++ b/kdb-bot/src/modules/achievements/events/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 - 2023 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = "modules.achievements.events" +__author__ = "Sven Heidemann" +__license__ = "MIT" +__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" +__version__ = "1.1.0" + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple("VersionInfo", "major minor micro") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py b/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py new file mode 100644 index 00000000..b3564ccf --- /dev/null +++ b/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py @@ -0,0 +1,59 @@ +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnMessageABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig +from modules.achievements.achievement_service import AchievementService + + +class AchievementOnMessageEvent(OnMessageABC): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + achievements: AchievementService, + db: DatabaseContextABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + ): + OnMessageABC.__init__(self) + + self._config = config + self._logger = logger + self._bot = bot + self._achievements = achievements + self._db = db + self._servers = servers + self._users = users + + @EventChecks.check_is_ready() + async def on_message(self, message: discord.Message): + if message.guild is None: + return + + if message.author.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + + server = self._servers.get_server_by_discord_id(message.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(message.author.id, server.id) + + user.message_count += 1 + self._users.update_user(user) + self._db.save_changes() + + await self._achievements.validate_achievements_for_user(user) diff --git a/kdb-bot/src/modules/achievements/events/achievement_on_reaction_add_event.py b/kdb-bot/src/modules/achievements/events/achievement_on_reaction_add_event.py new file mode 100644 index 00000000..b033b534 --- /dev/null +++ b/kdb-bot/src/modules/achievements/events/achievement_on_reaction_add_event.py @@ -0,0 +1,62 @@ +from typing import Union + +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnReactionAddABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig +from modules.achievements.achievement_service import AchievementService + + +class AchievementOnReactionAddEvent(OnReactionAddABC): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + achievements: AchievementService, + db: DatabaseContextABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + ): + OnReactionAddABC.__init__(self) + + self._config = config + self._logger = logger + self._bot = bot + self._achievements = achievements + self._db = db + self._servers = servers + self._users = users + + async def on_reaction_add(self, reaction: discord.reaction.Reaction, user: Union[discord.Member, discord.User]): + if not isinstance(user, discord.Member): + return + + if user.guild is None: + return + + if user.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{user.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + + server = self._servers.get_server_by_discord_id(user.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(user.id, server.id) + + user.reaction_count += 1 + self._db.save_changes() + self._users.update_user(user) + + await self._achievements.validate_achievements_for_user(user) diff --git a/kdb-bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py b/kdb-bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py new file mode 100644 index 00000000..bbb33f28 --- /dev/null +++ b/kdb-bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py @@ -0,0 +1,62 @@ +from typing import Union + +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnReactionRemoveABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig +from modules.achievements.achievement_service import AchievementService + + +class AchievementOnReactionRemoveEvent(OnReactionRemoveABC): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + achievements: AchievementService, + db: DatabaseContextABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + ): + OnReactionRemoveABC.__init__(self) + + self._config = config + self._logger = logger + self._bot = bot + self._achievements = achievements + self._db = db + self._servers = servers + self._users = users + + async def on_reaction_remove(self, reaction: discord.reaction.Reaction, user: Union[discord.Member, discord.User]): + if not isinstance(user, discord.Member): + return + + if user.guild is None: + return + + if user.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{user.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + + server = self._servers.get_server_by_discord_id(user.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(user.id, server.id) + + user.reaction_count -= 1 + self._db.save_changes() + self._users.update_user(user) + + await self._achievements.validate_achievements_for_user(user) diff --git a/kdb-bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py b/kdb-bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py new file mode 100644 index 00000000..2d9417a1 --- /dev/null +++ b/kdb-bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py @@ -0,0 +1,55 @@ +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnVoiceStateUpdateABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig +from modules.achievements.achievement_service import AchievementService + + +class AchievementOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + achievements: AchievementService, + db: DatabaseContextABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + ): + OnVoiceStateUpdateABC.__init__(self) + + self._config = config + self._logger = logger + self._bot = bot + self._achievements = achievements + self._db = db + self._servers = servers + self._users = users + + async def on_voice_state_update( + self, member: discord.member.Member, before: discord.member.VoiceState, after: discord.member.VoiceState + ): + if member.guild is None: + return + + if member.author.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + + server = self._servers.get_server_by_discord_id(member.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) + + await self._achievements.validate_achievements_for_user(user) diff --git a/kdb-bot/src/modules/achievements/model/__init__.py b/kdb-bot/src/modules/achievements/model/__init__.py new file mode 100644 index 00000000..94f78b11 --- /dev/null +++ b/kdb-bot/src/modules/achievements/model/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 - 2023 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = "modules.achievements.model" +__author__ = "Sven Heidemann" +__license__ = "MIT" +__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" +__version__ = "1.1.0" + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple("VersionInfo", "major minor micro") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/achievements/model/achievement_attribute.py b/kdb-bot/src/modules/achievements/model/achievement_attribute.py new file mode 100644 index 00000000..c90d4722 --- /dev/null +++ b/kdb-bot/src/modules/achievements/model/achievement_attribute.py @@ -0,0 +1,20 @@ +from typing import Callable + + +class AchievementAttribute: + # frontend type = TypeScript types + def __init__(self, name: str, resolver: Callable, frontend_type: str): + self._name = name + self._resolver = resolver + self._frontend_type = frontend_type + + @property + def name(self) -> str: + return self._name + + @property + def type(self) -> str: + return self._frontend_type + + def resolve(self, *args, **kwargs): + return self._resolver(*args, **kwargs) diff --git a/kdb-bot/src/modules/auto_role/__init__.py b/kdb-bot/src/modules/auto_role/__init__.py index fc32080e..50b27bcf 100644 --- a/kdb-bot/src/modules/auto_role/__init__.py +++ b/kdb-bot/src/modules/auto_role/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.auto_role" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/auto_role/auto-role.json b/kdb-bot/src/modules/auto_role/auto-role.json index 10d46ef4..4f93f5d0 100644 --- a/kdb-bot/src/modules/auto_role/auto-role.json +++ b/kdb-bot/src/modules/auto_role/auto-role.json @@ -3,8 +3,8 @@ "Name": "auto-role", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/auto_role/command/__init__.py b/kdb-bot/src/modules/auto_role/command/__init__.py index d6c3e020..558fcec3 100644 --- a/kdb-bot/src/modules/auto_role/command/__init__.py +++ b/kdb-bot/src/modules/auto_role/command/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.auto_role.command" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 3969aea9..7cf36c5d 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -1,6 +1,7 @@ from typing import List as TList 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 @@ -12,18 +13,22 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role_rule import AutoRoleRule +from bot_data.model.server_config import ServerConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC class AutoRoleGroup(DiscordCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, message_service: MessageServiceABC, bot: DiscordBotServiceABC, @@ -36,6 +41,7 @@ class AutoRoleGroup(DiscordCommandABC): ): DiscordCommandABC.__init__(self) + self._config = config self._logger = logger self._message_service = message_service self._bot = bot @@ -69,10 +75,13 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f"Received command auto-role list {ctx}") - if ctx.guild is None: return + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return + embed = discord.Embed( title=self._t.transform("modules.auto_role.list.title"), description=self._t.transform("modules.auto_role.list.description"), @@ -110,6 +119,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, channel: discord.TextChannel, message_id: str): self._logger.debug(__name__, f"Received command auto-role add {ctx} {message_id}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return message = ( List( @@ -170,6 +185,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role: int): self._logger.debug(__name__, f"Received command auto-role remove {ctx} {auto_role}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return auto_role_from_db = self._auto_roles.find_auto_role_by_id(auto_role) if auto_role_from_db is None: @@ -210,6 +231,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, auto_role: int, wait: int = None): self._logger.debug(__name__, f"Received command auto-role rule list {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return embed = discord.Embed( title=self._t.transform("modules.auto_role.list.title"), @@ -262,6 +289,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, auto_role: int, emoji_name: str, role_id: str): self._logger.debug(__name__, f"Received command auto-role add {ctx} {auto_role}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return emoji = discord.utils.get(self._bot.emojis, name=emoji_name) if emoji is None: @@ -354,6 +387,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role_rule: int): self._logger.debug(__name__, f"Received command auto-role remove {ctx} {auto_role_rule}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return auto_role_from_db = self._auto_roles.get_auto_role_rule_by_id(auto_role_rule) if auto_role_from_db is None: diff --git a/kdb-bot/src/modules/auto_role/events/__init__.py b/kdb-bot/src/modules/auto_role/events/__init__.py index 0891f624..405801d8 100644 --- a/kdb-bot/src/modules/auto_role/events/__init__.py +++ b/kdb-bot/src/modules/auto_role/events/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.auto_role.events" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index 72566c40..f5d14804 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -1,17 +1,22 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, @@ -20,6 +25,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): ): OnRawReactionAddABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._servers = servers @@ -29,6 +35,9 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): @EventChecks.check_is_ready() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return await self._reaction_handler.handle(payload, "add") diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index 394c9578..a174c8f5 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -1,17 +1,22 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, @@ -20,6 +25,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): ): OnRawReactionRemoveABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._servers = servers @@ -29,6 +35,9 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @EventChecks.check_is_ready() async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return await self._reaction_handler.handle(payload, "remove") diff --git a/kdb-bot/src/modules/auto_role/helper/__init__.py b/kdb-bot/src/modules/auto_role/helper/__init__.py index bbeee625..49e110ec 100644 --- a/kdb-bot/src/modules/auto_role/helper/__init__.py +++ b/kdb-bot/src/modules/auto_role/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.auto_role.helper" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/__init__.py b/kdb-bot/src/modules/base/__init__.py index 9d471777..d70d78dc 100644 --- a/kdb-bot/src/modules/base/__init__.py +++ b/kdb-bot/src/modules/base/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/abc/base_helper_abc.py b/kdb-bot/src/modules/base/abc/base_helper_abc.py deleted file mode 100644 index c62fe210..00000000 --- a/kdb-bot/src/modules/base/abc/base_helper_abc.py +++ /dev/null @@ -1,12 +0,0 @@ -from abc import abstractmethod, ABC - -from modules.base.configuration.base_server_settings import BaseServerSettings - - -class BaseHelperABC(ABC): - def __init__(self): - ABC.__init__(self) - - @abstractmethod - def get_config(self, g_id: int) -> BaseServerSettings: - pass diff --git a/kdb-bot/src/modules/base/base.json b/kdb-bot/src/modules/base/base.json index bba47173..458f1bb6 100644 --- a/kdb-bot/src/modules/base/base.json +++ b/kdb-bot/src/modules/base/base.json @@ -3,8 +3,8 @@ "Name": "base", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 4d8c4f2d..4c4c5e5f 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -6,16 +6,17 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum -from modules.base.abc.base_helper_abc import BaseHelperABC from modules.base.command.afk_command import AFKCommand from modules.base.command.game_server_group import GameServerGroup from modules.base.command.help_command import HelpCommand from modules.base.command.info_command import InfoCommand +from modules.base.command.make_coffee_command import MakeCoffeeCommand from modules.base.command.mass_move_command import MassMoveCommand from modules.base.command.ping_command import PingCommand from modules.base.command.presence_command import PresenceCommand from modules.base.command.purge_command import PurgeCommand from modules.base.command.register_group import RegisterGroup +from modules.base.command.submit_group import SubmitGroup from modules.base.command.unregister_group import UnregisterGroup from modules.base.command.user_group import UserGroup from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent @@ -36,8 +37,9 @@ from modules.base.events.base_on_voice_state_update_event_help_channel import ( from modules.base.events.base_on_voice_state_update_event_scheduled_event_bonus import ( BaseOnVoiceStateUpdateEventScheduledEventBonus, ) +from modules.base.forms.bug_report_form import BugReportForm +from modules.base.forms.complaint_form import ComplaintForm from modules.base.helper.base_reaction_handler import BaseReactionHandler -from modules.base.service.base_helper_service import BaseHelperService from modules.base.service.event_service import EventService from modules.base.service.user_warnings_service import UserWarningsService @@ -50,18 +52,23 @@ class BaseModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - services.add_transient(BaseHelperABC, BaseHelperService) services.add_transient(BaseReactionHandler) services.add_singleton(EventService) services.add_transient(UserWarningsService) + # forms + services.add_transient(BugReportForm) + services.add_transient(ComplaintForm) + # commands self._dc.add_command(AFKCommand) + self._dc.add_command(SubmitGroup) self._dc.add_command(HelpCommand) self._dc.add_command(InfoCommand) self._dc.add_command(MassMoveCommand) self._dc.add_command(PingCommand) self._dc.add_command(PresenceCommand) + self._dc.add_command(MakeCoffeeCommand) self._dc.add_command(PurgeCommand) self._dc.add_command(UserGroup) diff --git a/kdb-bot/src/modules/base/command/__init__.py b/kdb-bot/src/modules/base/command/__init__.py index bee4e881..1c43a7c7 100644 --- a/kdb-bot/src/modules/base/command/__init__.py +++ b/kdb-bot/src/modules/base/command/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.command" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/command/afk_command.py b/kdb-bot/src/modules/base/command/afk_command.py index 7d05f712..092c9788 100644 --- a/kdb-bot/src/modules/base/command/afk_command.py +++ b/kdb-bot/src/modules/base/command/afk_command.py @@ -10,7 +10,7 @@ from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.server_config import ServerConfig class AFKCommand(DiscordCommandABC): @@ -39,7 +39,7 @@ class AFKCommand(DiscordCommandABC): @CommandChecks.check_is_ready() async def afk(self, ctx: Context): self._logger.debug(__name__, f"Received command afk {ctx}") - settings: BaseServerSettings = self._config.get_configuration(f"BaseServerSettings_{ctx.guild.id}") + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") if ctx.author.voice is None or ctx.author.voice.channel is None: await self._message_service.send_ctx_msg( diff --git a/kdb-bot/src/modules/base/command/help_command.py b/kdb-bot/src/modules/base/command/help_command.py index 9c37cf08..beb63e77 100644 --- a/kdb-bot/src/modules/base/command/help_command.py +++ b/kdb-bot/src/modules/base/command/help_command.py @@ -12,7 +12,7 @@ from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.technician_config import TechnicianConfig class HelpCommand(DiscordCommandABC): @@ -39,7 +39,7 @@ class HelpCommand(DiscordCommandABC): @CommandChecks.check_is_ready() async def help(self, ctx: Context, persistent_flag: str = None): self._logger.debug(__name__, f"Received command help {ctx}:{persistent_flag}") - settings: BaseServerSettings = self._config.get_configuration(f"BaseServerSettings_{ctx.guild.id}") + settings: TechnicianConfig = self._config.get_configuration(TechnicianConfig) is_persistent = persistent_flag == "--stay" await self._message_service.send_ctx_msg( ctx, diff --git a/kdb-bot/src/modules/base/command/make_coffee_command.py b/kdb-bot/src/modules/base/command/make_coffee_command.py new file mode 100644 index 00000000..d1c96de4 --- /dev/null +++ b/kdb-bot/src/modules/base/command/make_coffee_command.py @@ -0,0 +1,17 @@ +from cpl_core.logging import LoggerABC +from cpl_discord.command import DiscordCommandABC +from cpl_discord.service import DiscordBotServiceABC +from discord.ext import commands +from discord.ext.commands import Context + + +class MakeCoffeeCommand(DiscordCommandABC): + def __init__(self, logger: LoggerABC, bot: DiscordBotServiceABC): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._bot = bot + + @commands.hybrid_command(name="make-coffee") + async def make_coffee(self, ctx: Context): + await ctx.send("https://media.giphy.com/media/M4ecx9P2jI4tq/giphy.gif") diff --git a/kdb-bot/src/modules/base/command/mass_move_command.py b/kdb-bot/src/modules/base/command/mass_move_command.py index dca23d73..4dd835d2 100644 --- a/kdb-bot/src/modules/base/command/mass_move_command.py +++ b/kdb-bot/src/modules/base/command/mass_move_command.py @@ -4,6 +4,7 @@ import discord from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe +from discord.app_commands import Transform from discord.ext import commands from discord.ext.commands import Context @@ -11,6 +12,7 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_core.service.client_utils_service import ClientUtilsService +from modules.base.helper.voice_channel_transformer import VoiceChannelTransformer class MassMoveCommand(DiscordCommandABC): @@ -36,7 +38,7 @@ class MassMoveCommand(DiscordCommandABC): self, ctx: Context, channel_to: discord.VoiceChannel, - channel_from: discord.VoiceChannel = None, + channel_from: Transform[str, VoiceChannelTransformer] = None, ): self._logger.debug(__name__, f"Received command mass-move {ctx}") diff --git a/kdb-bot/src/modules/base/command/ping_command.py b/kdb-bot/src/modules/base/command/ping_command.py index eaf58ff6..ca0a14ac 100644 --- a/kdb-bot/src/modules/base/command/ping_command.py +++ b/kdb-bot/src/modules/base/command/ping_command.py @@ -10,8 +10,7 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.technician_config import TechnicianConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -24,8 +23,8 @@ class PingCommand(DiscordCommandABC): client_utils: ClientUtilsABC, translate: TranslatePipe, permissions: PermissionServiceABC, - base_helper: BaseHelperABC, servers: ServerRepositoryABC, + settings: TechnicianConfig, ): DiscordCommandABC.__init__(self) @@ -35,8 +34,8 @@ class PingCommand(DiscordCommandABC): self._client_utils = client_utils self._t = translate self._permissions = permissions - self._base_helper = base_helper self._servers = servers + self._settings = settings self._logger.trace(__name__, f"Loaded command service: {type(self).__name__}") @@ -58,9 +57,7 @@ class PingCommand(DiscordCommandABC): description=self._t.transform("modules.base.info.description"), color=int("ef9d0d", 16), ) - server = self._servers.get_server_by_discord_id(ctx.guild.id) - settings: BaseServerSettings = self._base_helper.get_config(server.discord_id) - for server in settings.ping_urls: + for server in self._settings.ping_urls: embed.add_field(name=server, value=f"{self._get_ping(server)} ms", inline=False) await self._message_service.send_ctx_msg(ctx, embed) else: diff --git a/kdb-bot/src/modules/base/command/purge_command.py b/kdb-bot/src/modules/base/command/purge_command.py index 60a0ad23..9816e29f 100644 --- a/kdb-bot/src/modules/base/command/purge_command.py +++ b/kdb-bot/src/modules/base/command/purge_command.py @@ -8,9 +8,9 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.server_settings import ServerSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger +from bot_data.model.server_config import ServerConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -41,7 +41,7 @@ class PurgeCommand(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def purge(self, ctx: Context): self._logger.debug(__name__, f"Received command purge {ctx}") - server_settings: ServerSettings = self._config.get_configuration(f"ServerSettings_{ctx.guild.id}") + server_settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") await self._message_service.send_ctx_msg(ctx, self._t.transform("modules.moderator.purge_message")) await asyncio.sleep(server_settings.message_delete_timer) diff --git a/kdb-bot/src/modules/base/command/submit_group.py b/kdb-bot/src/modules/base/command/submit_group.py new file mode 100644 index 00000000..ad1570e3 --- /dev/null +++ b/kdb-bot/src/modules/base/command/submit_group.py @@ -0,0 +1,42 @@ +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.logging import LoggerABC +from cpl_discord.command import DiscordCommandABC +from cpl_discord.service import DiscordBotServiceABC +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.helper.command_checks import CommandChecks +from modules.base.forms.bug_report_form import BugReportForm +from modules.base.forms.complaint_form import ComplaintForm + + +class SubmitGroup(DiscordCommandABC): + def __init__(self, services: ServiceProviderABC, logger: LoggerABC, bot: DiscordBotServiceABC): + DiscordCommandABC.__init__(self) + + self._services = services + self._logger = logger + self._bot = bot + + @commands.hybrid_group() + @commands.guild_only() + async def submit(self, ctx: Context): + pass + + @submit.command() + @commands.guild_only() + @CommandChecks.check_is_ready() + async def complaint(self, ctx: Context): + self._logger.debug(__name__, f"Received command complaint {ctx}") + complaint_form: ComplaintForm = self._services.get_service(ComplaintForm) + await ctx.interaction.response.send_modal(complaint_form) + self._logger.trace(__name__, f"Finished command complaint {ctx}") + + @submit.command() + @commands.guild_only() + @CommandChecks.check_is_ready() + async def bug_report(self, ctx: Context): + self._logger.debug(__name__, f"Received command complaint {ctx}") + bug_form: BugReportForm = self._services.get_service(BugReportForm) + await ctx.interaction.response.send_modal(bug_form) + self._logger.trace(__name__, f"Finished command complaint {ctx}") diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py deleted file mode 100644 index f0538374..00000000 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ /dev/null @@ -1,99 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - - -class BaseServerSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._id: int = 0 - self._max_voice_state_hours: int = 0 - self._xp_per_message: int = 0 - self._xp_per_reaction: int = 0 - self._max_message_xp_per_hour: int = 0 - self._xp_per_ontime_hour: int = 0 - self._xp_per_event_participation: int = 0 - self._afk_channel_ids: List[int] = List(int) - self._afk_command_channel_id: int = 0 - self._help_command_reference_url: str = "" - self._help_voice_channel_id: int = 0 - self._team_channel_id: int = 0 - self._ping_urls = List(str) - - @property - def id(self) -> int: - return self._id - - @property - def max_voice_state_hours(self) -> int: - return self._max_voice_state_hours - - @property - def xp_per_message(self) -> int: - return self._xp_per_message - - @property - def xp_per_reaction(self) -> int: - return self._xp_per_reaction - - @property - def max_message_xp_per_hour(self) -> int: - return self._max_message_xp_per_hour - - @property - def xp_per_ontime_hour(self) -> int: - return self._xp_per_ontime_hour - - @property - def xp_per_event_participation(self) -> int: - return self._xp_per_event_participation - - @property - def afk_channel_ids(self) -> List[int]: - return self._afk_channel_ids - - @property - def afk_command_channel_id(self) -> int: - return self._afk_command_channel_id - - @property - def help_command_reference_url(self) -> str: - return self._help_command_reference_url - - @property - def team_channel_id(self) -> int: - return self._team_channel_id - - @property - def help_voice_channel_id(self) -> int: - return self._help_voice_channel_id - - @property - def ping_urls(self) -> List[str]: - return self._ping_urls - - def from_dict(self, settings: dict): - try: - self._id = int(settings["Id"]) - self._max_voice_state_hours = int(settings["MaxVoiceStateHours"]) - self._xp_per_message = int(settings["XpPerMessage"]) - self._xp_per_reaction = int(settings["XpPerReaction"]) - self._max_message_xp_per_hour = int(settings["MaxMessageXpPerHour"]) - self._xp_per_ontime_hour = int(settings["XpPerOntimeHour"]) - self._xp_per_event_participation = ( - 0 if "XpPerEventParticipation" not in settings else settings["XpPerEventParticipation"] - ) - for index in settings["AFKChannelIds"]: - self._afk_channel_ids.append(int(index)) - self._afk_command_channel_id = settings["AFKCommandChannelId"] - self._help_command_reference_url = settings["HelpCommandReferenceUrl"] - self._help_voice_channel_id = settings["HelpVoiceChannelId"] - self._team_channel_id = settings["TeamChannelId"] - for url in settings["PingURLs"]: - self._ping_urls.append(url) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/base/configuration/base_settings.py b/kdb-bot/src/modules/base/configuration/base_settings.py deleted file mode 100644 index 0c74132c..00000000 --- a/kdb-bot/src/modules/base/configuration/base_settings.py +++ /dev/null @@ -1,31 +0,0 @@ -import traceback - -from cpl_core.configuration import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - -from modules.base.configuration.base_server_settings import BaseServerSettings - - -class BaseSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._servers: List[BaseServerSettings] = List() - - @property - def servers(self) -> List[BaseServerSettings]: - return self._servers - - def from_dict(self, settings: dict): - try: - servers = List(BaseServerSettings) - for s in settings: - st = BaseServerSettings() - settings[s]["Id"] = s - st.from_dict(settings[s]) - servers.append(st) - self._servers = servers - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/base/events/__init__.py b/kdb-bot/src/modules/base/events/__init__.py index 8f94e244..482ba4bc 100644 --- a/kdb-bot/src/modules/base/events/__init__.py +++ b/kdb-bot/src/modules/base/events/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.events" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/events/base_on_command_error_event.py b/kdb-bot/src/modules/base/events/base_on_command_error_event.py index c9c4dcc8..d5aab492 100644 --- a/kdb-bot/src/modules/base/events/base_on_command_error_event.py +++ b/kdb-bot/src/modules/base/events/base_on_command_error_event.py @@ -1,19 +1,17 @@ import datetime -import traceback import uuid +from cpl_core.logging import LoggerABC from cpl_core.time import TimeFormatSettings +from cpl_discord.events.on_command_error_abc import OnCommandErrorABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe from discord.ext import commands from discord.ext.commands import Context, CommandError -from cpl_core.logging import LoggerABC -from cpl_discord.events.on_command_error_abc import OnCommandErrorABC - from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.bot_settings import BotSettings from bot_core.exception.check_error import CheckError +from bot_data.model.technician_config import TechnicianConfig class BaseOnCommandErrorEvent(OnCommandErrorABC): @@ -22,7 +20,7 @@ class BaseOnCommandErrorEvent(OnCommandErrorABC): logger: LoggerABC, bot: DiscordBotServiceABC, messenger: MessageServiceABC, - bot_settings: BotSettings, + tech_settings: TechnicianConfig, time_format_settings: TimeFormatSettings, translate: TranslatePipe, ): @@ -30,7 +28,7 @@ class BaseOnCommandErrorEvent(OnCommandErrorABC): self._logger = logger self._bot = bot self._messenger = messenger - self._bot_settings = bot_settings + self._tech_settings = tech_settings self._time_format_settings = time_format_settings self._t = translate @@ -268,6 +266,6 @@ class BaseOnCommandErrorEvent(OnCommandErrorABC): datetime.datetime.now().strftime(self._time_format_settings.date_time_format), uid, ) - for t in self._bot_settings.technicians: + for t in self._tech_settings.technician_ids: member = self._bot.get_user(t) await self._messenger.send_dm_message(message, member, without_tracking=True) diff --git a/kdb-bot/src/modules/base/events/base_on_command_event.py b/kdb-bot/src/modules/base/events/base_on_command_event.py index 14ff7287..407a875f 100644 --- a/kdb-bot/src/modules/base/events/base_on_command_event.py +++ b/kdb-bot/src/modules/base/events/base_on_command_event.py @@ -1,6 +1,7 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.time import TimeFormatSettings from cpl_discord.events import OnCommandABC @@ -9,39 +10,35 @@ from cpl_translation import TranslatePipe from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.bot_settings import BotSettings from bot_core.logging.command_logger import CommandLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings class BaseOnCommandEvent(OnCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, bot: DiscordBotServiceABC, messenger: MessageServiceABC, - bot_settings: BotSettings, time_format_settings: TimeFormatSettings, translate: TranslatePipe, - bhs: BaseHelperABC, db: DatabaseContextABC, users: UserRepositoryABC, clients: ClientRepositoryABC, servers: ServerRepositoryABC, ): OnCommandABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._messenger = messenger - self._bot_settings = bot_settings self._time_format_settings = time_format_settings self._t = translate - self._base_helper = bhs self._db = db self._users = users self._clients = clients @@ -73,7 +70,7 @@ class BaseOnCommandEvent(OnCommandABC): self._logger.error(__name__, f"User not found {dc_user_id}") return - settings: BaseServerSettings = self._base_helper.get_config(message.guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") old_xp = user.xp user.xp += settings.xp_per_message self._users.update_user(user) diff --git a/kdb-bot/src/modules/base/events/base_on_member_join_event.py b/kdb-bot/src/modules/base/events/base_on_member_join_event.py index 3acfe4aa..58af0a00 100644 --- a/kdb-bot/src/modules/base/events/base_on_member_join_event.py +++ b/kdb-bot/src/modules/base/events/base_on_member_join_event.py @@ -17,8 +17,6 @@ from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.known_user import KnownUser from bot_data.model.user import User from bot_data.model.user_joined_server import UserJoinedServer -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -27,7 +25,6 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): self, config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, messenger: MessageServiceABC, permissions: PermissionServiceABC, db: DatabaseContextABC, @@ -40,7 +37,6 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): OnMemberJoinABC.__init__(self) self._config = config self._logger = logger - self._base_helper = base_helper self._messenger = messenger self._permission_service = permissions self._db = db @@ -65,7 +61,6 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): async def _add_if_not_exists_user_async(self, member: Union[discord.User, discord.Member]): self._logger.debug(__name__, f"Check if user exists {member}") - settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) await self._messenger.send_dm_message( self._t.transform("modules.base.welcome_message").format(member.guild.name), member, @@ -92,7 +87,7 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): return self._logger.debug(__name__, f"Add user: {member.id}") - self._users.add_user(User(member.id, 0, server)) + self._users.add_user(User(member.id, 0, 0, 0, server)) self._db.save_changes() user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) self._user_joins.add_user_joined_server(UserJoinedServer(user, datetime.now())) diff --git a/kdb-bot/src/modules/base/events/base_on_member_remove_event.py b/kdb-bot/src/modules/base/events/base_on_member_remove_event.py index 211f8c57..adfe7e87 100644 --- a/kdb-bot/src/modules/base/events/base_on_member_remove_event.py +++ b/kdb-bot/src/modules/base/events/base_on_member_remove_event.py @@ -2,6 +2,7 @@ from datetime import datetime from typing import Union import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnMemberRemoveABC @@ -12,17 +13,16 @@ from bot_core.helper.event_checks import EventChecks 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_repository_abc import UserRepositoryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.server_config import ServerConfig class BaseOnMemberRemoveEvent(OnMemberRemoveABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, db: DatabaseContextABC, - messenger: MessageServiceABC, + message_service: MessageServiceABC, users: UserRepositoryABC, servers: ServerRepositoryABC, user_joins: UserJoinedServerRepositoryABC, @@ -30,10 +30,10 @@ class BaseOnMemberRemoveEvent(OnMemberRemoveABC): ): OnMemberRemoveABC.__init__(self) + self._config = config self._logger = logger - self._base_helper = base_helper self._db = db - self._messenger = messenger + self._message_service = message_service self._users = users self._servers = servers self._user_joins = user_joins @@ -41,8 +41,7 @@ class BaseOnMemberRemoveEvent(OnMemberRemoveABC): async def _remove_user(self, member: Union[discord.User, discord.Member]): self._logger.debug(__name__, f"Remove user {member}") - settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) - await self._messenger.send_dm_message(self._t.transform("modules.base.goodbye_message"), member) + await self._message_service.send_dm_message(self._t.transform("modules.base.goodbye_message"), member) try: server = self._servers.get_server_by_discord_id(member.guild.id) @@ -59,7 +58,19 @@ class BaseOnMemberRemoveEvent(OnMemberRemoveABC): except Exception as e: self._logger.error(__name__, f"Cannot remove user {member.id}", e) + async def _notify_team(self, member: discord.Member): + self._logger.debug(__name__, f"Notify team that a member left") + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + channel = member.guild.get_channel(settings.team_channel_id) + await self._message_service.send_channel_message( + channel, + self._t.transform("modules.base.member_left_message").format(member.mention), + is_persistent=True, + ) + self._logger.trace(__name__, f"Notified team that a member left") + @EventChecks.check_is_ready() async def on_member_remove(self, member: discord.Member): self._logger.debug(__name__, f"Module {type(self)} started") await self._remove_user(member) + await self._notify_team(member) diff --git a/kdb-bot/src/modules/base/events/base_on_message_delete_event.py b/kdb-bot/src/modules/base/events/base_on_message_delete_event.py index 6320269a..3d3aad1c 100644 --- a/kdb-bot/src/modules/base/events/base_on_message_delete_event.py +++ b/kdb-bot/src/modules/base/events/base_on_message_delete_event.py @@ -1,6 +1,7 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnMessageDeleteABC from cpl_discord.service import DiscordBotServiceABC @@ -10,16 +11,15 @@ from bot_core.logging.message_logger import MessageLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings class BaseOnMessageDeleteEvent(OnMessageDeleteABC): def __init__( self, + config: ConfigurationABC, logger: MessageLogger, - bhs: BaseHelperABC, db: DatabaseContextABC, bot: DiscordBotServiceABC, users: UserRepositoryABC, @@ -27,8 +27,8 @@ class BaseOnMessageDeleteEvent(OnMessageDeleteABC): servers: ServerRepositoryABC, ): OnMessageDeleteABC.__init__(self) + self._config = config self._logger = logger - self._base_helper = bhs self._db = db self._bot = bot self._users = users @@ -61,7 +61,7 @@ class BaseOnMessageDeleteEvent(OnMessageDeleteABC): self._logger.error(__name__, f"User not found {dc_user_id}") return - settings: BaseServerSettings = self._base_helper.get_config(message.guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") old_xp = user.xp user.xp -= settings.xp_per_message self._users.update_user(user) diff --git a/kdb-bot/src/modules/base/events/base_on_message_event.py b/kdb-bot/src/modules/base/events/base_on_message_event.py index 72637252..e6531385 100644 --- a/kdb-bot/src/modules/base/events/base_on_message_event.py +++ b/kdb-bot/src/modules/base/events/base_on_message_event.py @@ -1,6 +1,7 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnMessageABC from cpl_discord.service import DiscordBotServiceABC @@ -12,16 +13,15 @@ from bot_core.service.client_utils_service import ClientUtilsService from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings class BaseOnMessageEvent(OnMessageABC): def __init__( self, + config: ConfigurationABC, logger: MessageLogger, - bhs: BaseHelperABC, client_utils: ClientUtilsService, db: DatabaseContextABC, bot: DiscordBotServiceABC, @@ -30,8 +30,8 @@ class BaseOnMessageEvent(OnMessageABC): servers: ServerRepositoryABC, ): OnMessageABC.__init__(self) + self._config = config self._logger = logger - self._base_helper = bhs self._client_utils = client_utils self._bot = bot self._db = db @@ -66,7 +66,7 @@ class BaseOnMessageEvent(OnMessageABC): self._logger.error(__name__, f"User not found {dc_user_id}") return - settings: BaseServerSettings = self._base_helper.get_config(message.guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}") if self._client_utils.is_message_xp_count_by_hour_higher_that_max_message_count_per_hour( message.created_at, user, settings ): diff --git a/kdb-bot/src/modules/base/events/base_on_scheduled_event_update_event.py b/kdb-bot/src/modules/base/events/base_on_scheduled_event_update_event.py index 5b9cf901..640a9a80 100644 --- a/kdb-bot/src/modules/base/events/base_on_scheduled_event_update_event.py +++ b/kdb-bot/src/modules/base/events/base_on_scheduled_event_update_event.py @@ -26,7 +26,11 @@ class BaseOnScheduledEventUpdateEvent(OnScheduledEventUpdateABC): # save started event if before.status != after.status and after.status == EventStatus.active: - self._events.add_event(ActiveEvent(after)) + active_event = ActiveEvent(after) + self._events.add_event(active_event) + + for member in after.channel.members: + self._events.give_xp_for_event_participation(member, active_event) # delete stopped event if before.status != after.status and ( after.status.value == EventStatus.cancelled.value or after.status.value == EventStatus.completed.value 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 52e0b551..6fb0c113 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 @@ -16,10 +16,9 @@ from bot_data.abc.user_joined_voice_channel_repository_abc import ( ) from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.server import Server +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): @@ -27,7 +26,6 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self, config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, servers: ServerRepositoryABC, known_users: KnownUserRepositoryABC, users: UserRepositoryABC, @@ -38,7 +36,6 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): OnVoiceStateUpdateABC.__init__(self) self._config = config self._logger = logger - self._base_helper = base_helper self._servers = servers self._known_users = known_users self._users = users @@ -67,7 +64,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._db.save_changes() return - settings: BaseServerSettings = self._base_helper.get_config(server.discord_id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}") join = self._user_joins_vc.get_active_user_joined_voice_channel_by_user_id(user.id) join.leaved_on = datetime.now() @@ -98,7 +95,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): __name__, f"Detected on_voice_state_update {member.id} from {before} to {after}", ) - settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") server = self._servers.get_server_by_discord_id(member.guild.id) try: diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py index 4f640ff0..5bcd1f95 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py @@ -7,8 +7,7 @@ from cpl_translation import TranslatePipe from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.event_checks import EventChecks from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.server_config import ServerConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -17,7 +16,6 @@ class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC): self, config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, servers: ServerRepositoryABC, permissions: PermissionServiceABC, message_service: MessageServiceABC, @@ -26,7 +24,6 @@ class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC): OnVoiceStateUpdateABC.__init__(self) self._config = config self._logger = logger - self._base_helper = base_helper self._servers = servers self._permissions = permissions self._message_service = message_service @@ -43,7 +40,7 @@ class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC): ): self._logger.debug(__name__, f"Module {type(self)} started") server = self._servers.get_server_by_discord_id(member.guild.id) - settings: BaseServerSettings = self._base_helper.get_config(server.discord_id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}") if after.channel is None or after.channel.id != settings.help_voice_channel_id: return diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py index d8283652..fe5195c9 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py @@ -7,8 +7,6 @@ from cpl_discord.events import OnVoiceStateUpdateABC from bot_core.helper.event_checks import EventChecks from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings from modules.base.service.event_service import EventService @@ -17,7 +15,6 @@ class BaseOnVoiceStateUpdateEventScheduledEventBonus(OnVoiceStateUpdateABC): self, config: ConfigurationABC, logger: LoggerABC, - base_helper: BaseHelperABC, servers: ServerRepositoryABC, users: UserRepositoryABC, events: EventService, @@ -26,7 +23,6 @@ class BaseOnVoiceStateUpdateEventScheduledEventBonus(OnVoiceStateUpdateABC): OnVoiceStateUpdateABC.__init__(self) self._config = config self._logger = logger - self._base_helper = base_helper self._servers = servers self._users = users self._events = events @@ -51,16 +47,6 @@ class BaseOnVoiceStateUpdateEventScheduledEventBonus(OnVoiceStateUpdateABC): self._logger.debug(__name__, f"Module {type(self)} stopped") return - server = self._servers.get_server_by_discord_id(member.guild.id) - user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) - if active_event.participants.any(lambda x: x.id == user.id): - self._logger.debug(__name__, f"Module {type(self)} stopped") - return - - settings: BaseServerSettings = self._base_helper.get_config(server.discord_id) - user.xp += settings.xp_per_event_participation - self._users.update_user(user) - self._db.save_changes() - active_event.participants.append(user) + self._events.give_xp_for_event_participation(member, active_event) self._logger.debug(__name__, f"Module {type(self)} stopped") diff --git a/kdb-bot/src/modules/base/forms/__init__.py b/kdb-bot/src/modules/base/forms/__init__.py new file mode 100644 index 00000000..518fdb84 --- /dev/null +++ b/kdb-bot/src/modules/base/forms/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 - 2023 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = "modules.base.forms" +__author__ = "Sven Heidemann" +__license__ = "MIT" +__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" +__version__ = "1.1.0" + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple("VersionInfo", "major minor micro") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/forms/bug_report_form.py b/kdb-bot/src/modules/base/forms/bug_report_form.py new file mode 100644 index 00000000..185fd51f --- /dev/null +++ b/kdb-bot/src/modules/base/forms/bug_report_form.py @@ -0,0 +1,47 @@ +import discord +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.service import DiscordBotServiceABC +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 bot_data.model.technician_config import TechnicianConfig + + +class BugReportForm(ui.Modal): + description = ui.TextInput(label="Report a bug", required=True, style=TextStyle.long) + + def __init__( + self, + technician_config: TechnicianConfig, + bot: DiscordBotServiceABC, + db: DatabaseContextABC, + logger: CommandLogger, + message_service: MessageServiceABC, + t: TranslatePipe, + ): + ui.Modal.__init__(self, title=t.transform("modules.base.bug.title")) + + self._technician_config = technician_config + self._bot = bot + self._db = db + self._message_service = message_service + self._logger = logger + self._t = t + + self.description.label = t.transform("modules.base.bug.label") + + async def on_submit(self, interaction: discord.Interaction): + self._logger.debug(__name__, f"Started bug report form") + + for t in self._technician_config.technician_ids: + member = self._bot.get_user(t) + await self._message_service.send_dm_message( + self._t.transform("modules.base.bug.message").format(interaction.user.mention, self.description.value), + member, + without_tracking=True, + ) + + await self._message_service.send_interaction_msg(interaction, self._t.transform("modules.base.bug.response")) + self._logger.trace(__name__, f"Finished bug report form") diff --git a/kdb-bot/src/modules/base/forms/complaint_form.py b/kdb-bot/src/modules/base/forms/complaint_form.py new file mode 100644 index 00000000..b0d77813 --- /dev/null +++ b/kdb-bot/src/modules/base/forms/complaint_form.py @@ -0,0 +1,47 @@ +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +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 bot_data.model.server_config import ServerConfig + + +class ComplaintForm(ui.Modal): + description = ui.TextInput(label="Complain about something", required=True, style=TextStyle.long) + + def __init__( + self, + config: ConfigurationABC, + db: DatabaseContextABC, + logger: CommandLogger, + message_service: MessageServiceABC, + t: TranslatePipe, + ): + ui.Modal.__init__(self, title=t.transform("modules.base.complaints.title")) + + self._config = config + self._db = db + self._message_service = message_service + self._logger = logger + self._t = t + + self.description.label = t.transform("modules.base.complaints.label") + + async def on_submit(self, interaction: discord.Interaction): + self._logger.debug(__name__, f"Started complaint command form") + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{interaction.guild.id}") + channel = interaction.guild.get_channel(settings.team_channel_id) + await self._message_service.send_channel_message( + channel, + self._t.transform("modules.base.complaints.message").format( + interaction.user.mention, self.description.value + ), + is_persistent=True, + ) + await self._message_service.send_interaction_msg( + interaction, self._t.transform("modules.base.complaints.response") + ) + self._logger.trace(__name__, f"Finished complaint command form") diff --git a/kdb-bot/src/modules/base/helper/__init__.py b/kdb-bot/src/modules/base/helper/__init__.py index 7f6206e5..1fee883b 100644 --- a/kdb-bot/src/modules/base/helper/__init__.py +++ b/kdb-bot/src/modules/base/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.helper" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py index 9fb4f89e..d2cb1581 100644 --- a/kdb-bot/src/modules/base/helper/base_reaction_handler.py +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -1,5 +1,6 @@ from datetime import datetime +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent @@ -9,26 +10,25 @@ from bot_core.helper.log_message_helper import LogMessageHelper from bot_core.logging.message_logger import MessageLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings +from bot_data.model.server_config import ServerConfig class BaseReactionHandler: def __init__( self, + config: ConfigurationABC, logger: MessageLogger, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, users: UserRepositoryABC, - base_helper: BaseHelperABC, client_utils: ClientUtilsABC, db: DatabaseContextABC, ): + self._config = config self._logger = logger self._bot = bot self._servers = servers self._users = users - self._base_helper = base_helper self._client_utils = client_utils self._db = db @@ -47,6 +47,10 @@ class BaseReactionHandler: log_msg += f" with {payload.emoji.name}" try: channel = guild.get_channel(payload.channel_id) + if channel is None: + self._logger.warn(__name__, f"Channel not found") + return + message = await channel.fetch_message(payload.message_id) self._logger.info( __name__, @@ -66,7 +70,7 @@ class BaseReactionHandler: server = self._servers.get_server_by_discord_id(guild.id) user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) - settings: BaseServerSettings = self._base_helper.get_config(guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") if r_type == "add": if self._client_utils.is_message_xp_count_by_hour_higher_that_max_message_count_per_hour( diff --git a/kdb-bot/src/modules/base/helper/voice_channel_transformer.py b/kdb-bot/src/modules/base/helper/voice_channel_transformer.py new file mode 100644 index 00000000..a9ee5df0 --- /dev/null +++ b/kdb-bot/src/modules/base/helper/voice_channel_transformer.py @@ -0,0 +1,30 @@ +import discord +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_query.extension import List +from discord import Interaction, app_commands +from discord.app_commands import Transformer, Choice + +from bot_core.abc.client_utils_abc import ClientUtilsABC + + +class VoiceChannelTransformer(Transformer): + async def transform(self, interaction: Interaction, value: str, /) -> discord.VoiceChannel: + voice_channel = ( + List(discord.VoiceChannel, interaction.guild.voice_channels) + .where(lambda x: str(x.id) == value) + .first_or_default() + ) + return voice_channel + + async def autocomplete(self, interaction: Interaction, current: str, /) -> list[Choice[str]]: + @ServiceProviderABC.inject + def get_client_utils(client_utils: ClientUtilsABC) -> ClientUtilsABC: + return client_utils + + voice_channels = List(discord.Role, interaction.guild.voice_channels).where(lambda x: len(x.members) > 0) + return [ + app_commands.Choice( + name=f"{vc.name}" if vc.category is None else f"{vc.name}: {vc.category.name}", value=vc.name + ) + for vc in get_client_utils().get_auto_complete_list(voice_channels, current, lambda x: x.name) + ] diff --git a/kdb-bot/src/modules/base/model/__init__.py b/kdb-bot/src/modules/base/model/__init__.py index d1aff8fe..918311e7 100644 --- a/kdb-bot/src/modules/base/model/__init__.py +++ b/kdb-bot/src/modules/base/model/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.model" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/service/__init__.py b/kdb-bot/src/modules/base/service/__init__.py index dd891b5a..fc37b5e5 100644 --- a/kdb-bot/src/modules/base/service/__init__.py +++ b/kdb-bot/src/modules/base/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/base/service/base_helper_service.py b/kdb-bot/src/modules/base/service/base_helper_service.py deleted file mode 100644 index 89c9d9c3..00000000 --- a/kdb-bot/src/modules/base/service/base_helper_service.py +++ /dev/null @@ -1,13 +0,0 @@ -from cpl_core.configuration import ConfigurationABC - -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings - - -class BaseHelperService(BaseHelperABC): - def __init__(self, config: ConfigurationABC): - BaseHelperABC.__init__(self) - self._config = config - - def get_config(self, g_id: int) -> BaseServerSettings: - return self._config.get_configuration(f"BaseServerSettings_{g_id}") diff --git a/kdb-bot/src/modules/base/service/event_service.py b/kdb-bot/src/modules/base/service/event_service.py index 8073b3d3..30b19d96 100644 --- a/kdb-bot/src/modules/base/service/event_service.py +++ b/kdb-bot/src/modules/base/service/event_service.py @@ -1,13 +1,32 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC from cpl_query.extension import List +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.base.model.active_event import ActiveEvent class EventService: - def __init__(self): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + db: DatabaseContextABC, + ): + self._config = config + self._logger = logger + self._servers = servers + self._users = users + self._db = db + self._active_events = List(ActiveEvent) def add_event(self, event: ActiveEvent): @@ -29,3 +48,16 @@ class EventService: return self._active_events.remove(event) + + def give_xp_for_event_participation(self, member: discord.Member, active_event: ActiveEvent): + server = self._servers.get_server_by_discord_id(member.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) + if active_event.participants.any(lambda x: x.id == user.id): + self._logger.debug(__name__, f"Module {type(self)} stopped") + return + + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}") + user.xp += settings.xp_per_event_participation + self._users.update_user(user) + self._db.save_changes() + active_event.participants.append(user) diff --git a/kdb-bot/src/modules/base/service/user_warnings_service.py b/kdb-bot/src/modules/base/service/user_warnings_service.py index a67d3258..f3b0ab3f 100644 --- a/kdb-bot/src/modules/base/service/user_warnings_service.py +++ b/kdb-bot/src/modules/base/service/user_warnings_service.py @@ -1,4 +1,5 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.service import DiscordBotServiceABC @@ -9,10 +10,9 @@ from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User from bot_data.model.user_warnings import UserWarnings -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.configuration.base_server_settings import BaseServerSettings from modules.level.service.level_service import LevelService from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -20,6 +20,7 @@ from modules.permission.abc.permission_service_abc import PermissionServiceABC class UserWarningsService: def __init__( self, + config: ConfigurationABC, logger: LoggerABC, db: DatabaseContextABC, bot: DiscordBotServiceABC, @@ -31,8 +32,8 @@ class UserWarningsService: message_service: MessageServiceABC, t: TranslatePipe, permissions: PermissionServiceABC, - base_helper: BaseHelperABC, ): + self._config = config self._logger = logger self._db = db self._bot = bot @@ -44,11 +45,10 @@ class UserWarningsService: self._message_service = message_service self._t = t self._permissions = permissions - self._base_helper = base_helper async def notify_team(self, member: discord.Member, description: str, removed=False): try: - settings: BaseServerSettings = self._base_helper.get_config(member.guild.id) + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") channel = member.guild.get_channel(settings.team_channel_id) if removed: translation = self._t.transform("modules.base.warnings.team_removed").format( diff --git a/kdb-bot/src/modules/base/thread/__init__.py b/kdb-bot/src/modules/base/thread/__init__.py index 6070f2c8..d60f11c1 100644 --- a/kdb-bot/src/modules/base/thread/__init__.py +++ b/kdb-bot/src/modules/base/thread/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.base.thread" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/boot_log/__init__.py b/kdb-bot/src/modules/boot_log/__init__.py index 645c86c3..2ef0bea7 100644 --- a/kdb-bot/src/modules/boot_log/__init__.py +++ b/kdb-bot/src/modules/boot_log/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.boot_log" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/boot_log/boot-log.json b/kdb-bot/src/modules/boot_log/boot-log.json index e337e3cb..5799bc44 100644 --- a/kdb-bot/src/modules/boot_log/boot-log.json +++ b/kdb-bot/src/modules/boot_log/boot-log.json @@ -3,8 +3,8 @@ "Name": "boot-log", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py b/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py index 58c2b5c4..1e2e5a52 100644 --- a/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py +++ b/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py @@ -8,10 +8,10 @@ from cpl_translation import TranslatePipe from discord import guild from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.server_settings import ServerSettings -from modules.boot_log.configuration.boot_log_server_settings import ( - BootLogServerSettings, -) +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.model.server_config import ServerConfig +from bot_data.model.technician_config import TechnicianConfig class BootLogOnReadyEvent(OnReadyABC): @@ -22,6 +22,7 @@ class BootLogOnReadyEvent(OnReadyABC): bot: DiscordBotServiceABC, message_service: MessageServiceABC, translate: TranslatePipe, + tech_config: TechnicianConfig, ): OnReadyABC.__init__(self) self._config = config @@ -30,11 +31,17 @@ class BootLogOnReadyEvent(OnReadyABC): self._bot = bot self._message_service = message_service self._t = translate + self._tech_config = tech_config self._logger.info(__name__, f"Module {type(self)} loaded") async def on_ready(self): self._logger.debug(__name__, f"Module {type(self)} started") + if not FeatureFlagsSettings.get_flag_from_dict( + self._tech_config.feature_flags, FeatureFlagsEnum.boot_log_module + ): + return + try: start_time = self._config.get_configuration("Bot_StartTime") init_time = round( @@ -58,19 +65,19 @@ class BootLogOnReadyEvent(OnReadyABC): g: guild = g self._logger.debug(__name__, f"Server detected: {g.id}") - server_settings: ServerSettings = self._config.get_configuration(f"ServerSettings_{g.id}") - if server_settings is None: - self._logger.error(__name__, f"BootLog settings for server {g.id} not found!") - return - - module_settings: BootLogServerSettings = self._config.get_configuration(f"BootLogServerSettings_{g.id}") - if module_settings is None: + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{g.id}") + if server_config is None: self._logger.error(__name__, f"Config {type(self).__name__}_{g.id} not found!") return + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.boot_log_module + ): + continue + self._bot.loop.create_task( self._message_service.send_channel_message( - self._bot.get_channel(module_settings.login_message_channel_id), + self._bot.get_channel(server_config.login_message_channel_id), self._t.transform("modules.boot_log.login_message").format(init_time), ) ) diff --git a/kdb-bot/src/modules/boot_log/configuration/boot_log_server_settings.py b/kdb-bot/src/modules/boot_log/configuration/boot_log_server_settings.py deleted file mode 100644 index 148609af..00000000 --- a/kdb-bot/src/modules/boot_log/configuration/boot_log_server_settings.py +++ /dev/null @@ -1,28 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console - - -class BootLogServerSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._id: int = 0 - self._login_message_channel_id: int = 0 - - @property - def id(self) -> int: - return self._id - - @property - def login_message_channel_id(self) -> int: - return self._login_message_channel_id - - def from_dict(self, settings: dict): - try: - self._id = int(settings["Id"]) - self._login_message_channel_id = int(settings["LoginMessageChannelId"]) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/boot_log/configuration/boot_log_settings.py b/kdb-bot/src/modules/boot_log/configuration/boot_log_settings.py deleted file mode 100644 index d06ed5fb..00000000 --- a/kdb-bot/src/modules/boot_log/configuration/boot_log_settings.py +++ /dev/null @@ -1,33 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - -from modules.boot_log.configuration.boot_log_server_settings import ( - BootLogServerSettings, -) - - -class BootLogSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._servers: List[BootLogServerSettings] = List() - - @property - def servers(self) -> List[BootLogServerSettings]: - return self._servers - - def from_dict(self, settings: dict): - try: - servers = List(BootLogServerSettings) - for s in settings: - st = BootLogServerSettings() - settings[s]["Id"] = s - st.from_dict(settings[s]) - servers.append(st) - self._servers = servers - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/config/__init__.py b/kdb-bot/src/modules/config/__init__.py new file mode 100644 index 00000000..7d02c76a --- /dev/null +++ b/kdb-bot/src/modules/config/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 - 2023 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = "modules.config" +__author__ = "Sven Heidemann" +__license__ = "MIT" +__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" +__version__ = "1.1.0" + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple("VersionInfo", "major minor micro") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/config/config.json b/kdb-bot/src/modules/config/config.json new file mode 100644 index 00000000..19490f04 --- /dev/null +++ b/kdb-bot/src/modules/config/config.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "config", + "Version": { + "Major": "1", + "Minor": "1", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=1.1.0" + ], + "DevDependencies": [ + "cpl-cli>=1.1.0" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "config.main", + "EntryPoint": "config", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/config/config_extension.py b/kdb-bot/src/modules/config/config_extension.py new file mode 100644 index 00000000..810d2214 --- /dev/null +++ b/kdb-bot/src/modules/config/config_extension.py @@ -0,0 +1,22 @@ +from cpl_core.application.application_extension_abc import ApplicationExtensionABC +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.logging import LoggerABC + +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.service.config_service import ConfigService + + +class ConfigExtension(ApplicationExtensionABC): + def __init__(self): + pass + + async def run(self, config: ConfigurationABC, services: ServiceProviderABC): + feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + if not feature_flags.get_flag(FeatureFlagsEnum.config_module): + return + logger: LoggerABC = services.get_service(LoggerABC) + logger.debug(__name__, "Config extension started") + config: ConfigService = services.get_service(ConfigService) + await config.reload_technician_config() diff --git a/kdb-bot/src/modules/config/config_module.py b/kdb-bot/src/modules/config/config_module.py new file mode 100644 index 00000000..c55b62d1 --- /dev/null +++ b/kdb-bot/src/modules/config/config_module.py @@ -0,0 +1,20 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.config.events.config_on_ready_event import ConfigOnReadyEvent + + +class ConfigModule(ModuleABC): + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.config_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + self._dc.add_event(DiscordEventTypesEnum.on_ready.value, ConfigOnReadyEvent) diff --git a/kdb-bot/src/modules/config/events/__init__.py b/kdb-bot/src/modules/config/events/__init__.py new file mode 100644 index 00000000..5e8c8171 --- /dev/null +++ b/kdb-bot/src/modules/config/events/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 - 2023 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = "modules.config.events" +__author__ = "Sven Heidemann" +__license__ = "MIT" +__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" +__version__ = "1.1.0" + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple("VersionInfo", "major minor micro") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/config/events/config_on_ready_event.py b/kdb-bot/src/modules/config/events/config_on_ready_event.py new file mode 100644 index 00000000..14ad229a --- /dev/null +++ b/kdb-bot/src/modules/config/events/config_on_ready_event.py @@ -0,0 +1,29 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnReadyABC +from cpl_discord.service import DiscordBotServiceABC + +from bot_core.service.config_service import ConfigService +from bot_data.abc.server_repository_abc import ServerRepositoryABC + + +class ConfigOnReadyEvent(OnReadyABC): + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + config_service: ConfigService, + ): + OnReadyABC.__init__(self) + + self._config = config + self._logger = logger + self._bot = bot + self._servers = servers + self._config_service = config_service + + async def on_ready(self): + for guild in self._bot.guilds: + await self._config_service.reload_server_config(self._servers.get_server_by_discord_id(guild.id)) diff --git a/kdb-bot/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/config/service/__init__.py similarity index 78% rename from kdb-bot/src/modules/base/abc/__init__.py rename to kdb-bot/src/modules/config/service/__init__.py index 971d0324..00a48fdd 100644 --- a/kdb-bot/src/modules/base/abc/__init__.py +++ b/kdb-bot/src/modules/config/service/__init__.py @@ -11,11 +11,11 @@ Discord bot for the Keksdose discord Server """ -__title__ = "modules.base.abc" +__title__ = "modules.config.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/database/__init__.py b/kdb-bot/src/modules/database/__init__.py index 4b0ad816..6bb818bc 100644 --- a/kdb-bot/src/modules/database/__init__.py +++ b/kdb-bot/src/modules/database/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.database" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/database/database.json b/kdb-bot/src/modules/database/database.json index f71c339a..b7a292f5 100644 --- a/kdb-bot/src/modules/database/database.json +++ b/kdb-bot/src/modules/database/database.json @@ -3,8 +3,8 @@ "Name": "database", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/src/modules/database/database_on_ready_event.py b/kdb-bot/src/modules/database/database_on_ready_event.py index 8d512035..5c3bd775 100644 --- a/kdb-bot/src/modules/database/database_on_ready_event.py +++ b/kdb-bot/src/modules/database/database_on_ready_event.py @@ -1,31 +1,11 @@ -from ctypes import Union -from datetime import datetime, timedelta +from datetime import datetime -import discord from cpl_core.configuration import ConfigurationABC -from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnReadyABC -from cpl_discord.service import DiscordBotServiceABC from bot_core.logging.database_logger import DatabaseLogger -from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe -from bot_data.abc.client_repository_abc import ClientRepositoryABC -from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC -from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC -from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC -from bot_data.abc.user_joined_voice_channel_repository_abc import ( - UserJoinedVoiceChannelRepositoryABC, -) -from bot_data.abc.user_repository_abc import UserRepositoryABC -from bot_data.model.client import Client -from bot_data.model.known_user import KnownUser -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 bot_core.service.data_integrity_service import DataIntegrityService from bot_data.service.seeder_service import SeederService -from bot_data.service.user_repository_service import ServerRepositoryABC -from modules.base.configuration.base_server_settings import BaseServerSettings class DatabaseOnReadyEvent(OnReadyABC): @@ -34,31 +14,13 @@ class DatabaseOnReadyEvent(OnReadyABC): config: ConfigurationABC, logger: DatabaseLogger, seeder: SeederService, - bot: DiscordBotServiceABC, - db_context: DatabaseContextABC, - server_repo: ServerRepositoryABC, - user_repo: UserRepositoryABC, - client_repo: ClientRepositoryABC, - known_users: KnownUserRepositoryABC, - user_joins: UserJoinedServerRepositoryABC, - user_joins_vc: UserJoinedVoiceChannelRepositoryABC, - user_joined_gs: UserJoinedGameServerRepositoryABC, - dtp: DateTimeOffsetPipe, + data_integrity: DataIntegrityService, ): self._config = config self._logger = logger self._seeder = seeder - self._bot = bot - self._db_context = db_context - self._servers = server_repo - self._users = user_repo - self._clients = client_repo - self._known_users = known_users - self._user_joins = user_joins - self._user_joins_vc = user_joins_vc - self._user_joined_gs = user_joined_gs - self._dtp = dtp + self._data_integrity = data_integrity OnReadyABC.__init__(self) self._logger.info(__name__, f"Module {type(self)} loaded") @@ -83,306 +45,10 @@ class DatabaseOnReadyEvent(OnReadyABC): self._logger.error(__name__, "Database init time calculation failed", e) return - def _check_known_users(self): - self._logger.debug(__name__, f"Start checking KnownUsers table, {len(self._bot.users)}") - for u in self._bot.users: - u: discord.User = u - try: - if u.bot: - self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") - continue - - user = self._known_users.find_user_by_discord_id(u.id) - if user is not None: - continue - - self._logger.warn(__name__, f"Unknown user: {u.id}") - self._logger.debug(__name__, f"Add user: {u.id}") - self._known_users.add_user(KnownUser(u.id)) - self._db_context.save_changes() - - user = self._known_users.find_user_by_discord_id(u.id) - if user is None: - self._logger.fatal(__name__, f"Cannot add user: {u.id}") - - self._logger.debug(__name__, f"Added user: {u.id}") - except Exception as e: - self._logger.error(__name__, f"Cannot get user", e) - - def _check_servers(self): - self._logger.debug(__name__, f"Start checking Servers table") - for g in self._bot.guilds: - g: discord.Guild = g - try: - server = self._servers.find_server_by_discord_id(g.id) - if server is not None: - continue - - self._logger.warn(__name__, f"Server not found in database: {g.id}") - self._logger.debug(__name__, f"Add server: {g.id}") - self._servers.add_server(Server(g.id)) - self._db_context.save_changes() - - server = self._servers.find_server_by_discord_id(g.id) - if server is None: - self._logger.fatal(__name__, f"Cannot add server: {g.id}") - - self._logger.debug(__name__, f"Added server: {g.id}") - except Exception as e: - self._logger.error(__name__, f"Cannot get server", e) - - results = self._servers.get_servers() - if results is None or len(results) == 0: - self._logger.error(__name__, f"Table Servers is empty!") - - def _check_clients(self): - self._logger.debug(__name__, f"Start checking Clients table") - for g in self._bot.guilds: - g: discord.Guild = g - try: - server: Server = self._servers.find_server_by_discord_id(g.id) - if server is None: - self._logger.fatal(__name__, f"Server not found in database: {g.id}") - - client = self._clients.find_client_by_server_id(server.id) - if client is not None: - continue - - self._logger.warn( - __name__, - f"Client for server {g.id} not found in database: {self._bot.user.id}", - ) - self._logger.debug(__name__, f"Add client: {self._bot.user.id}") - self._clients.add_client(Client(self._bot.user.id, 0, 0, 0, 0, 0, server)) - self._db_context.save_changes() - - client = self._clients.find_client_by_server_id(server.id) - if client is None: - self._logger.fatal( - __name__, - f"Cannot add client {self._bot.user.id} for server {g.id}", - ) - - self._logger.debug(__name__, f"Added client: {g.id}") - except Exception as e: - self._logger.error(__name__, f"Cannot get client", e) - - results = self._servers.get_servers() - if results is None or len(results) == 0: - self._logger.error(__name__, f"Table Servers is empty!") - - def _check_users(self): - self._logger.debug(__name__, f"Start checking Users table") - for g in self._bot.guilds: - g: discord.Guild = g - - try: - server = self._servers.find_server_by_discord_id(g.id) - if server is None: - self._logger.fatal(__name__, f"Server not found in database: {g.id}") - - for u in g.members: - u: Union[discord.Member, discord.User] = u - if u.bot: - self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") - continue - - user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id) - if user is not None: - continue - - self._logger.warn(__name__, f"User not found in database: {u.id}") - self._logger.debug(__name__, f"Add user: {u.id}") - self._users.add_user(User(u.id, 0, server)) - self._db_context.save_changes() - - self._logger.debug(__name__, f"Added User: {u.id}") - except Exception as e: - self._logger.error(__name__, f"Cannot get User", e) - - results = self._users.get_users() - if results is None or len(results) == 0: - self._logger.error(__name__, f"Table Users is empty!") - - def _check_user_joins(self): - self._logger.debug(__name__, f"Start checking UserJoinedServers table") - for guild in self._bot.guilds: - guild: discord.Guild = guild - - server = self._servers.find_server_by_discord_id(guild.id) - if server is None: - self._logger.fatal(__name__, f"Server not found in database: {guild.id}") - - try: - for u in guild.members: - u: discord.User = u - if u.bot: - self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot") - continue - - user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id) - if user is None: - self._logger.fatal(__name__, f"User not found in database: {u.id}") - - join = self._user_joins.find_active_user_joined_server_by_user_id(user.id) - if join is not None: - continue - - m: discord.Member = u - self._logger.warn( - __name__, - f"Active UserJoinedServer not found in database: {guild.id}:{u.id}@{m.joined_at}", - ) - self._logger.debug( - __name__, - f"Add UserJoinedServer: {guild.id}:{u.id}@{m.joined_at}", - ) - self._user_joins.add_user_joined_server( - UserJoinedServer(user, self._dtp.transform(m.joined_at), None) - ) - self._db_context.save_changes() - - self._logger.debug(__name__, f"Added UserJoinedServer: {u.id}") - except Exception as e: - self._logger.error(__name__, f"Cannot get UserJoinedServer", e) - - results = self._users.get_users() - if results is None or len(results) == 0: - self._logger.error(__name__, f"Table Users is empty!") - - joins = self._user_joins.get_user_joined_servers() - for join in joins: - join: UserJoinedServer = join - if join.user.server.discord_id != guild.id: - continue - - if join.leaved_on is not None: - continue - - dc_user = guild.get_member(join.user.discord_id) - if dc_user is None: - self._logger.warn( - __name__, - f"User {join.user.discord_id} already left the server.", - ) - join.leaved_on = datetime.now() - self._user_joins.update_user_joined_server(join) - - self._db_context.save_changes() - - def _check_user_joins_vc(self): - self._logger.debug(__name__, f"Start checking UserJoinedVoiceChannel table") - for guild in self._bot.guilds: - guild: discord.Guild = guild - settings: BaseServerSettings = self._config.get_configuration(f"BaseServerSettings_{guild.id}") - - server = self._servers.find_server_by_discord_id(guild.id) - if server is None: - self._logger.fatal(__name__, f"Server not found in database: {guild.id}") - - try: - for member in guild.members: - if member.bot: - self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") - continue - - user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) - if user is None: - self._logger.fatal(__name__, f"User not found in database: {member.id}") - - joins = self._user_joins_vc.find_active_user_joined_voice_channels_by_user_id(user.id) - if joins is None or len(joins) == 0: - continue - - for join in joins: - self._logger.warn( - __name__, - f"Active UserJoinedVoiceChannel found in database: {guild.id}:{member.id}@{join.joined_on}", - ) - join.leaved_on = datetime.now() - - if ( - (join.leaved_on - join.joined_on).total_seconds() / 60 / 60 - ) > settings.max_voice_state_hours: - join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours) - - self._user_joins_vc.update_user_joined_voice_channel(join) - # todo: maybe add XP - self._db_context.save_changes() - - for member in guild.members: - if member.bot: - self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") - continue - - if member.voice is None or member.voice.channel.id in settings.afk_channel_ids: - continue - - user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) - if user is None: - self._logger.fatal(__name__, f"User not found in database: {member.id}") - - join = UserJoinedVoiceChannel(user, member.voice.channel.id, datetime.now()) - self._user_joins_vc.add_user_joined_voice_channel(join) - self._db_context.save_changes() - self._logger.warn(__name__, f"VS {member.voice}") - - except Exception as e: - self._logger.error(__name__, f"Cannot get UserJoinedVoiceChannel", e) - - def _check_user_joined_gs(self): - self._logger.debug(__name__, f"Start checking UserJoinedGameServer table") - for guild in self._bot.guilds: - guild: discord.Guild = guild - - server = self._servers.find_server_by_discord_id(guild.id) - if server is None: - self._logger.fatal(__name__, f"Server not found in database: {guild.id}") - - try: - for member in guild.members: - if member.bot: - self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot") - continue - - user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id) - if user is None: - self._logger.fatal(__name__, f"User not found in database: {member.id}") - - joins = self._user_joined_gs.find_active_user_joined_game_servers_by_user_id(user.id) - if joins is None or len(joins) == 0: - continue - - for join in joins: - self._logger.warn( - __name__, - f"Active UserJoinedGameServer found in database: {guild.id}:{member.id}@{join.joined_on}", - ) - join.leaved_on = datetime.now() - settings: BaseServerSettings = self._config.get_configuration(f"BaseServerSettings_{guild.id}") - - if ( - (join.leaved_on - join.joined_on).total_seconds() / 60 / 60 - ) > settings.max_voice_state_hours: - join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours) - - self._user_joined_gs.update_user_joined_game_server(join) - # todo: maybe add XP - self._db_context.save_changes() - except Exception as e: - self._logger.error(__name__, f"Cannot get UserJoinedGameServer", e) - async def on_ready(self): self._logger.debug(__name__, f"Module {type(self)} started") - self._check_known_users() - self._check_servers() - self._check_clients() - self._check_users() - self._check_user_joins() - self._check_user_joins_vc() - self._check_user_joined_gs() - + await self._data_integrity.check_data_integrity() await self._seeder.seed() self._validate_init_time() diff --git a/kdb-bot/src/modules/level/__init__.py b/kdb-bot/src/modules/level/__init__.py index 83d073d3..138846f5 100644 --- a/kdb-bot/src/modules/level/__init__.py +++ b/kdb-bot/src/modules/level/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.level" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/level/command/__init__.py b/kdb-bot/src/modules/level/command/__init__.py index 2a16d188..2c21c282 100644 --- a/kdb-bot/src/modules/level/command/__init__.py +++ b/kdb-bot/src/modules/level/command/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.level.command" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 1e7c4ae5..50e03692 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -1,6 +1,7 @@ from typing import List as TList import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC from cpl_discord.container import Guild, Role @@ -12,12 +13,15 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.level import Level +from bot_data.model.server_config import ServerConfig from modules.level.level_seeder import LevelSeeder from modules.level.service.level_service import LevelService from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -26,6 +30,7 @@ from modules.permission.abc.permission_service_abc import PermissionServiceABC class LevelGroup(DiscordCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, message_service: MessageServiceABC, bot: DiscordBotServiceABC, @@ -41,6 +46,7 @@ class LevelGroup(DiscordCommandABC): ): DiscordCommandABC.__init__(self) + self._config = config self._logger = logger self._message_service = message_service self._bot = bot @@ -125,10 +131,13 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f"Received command level list {ctx}") - if ctx.guild is None: return + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + server = self._servers.get_server_by_discord_id(ctx.guild.id) levels = self._levels.get_levels_by_server_id(server.id) if levels.count() < 1: @@ -169,6 +178,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_admin() async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f"Received command level create {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return try: color = hex(discord.Colour.from_str(color).value) @@ -258,6 +273,12 @@ class LevelGroup(DiscordCommandABC): permissions: int = None, ): self._logger.debug(__name__, f"Received command level edit {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = ( @@ -350,6 +371,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_admin() async def remove(self, ctx: Context, level: str): self._logger.debug(__name__, f"Received command level remove {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = ( @@ -394,6 +421,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def down(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f"Received command level down {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -436,6 +469,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def up(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f"Received command level up {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -477,6 +516,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def set(self, ctx: Context, member: discord.Member, level: str): self._logger.debug(__name__, f"Received command level up {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -529,5 +574,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def reload(self, ctx: Context): self._logger.debug(__name__, f"Received command level reload {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._seed_levels(ctx) self._logger.trace(__name__, f"Finished command level reload") diff --git a/kdb-bot/src/modules/level/configuration/__init__.py b/kdb-bot/src/modules/level/configuration/__init__.py index 17aa17dd..fa07c4f3 100644 --- a/kdb-bot/src/modules/level/configuration/__init__.py +++ b/kdb-bot/src/modules/level/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.level.configuration" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/level/configuration/default_level_settings.py b/kdb-bot/src/modules/level/configuration/default_level_settings.py index fcce9e47..4e1c813f 100644 --- a/kdb-bot/src/modules/level/configuration/default_level_settings.py +++ b/kdb-bot/src/modules/level/configuration/default_level_settings.py @@ -1,18 +1,28 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console from cpl_query.extension import List from bot_data.model.level import Level class DefaultLevelSettings(ConfigurationModelABC): - def __init__(self): + def __init__(self, level_header: str = None, levels: list = None): ConfigurationModelABC.__init__(self) self._levels = List(Level) - self._level_header = "" + self._level_header = level_header + + if levels is None: + return + for level in levels: + self._levels.append( + Level( + level["Name"], + level["Color"], + int(level["MinXp"]), + int(level["Permissions"]), + None, + ) + ) @property def levels(self) -> List[Level]: @@ -21,20 +31,3 @@ class DefaultLevelSettings(ConfigurationModelABC): @property def level_header(self) -> str: return self._level_header - - def from_dict(self, settings: dict): - try: - self._level_header = settings["LevelHeader"] - for level in settings["Levels"]: - self._levels.append( - Level( - level["Name"], - level["Color"], - int(level["MinXp"]), - int(level["Permissions"]), - None, - ) - ) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/level/configuration/level_server_settings.py b/kdb-bot/src/modules/level/configuration/level_server_settings.py deleted file mode 100644 index f6dce290..00000000 --- a/kdb-bot/src/modules/level/configuration/level_server_settings.py +++ /dev/null @@ -1,28 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console - - -class LevelServerSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._id: int = 0 - self._changed_level_notification_channel = 0 - - @property - def id(self) -> int: - return self._id - - @property - def changed_level_notification_channel(self) -> int: - return self._changed_level_notification_channel - - def from_dict(self, settings: dict): - try: - self._id = int(settings["Id"]) - self._changed_level_notification_channel = int(settings["ChangedLevelNotificationChannelId"]) - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/level/configuration/level_settings.py b/kdb-bot/src/modules/level/configuration/level_settings.py deleted file mode 100644 index 7dd7db42..00000000 --- a/kdb-bot/src/modules/level/configuration/level_settings.py +++ /dev/null @@ -1,31 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - -from modules.level.configuration.level_server_settings import LevelServerSettings - - -class LevelSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._servers: List[LevelServerSettings] = List() - - @property - def servers(self) -> List[LevelServerSettings]: - return self._servers - - def from_dict(self, settings: dict): - try: - servers = List(LevelServerSettings) - for s in settings: - st = LevelServerSettings() - settings[s]["Id"] = s - st.from_dict(settings[s]) - servers.append(st) - self._servers = servers - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/level/events/__init__.py b/kdb-bot/src/modules/level/events/__init__.py index ffaa885e..7eddedf6 100644 --- a/kdb-bot/src/modules/level/events/__init__.py +++ b/kdb-bot/src/modules/level/events/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.level.events" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/level/events/level_on_member_join_event.py b/kdb-bot/src/modules/level/events/level_on_member_join_event.py index 26a9dccb..7113f761 100644 --- a/kdb-bot/src/modules/level/events/level_on_member_join_event.py +++ b/kdb-bot/src/modules/level/events/level_on_member_join_event.py @@ -1,18 +1,27 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_discord.events import OnMemberJoinABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_core.logging.message_logger import MessageLogger +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnMemberJoinEvent(OnMemberJoinABC): - def __init__(self, logger: MessageLogger, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: MessageLogger, level: LevelService): OnMemberJoinABC.__init__(self) + self._config = config self._logger = logger self._level = level @EventChecks.check_is_ready() async def on_member_join(self, member: discord.Member): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._level.check_level(member) diff --git a/kdb-bot/src/modules/level/events/level_on_message_event.py b/kdb-bot/src/modules/level/events/level_on_message_event.py index 8bdd054c..4c98706f 100644 --- a/kdb-bot/src/modules/level/events/level_on_message_event.py +++ b/kdb-bot/src/modules/level/events/level_on_message_event.py @@ -1,20 +1,35 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_discord.events import OnMessageABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_core.logging.message_logger import MessageLogger +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnMessageEvent(OnMessageABC): - def __init__(self, logger: MessageLogger, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: MessageLogger, level: LevelService): OnMessageABC.__init__(self) + self._config = config self._logger = logger self._level = level @EventChecks.check_is_ready() async def on_message(self, message: discord.Message): self._logger.debug(__name__, f"Module {type(self)} started") + if message.guild is None: + return + + if message.author.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: await self._level.check_level(message.author) except Exception as e: diff --git a/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py b/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py index 5535b0e7..bffc56ef 100644 --- a/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py +++ b/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py @@ -1,21 +1,27 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnRawReactionAddEvent(OnRawReactionAddABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, level: LevelService, ): OnRawReactionAddABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._level = level @@ -23,6 +29,10 @@ class LevelOnRawReactionAddEvent(OnRawReactionAddABC): @EventChecks.check_is_ready() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: self._logger.trace(__name__, f"Handle reaction {payload} for level") diff --git a/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py b/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py index 3e4c32d7..fa94e334 100644 --- a/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py +++ b/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py @@ -1,21 +1,27 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnRawReactionRemoveEvent(OnRawReactionRemoveABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, level: LevelService, ): OnRawReactionRemoveABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._level = level @@ -23,6 +29,10 @@ class LevelOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @EventChecks.check_is_ready() async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: self._logger.trace(__name__, f"Handle reaction {payload} for level") diff --git a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py index d5c51f95..9a0b5978 100644 --- a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py @@ -1,14 +1,19 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnVoiceStateUpdateABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): - def __init__(self, logger: LoggerABC, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: LoggerABC, level: LevelService): OnVoiceStateUpdateABC.__init__(self) + self._config = config self._logger = logger self._level = level @@ -22,4 +27,8 @@ class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): after: discord.VoiceState, ): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._level.check_level(member) diff --git a/kdb-bot/src/modules/level/level.json b/kdb-bot/src/modules/level/level.json index 0501e713..65478532 100644 --- a/kdb-bot/src/modules/level/level.json +++ b/kdb-bot/src/modules/level/level.json @@ -3,8 +3,8 @@ "Name": "level", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 1ed5ab7d..19c7b773 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,14 +1,18 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.container import Guild from cpl_discord.service import DiscordBotServiceABC from discord import Permissions, Colour +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.logging.database_logger import DatabaseLogger from bot_data.abc.data_seeder_abc import DataSeederABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.level import Level from bot_data.model.server import Server +from bot_data.model.server_config import ServerConfig from bot_data.service.level_repository_service import LevelRepositoryService from modules.level.configuration.default_level_settings import DefaultLevelSettings from modules.level.service.level_service import LevelService @@ -17,6 +21,7 @@ from modules.level.service.level_service import LevelService class LevelSeeder(DataSeederABC): def __init__( self, + config: ConfigurationABC, logger: DatabaseLogger, levels: DefaultLevelSettings, level_repo: LevelRepositoryService, @@ -27,6 +32,7 @@ class LevelSeeder(DataSeederABC): ): DataSeederABC.__init__(self) + self._config = config self._logger = logger self._levels = level_repo self._servers = servers @@ -65,6 +71,10 @@ class LevelSeeder(DataSeederABC): async def seed(self): # create levels for guild in self._bot.guilds: + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + continue + created_default = False if guild.roles.where(lambda r: r.name == self._level_header).first_or_default() is None: await guild.create_role(name=self._level_header) diff --git a/kdb-bot/src/modules/level/service/__init__.py b/kdb-bot/src/modules/level/service/__init__.py index af573e45..04652c5b 100644 --- a/kdb-bot/src/modules/level/service/__init__.py +++ b/kdb-bot/src/modules/level/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.level.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index 412139ba..3dccade3 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -8,11 +8,11 @@ from cpl_translation import TranslatePipe from bot_core.service.message_service import MessageService from bot_data.model.level import Level +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.server_repository_service import ServerRepositoryService from bot_data.service.user_repository_service import UserRepositoryService -from modules.level.configuration.level_server_settings import LevelServerSettings class LevelService: @@ -75,9 +75,9 @@ class LevelService: self._logger.error(__name__, f"Adding role {level_role.name} to {member.name} failed!", e) if notification_needed: - level_settings: LevelServerSettings = self._config.get_configuration(f"LevelServerSettings_{guild.id}") + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") await self._message_service.send_channel_message( - self._bot.get_channel(level_settings.changed_level_notification_channel), + self._bot.get_channel(settings.notification_chat_id), self._t.transform("modules.level.new_level_message").format(member.mention, level.name), is_persistent=True, ) diff --git a/kdb-bot/src/modules/permission/__init__.py b/kdb-bot/src/modules/permission/__init__.py index e3ca62b2..5689b9c8 100644 --- a/kdb-bot/src/modules/permission/__init__.py +++ b/kdb-bot/src/modules/permission/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.permission" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py index dda8bf6c..06cb36ff 100644 --- a/kdb-bot/src/modules/permission/abc/__init__.py +++ b/kdb-bot/src/modules/permission/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.permission.abc" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/permission/configuration/permission_server_settings.py b/kdb-bot/src/modules/permission/configuration/permission_server_settings.py deleted file mode 100644 index 1ffdfdf7..00000000 --- a/kdb-bot/src/modules/permission/configuration/permission_server_settings.py +++ /dev/null @@ -1,37 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console - - -class PermissionServerSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._id: int = 0 - self._admin_roles: list[int] = [] - self._moderator_roles: list[int] = [] - - @property - def id(self) -> int: - return self._id - - @property - def admin_roles(self) -> list[int]: - return self._admin_roles - - @property - def moderator_roles(self) -> list[int]: - return self._moderator_roles - - def from_dict(self, settings: dict): - try: - self._id = int(settings["Id"]) - for index in settings["AdminRoleIds"]: - self._admin_roles.append(int(index)) - - for index in settings["ModeratorRoleIds"]: - self._moderator_roles.append(int(index)) - 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/kdb-bot/src/modules/permission/configuration/permission_settings.py b/kdb-bot/src/modules/permission/configuration/permission_settings.py deleted file mode 100644 index 532c592a..00000000 --- a/kdb-bot/src/modules/permission/configuration/permission_settings.py +++ /dev/null @@ -1,33 +0,0 @@ -import traceback - -from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console import Console -from cpl_query.extension import List - -from modules.permission.configuration.permission_server_settings import ( - PermissionServerSettings, -) - - -class PermissionSettings(ConfigurationModelABC): - def __init__(self): - ConfigurationModelABC.__init__(self) - - self._servers: List[PermissionServerSettings] = List() - - @property - def servers(self) -> List[PermissionServerSettings]: - return self._servers - - def from_dict(self, settings: dict): - try: - servers = List(PermissionServerSettings) - for s in settings: - st = PermissionServerSettings() - settings[s]["Id"] = s - st.from_dict(settings[s]) - servers.append(st) - self._servers = servers - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/kdb-bot/src/modules/permission/events/__init__.py b/kdb-bot/src/modules/permission/events/__init__.py index 2c24b186..d7ed4649 100644 --- a/kdb-bot/src/modules/permission/events/__init__.py +++ b/kdb-bot/src/modules/permission/events/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.permission.events" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/permission/events/permission_on_member_update_event.py b/kdb-bot/src/modules/permission/events/permission_on_member_update_event.py index 30d567ee..b2e65c19 100644 --- a/kdb-bot/src/modules/permission/events/permission_on_member_update_event.py +++ b/kdb-bot/src/modules/permission/events/permission_on_member_update_event.py @@ -1,17 +1,29 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnMemberUpdateABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.model.server_config import ServerConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC class PermissionOnMemberUpdateEvent(OnMemberUpdateABC): - def __init__(self, logger: LoggerABC, permission_service: PermissionServiceABC): + def __init__(self, config: ConfigurationABC, logger: LoggerABC, permission_service: PermissionServiceABC): OnMemberUpdateABC.__init__(self) + self._config = config self._logger = logger self._permission_service = permission_service async def on_member_update(self, before: discord.Member, after: discord.Member): + if before.guild is not None: + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{before.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.permission_module + ): + return + self._logger.debug(__name__, f"Module {type(self)} started") if before.roles != after.roles: diff --git a/kdb-bot/src/modules/permission/events/permission_on_ready_event.py b/kdb-bot/src/modules/permission/events/permission_on_ready_event.py index 4da13dc0..a6dbd62c 100644 --- a/kdb-bot/src/modules/permission/events/permission_on_ready_event.py +++ b/kdb-bot/src/modules/permission/events/permission_on_ready_event.py @@ -1,15 +1,22 @@ from cpl_core.logging import LoggerABC from cpl_discord.events import OnReadyABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.model.technician_config import TechnicianConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC class PermissionOnReadyEvent(OnReadyABC): - def __init__(self, logger: LoggerABC, permission_service: PermissionServiceABC): + def __init__(self, logger: LoggerABC, permission_service: PermissionServiceABC, tech_config: TechnicianConfig): OnReadyABC.__init__(self) self._logger = logger self._permission_service = permission_service + self._tech_config = tech_config async def on_ready(self): - self._logger.debug(__name__, f"Module {type(self)} started") + if not FeatureFlagsSettings.get_flag_from_dict( + self._tech_config.feature_flags, FeatureFlagsEnum.permission_module + ): + return self._permission_service.on_ready() diff --git a/kdb-bot/src/modules/permission/permission.json b/kdb-bot/src/modules/permission/permission.json index 977bec2b..a3881919 100644 --- a/kdb-bot/src/modules/permission/permission.json +++ b/kdb-bot/src/modules/permission/permission.json @@ -3,8 +3,8 @@ "Name": "permission", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/permission/service/__init__.py b/kdb-bot/src/modules/permission/service/__init__.py index 6a2bfc5c..5d1ad3e7 100644 --- a/kdb-bot/src/modules/permission/service/__init__.py +++ b/kdb-bot/src/modules/permission/service/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.permission.service" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/permission/service/permission_service.py b/kdb-bot/src/modules/permission/service/permission_service.py index c13f517c..f94a1ff1 100644 --- a/kdb-bot/src/modules/permission/service/permission_service.py +++ b/kdb-bot/src/modules/permission/service/permission_service.py @@ -3,25 +3,25 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.service import DiscordBotServiceABC -from bot_core.configuration.bot_settings import BotSettings +from bot_data.model.server_config import ServerConfig +from bot_data.model.team_member_type_enum import TeamMemberTypeEnum +from bot_data.model.technician_config import TechnicianConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC -from modules.permission.configuration.permission_server_settings import ( - PermissionServerSettings, -) class PermissionService(PermissionServiceABC): def __init__( self, + technician_settings: TechnicianConfig, logger: LoggerABC, bot: DiscordBotServiceABC, config: ConfigurationABC, - bot_settings: BotSettings, ): PermissionServiceABC.__init__(self) self._logger = logger self._bot = bot self._config = config + self._technician_settings = technician_settings self._admin_role_ids: dict[int, list[int]] = {} self._admin_roles: dict[int, list[discord.Role]] = {} @@ -31,7 +31,7 @@ class PermissionService(PermissionServiceABC): self._moderator_roles: dict[int, list[discord.Role]] = {} self._moderators: dict[int, list[discord.Member]] = {} - self._technician_ids: list[int] = bot_settings.technicians + self._technician_ids: list[int] = technician_settings.technician_ids.to_list() self._technicians: list[discord.Member] = [] def on_ready(self): @@ -45,13 +45,21 @@ class PermissionService(PermissionServiceABC): continue self._technicians.append(technician) - settings: PermissionServerSettings = self._config.get_configuration(f"PermissionServerSettings_{guild.id}") + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}") if settings is None: - self._logger.error(__name__, "Permission settings not found") + self._logger.error(__name__, "Server settings not found") return - self._admin_role_ids[guild.id] = settings.admin_roles - self._moderator_role_ids[guild.id] = settings.moderator_roles + self._admin_role_ids[guild.id] = ( + settings.team_role_ids.where(lambda x: x.team_member_type == TeamMemberTypeEnum.admin) + .select(lambda x: x.role_id) + .to_list() + ) + self._moderator_role_ids[guild.id] = ( + settings.team_role_ids.where(lambda x: x.team_member_type == TeamMemberTypeEnum.moderator) + .select(lambda x: x.role_id) + .to_list() + ) admin_roles = [] admins = [] diff --git a/kdb-bot/src/modules/technician/__init__.py b/kdb-bot/src/modules/technician/__init__.py index b0638e15..80f70b85 100644 --- a/kdb-bot/src/modules/technician/__init__.py +++ b/kdb-bot/src/modules/technician/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.technician" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/technician/command/__init__.py b/kdb-bot/src/modules/technician/command/__init__.py index 054e1ea4..d48a10ad 100644 --- a/kdb-bot/src/modules/technician/command/__init__.py +++ b/kdb-bot/src/modules/technician/command/__init__.py @@ -15,7 +15,7 @@ __title__ = "modules.technician.command" __author__ = "Sven Heidemann" __license__ = "MIT" __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" -__version__ = "1.0.7" +__version__ = "1.1.0" from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple("VersionInfo", "major minor micro") -version_info = VersionInfo(major="1", minor="0", micro="7") +version_info = VersionInfo(major="1", minor="1", micro="0") diff --git a/kdb-bot/src/modules/technician/command/restart_command.py b/kdb-bot/src/modules/technician/command/restart_command.py index 302ba7e9..76fda82c 100644 --- a/kdb-bot/src/modules/technician/command/restart_command.py +++ b/kdb-bot/src/modules/technician/command/restart_command.py @@ -9,9 +9,10 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.bot_settings import BotSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger +from bot_core.service.data_integrity_service import DataIntegrityService +from bot_data.model.technician_config import TechnicianConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -25,7 +26,8 @@ class RestartCommand(DiscordCommandABC): client_utils: ClientUtilsABC, translate: TranslatePipe, permissions: PermissionServiceABC, - settings: BotSettings, + settings: TechnicianConfig, + data_integrity: DataIntegrityService, ): DiscordCommandABC.__init__(self) @@ -37,6 +39,7 @@ class RestartCommand(DiscordCommandABC): self._t = translate self._permissions = permissions self._settings = settings + self._data_integrity = data_integrity self._logger.trace(__name__, f"Loaded command service: {type(self).__name__}") @@ -51,6 +54,7 @@ class RestartCommand(DiscordCommandABC): await self._client_utils.presence_game("common.presence.restart") await self._message_service.send_ctx_msg(ctx, self._t.transform("modules.technician.restart_message")) await asyncio.sleep(self._settings.wait_for_restart) + await self._data_integrity.check_data_integrity(is_for_shutdown=True) await self._bot.stop_async() self._logger.trace(__name__, f"Finished restart command") diff --git a/kdb-bot/src/modules/technician/command/shutdown_command.py b/kdb-bot/src/modules/technician/command/shutdown_command.py index 6ba52651..db2f807a 100644 --- a/kdb-bot/src/modules/technician/command/shutdown_command.py +++ b/kdb-bot/src/modules/technician/command/shutdown_command.py @@ -1,6 +1,5 @@ import asyncio -import discord from cpl_core.configuration import ConfigurationABC from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC @@ -10,9 +9,10 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC -from bot_core.configuration.bot_settings import BotSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger +from bot_core.service.data_integrity_service import DataIntegrityService +from bot_data.model.technician_config import TechnicianConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -26,7 +26,8 @@ class ShutdownCommand(DiscordCommandABC): client_utils: ClientUtilsABC, translate: TranslatePipe, permissions: PermissionServiceABC, - settings: BotSettings, + settings: TechnicianConfig, + data_integrity: DataIntegrityService, ): DiscordCommandABC.__init__(self) @@ -38,6 +39,7 @@ class ShutdownCommand(DiscordCommandABC): self._t = translate self._permissions = permissions self._settings = settings + self._data_integrity = data_integrity self._logger.trace(__name__, f"Loaded command service: {type(self).__name__}") @@ -51,6 +53,7 @@ class ShutdownCommand(DiscordCommandABC): await self._client_utils.presence_game("common.presence.shutdown") await self._message_service.send_ctx_msg(ctx, self._t.transform("modules.technician.shutdown_message")) await asyncio.sleep(self._settings.wait_for_shutdown) + await self._data_integrity.check_data_integrity(is_for_shutdown=True) await self._bot.stop_async() self._logger.trace(__name__, f"Finished shutdown command") diff --git a/kdb-bot/src/modules/technician/technician.json b/kdb-bot/src/modules/technician/technician.json index c401ee4c..bab2ed2c 100644 --- a/kdb-bot/src/modules/technician/technician.json +++ b/kdb-bot/src/modules/technician/technician.json @@ -3,8 +3,8 @@ "Name": "technician", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "", "AuthorEmail": "", diff --git a/kdb-bot/src/modules/technician/technician_module.py b/kdb-bot/src/modules/technician/technician_module.py index 9aa5089c..57282ebc 100644 --- a/kdb-bot/src/modules/technician/technician_module.py +++ b/kdb-bot/src/modules/technician/technician_module.py @@ -6,8 +6,6 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.abc.data_seeder_abc import DataSeederABC -from modules.base.abc.base_helper_abc import BaseHelperABC -from modules.base.service.base_helper_service import BaseHelperService from modules.technician.api_key_seeder import ApiKeySeeder from modules.technician.command.api_key_group import ApiKeyGroup from modules.technician.command.log_command import LogCommand @@ -24,7 +22,6 @@ class TechnicianModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(DataSeederABC, ApiKeySeeder) - services.add_transient(BaseHelperABC, BaseHelperService) # commands self._dc.add_command(RestartCommand) self._dc.add_command(ShutdownCommand) diff --git a/kdb-bot/tools/checks/checks.json b/kdb-bot/tools/checks/checks.json index 232db60b..32b5a3af 100644 --- a/kdb-bot/tools/checks/checks.json +++ b/kdb-bot/tools/checks/checks.json @@ -3,8 +3,8 @@ "Name": "checks", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/tools/get_version/get-version.json b/kdb-bot/tools/get_version/get-version.json index 2a4887fc..328c9198 100644 --- a/kdb-bot/tools/get_version/get-version.json +++ b/kdb-bot/tools/get_version/get-version.json @@ -3,8 +3,8 @@ "Name": "get-version", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/tools/post_build/post-build.json b/kdb-bot/tools/post_build/post-build.json index 1efd57ec..697f15ab 100644 --- a/kdb-bot/tools/post_build/post-build.json +++ b/kdb-bot/tools/post_build/post-build.json @@ -3,8 +3,8 @@ "Name": "post-build", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-bot/tools/set_version/set-version.json b/kdb-bot/tools/set_version/set-version.json index cce95e12..b987baa9 100644 --- a/kdb-bot/tools/set_version/set-version.json +++ b/kdb-bot/tools/set_version/set-version.json @@ -3,8 +3,8 @@ "Name": "set-version", "Version": { "Major": "1", - "Minor": "0", - "Micro": "7" + "Minor": "1", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index 9c7b31b2..9281a434 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "kdb-web", - "version": "1.0.dev251", + "version": "1.0.dev127_config_in_wi", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kdb-web", - "version": "1.0.dev251", + "version": "1.0.dev127_config_in_wi", "dependencies": { "@angular/animations": "^15.1.4", "@angular/common": "^15.1.4", @@ -21,6 +21,7 @@ "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "@types/socket.io-client": "^3.0.0", + "primeflex": "^3.3.1", "primeicons": "^6.0.1", "primeng": "^15.2.0", "rxjs": "~7.5.0", @@ -9302,6 +9303,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/primeflex": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz", + "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==" + }, "node_modules/primeicons": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz", diff --git a/kdb-web/package.json b/kdb-web/package.json index 8e415632..fc35367e 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -1,6 +1,6 @@ { "name": "kdb-web", - "version": "1.0.7", + "version": "1.1.0", "scripts": { "ng": "ng", "update-version": "ts-node-esm update-version.ts", @@ -51,4 +51,4 @@ "tslib": "^2.4.1", "typescript": "~4.9.5" } -} +} \ No newline at end of file diff --git a/kdb-web/src/app/app.component.html b/kdb-web/src/app/app.component.html index 8c0cfa4f..3c199a6f 100644 --- a/kdb-web/src/app/app.component.html +++ b/kdb-web/src/app/app.component.html @@ -5,7 +5,7 @@
-
diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index d6b87c69..56419fb9 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -16,7 +16,7 @@ import { takeUntil } from "rxjs/operators"; export class AppComponent implements OnInit, OnDestroy { themeName: string = Themes.Default; - sidebarWidth: string = '175px'; + isSidebarOpen: boolean = true; isLoggedIn: boolean = false; @@ -29,10 +29,10 @@ export class AppComponent implements OnInit, OnDestroy { private translateService: TranslateService, private config: PrimeNGConfig, ) { - this.themeService.sidebarWidth$.pipe( + this.themeService.isSidebarOpen$.pipe( takeUntil(this.unsubscriber) ).subscribe(value => { - this.sidebarWidth = value; + this.isSidebarOpen = value; }); this.themeService.themeName$.pipe( takeUntil(this.unsubscriber) diff --git a/kdb-web/src/app/base/component-with-table.spec.ts b/kdb-web/src/app/base/component-with-table.spec.ts new file mode 100644 index 00000000..5b08b905 --- /dev/null +++ b/kdb-web/src/app/base/component-with-table.spec.ts @@ -0,0 +1,7 @@ +import { ComponentWithTable } from './component-with-table'; + +describe('ComponentWithTable', () => { + it('should create an instance', () => { + expect(new ComponentWithTable()).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/base/component-with-table.ts b/kdb-web/src/app/base/component-with-table.ts new file mode 100644 index 00000000..9386a28f --- /dev/null +++ b/kdb-web/src/app/base/component-with-table.ts @@ -0,0 +1,44 @@ +export interface Column { + key: string; + name: string; +} + +export class ComponentWithTable { + + private _hiddenColumns: Column[] = []; + set hiddenColumns(value: Column[]) { + this._hiddenColumns = value; + localStorage.setItem("hiddenColumns", JSON.stringify(value)); + } + + get hiddenColumns(): Column[] { + return this._hiddenColumns; + } + + public name: string = ""; + public columns: Column[] = []; + + constructor( + name: string, + columns: string[] + ) { + this.name = name; + this.columns = columns.map(column => { + return { key: this.getKey(column), name: column }; + }); + let hiddenColumns = localStorage.getItem("hiddenColumns"); + if (!hiddenColumns) { + localStorage.setItem("hiddenColumns", JSON.stringify([{}])); + hiddenColumns = localStorage.getItem("hiddenColumns") ?? JSON.stringify([{}]); + } + this._hiddenColumns = JSON.parse(hiddenColumns); + } + + private getKey(column: string): string { + return `${this.name}_${column}`; + } + + public isColumnVisible(column: string): boolean { + return !this._hiddenColumns.map(column => column.key).includes(this.getKey(column)); + } +} diff --git a/kdb-web/src/app/components/footer/footer.component.html b/kdb-web/src/app/components/footer/footer.component.html index 2b104231..dda279bb 100644 --- a/kdb-web/src/app/components/footer/footer.component.html +++ b/kdb-web/src/app/components/footer/footer.component.html @@ -8,7 +8,7 @@ {{frontendVersion.getVersionString()}} - + |
@@ -22,7 +22,7 @@
- | + |
diff --git a/kdb-web/src/app/models/auth/auth-user.dto.ts b/kdb-web/src/app/models/auth/auth-user.dto.ts index 4b1e90e4..50b913e0 100644 --- a/kdb-web/src/app/models/auth/auth-user.dto.ts +++ b/kdb-web/src/app/models/auth/auth-user.dto.ts @@ -25,3 +25,9 @@ export interface UserDTO { isAdmin: boolean; isModerator: boolean; } + +export enum MemberRoles { + Moderator = 0, + Admin = 1, + Technician = 2, +} diff --git a/kdb-web/src/app/models/config/app-settings.ts b/kdb-web/src/app/models/config/app-settings.ts new file mode 100644 index 00000000..7f45d9a1 --- /dev/null +++ b/kdb-web/src/app/models/config/app-settings.ts @@ -0,0 +1,21 @@ +import { SoftwareVersion } from "./software-version"; +import { Theme } from '../view/theme'; + +export interface AppAndVersionSettings { + ApiURL: string; + PrivacyURL: string; + ImprintURL: string; + Themes: Theme[]; + WebVersion: SoftwareVersion; +} + +export interface AppSettings { + ApiURL: string; + PrivacyURL: string; + ImprintURL: string; + Themes: Theme[]; +} + +export interface VersionSettings { + WebVersion: SoftwareVersion; +} diff --git a/kdb-web/src/app/models/config/appsettings.ts b/kdb-web/src/app/models/config/appsettings.ts deleted file mode 100644 index f3f5796b..00000000 --- a/kdb-web/src/app/models/config/appsettings.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SoftwareVersion } from "./software-version"; -import { Theme } from '../view/theme'; - -export interface Appsettings { - ApiURL: string; - PrivacyURL: string; - ImprintURL: string; - WebVersion: SoftwareVersion; - Themes: Theme[]; -} diff --git a/kdb-web/src/app/models/config/feature-flags.model.ts b/kdb-web/src/app/models/config/feature-flags.model.ts new file mode 100644 index 00000000..010843a3 --- /dev/null +++ b/kdb-web/src/app/models/config/feature-flags.model.ts @@ -0,0 +1,4 @@ +export interface FeatureFlag { + key: string; + value: boolean; +} diff --git a/kdb-web/src/app/models/config/server-config.model.ts b/kdb-web/src/app/models/config/server-config.model.ts new file mode 100644 index 00000000..c2b1d1c8 --- /dev/null +++ b/kdb-web/src/app/models/config/server-config.model.ts @@ -0,0 +1,23 @@ +import { DataWithHistory } from "../data/data.model"; +import { FeatureFlag } from "./feature-flags.model"; + +export interface ServerConfig extends DataWithHistory { + id?: number; + messageDeleteTimer?: number; + notificationChatId?: string; + maxVoiceStateHours?: number; + xpPerMessage?: number; + xpPerReaction?: number; + maxMessageXpPerHour?: number; + xpPerOntimeHour?: number; + xpPerEventParticipation?: number; + xpPerAchievement?: number; + afkCommandChannelId?: string; + helpVoiceChannelId?: string; + teamChannelId?: string; + loginMessageChannelId?: string; + featureFlags: FeatureFlag[]; + afkChannelIds: string[]; + moderatorRoleIds: string[]; + adminRoleIds: string[]; +} diff --git a/kdb-web/src/app/models/config/technician-config.model.ts b/kdb-web/src/app/models/config/technician-config.model.ts new file mode 100644 index 00000000..e70ae752 --- /dev/null +++ b/kdb-web/src/app/models/config/technician-config.model.ts @@ -0,0 +1,13 @@ +import { DataWithHistory } from "../data/data.model"; +import { FeatureFlag } from "./feature-flags.model"; + +export interface TechnicianConfig extends DataWithHistory { + id?: number; + helpCommandReferenceUrl?: string; + waitForRestart?: number; + waitForShutdown?: number; + cacheMaxMessages?: number; + featureFlags: FeatureFlag[]; + pingURLs: string[]; + technicianIds: string[]; +} diff --git a/kdb-web/src/app/models/data/achievement.model.ts b/kdb-web/src/app/models/data/achievement.model.ts new file mode 100644 index 00000000..c32b6440 --- /dev/null +++ b/kdb-web/src/app/models/data/achievement.model.ts @@ -0,0 +1,29 @@ +import { DataWithHistory } from "./data.model"; +import { Server, ServerFilter } from "./server.model"; + +export interface AchievementAttribute { + name?: string; + type?: string; +} + +export interface Achievement extends DataWithHistory { + id?: number; + name?: string; + description?: string; + attribute?: string | AchievementAttribute; + operator?: string; + value?: string; + server?: Server; + + createdAt?: string; +} + +export interface AchievementFilter { + id?: number; + name?: string; + description?: string; + attribute?: string; + operator?: string; + value?: string; + server?: ServerFilter; +} diff --git a/kdb-web/src/app/models/data/discord.model.ts b/kdb-web/src/app/models/data/discord.model.ts index 680225ef..6e640052 100644 --- a/kdb-web/src/app/models/data/discord.model.ts +++ b/kdb-web/src/app/models/data/discord.model.ts @@ -1,3 +1,8 @@ +export interface Discord { + guilds?: Guild[]; + users?: DiscordUser[]; +} + export interface Guild { id?: string; name?: string; @@ -14,9 +19,9 @@ export interface Channel { } export enum ChannelType { - category = "category", - text = "text", - voice = "voice" + category = "CategoryChannel", + text = "TextChannel", + voice = "VoiceChannel" } export interface Role { @@ -29,3 +34,9 @@ export interface Emoji { name?: string; url?: string; } + +export interface DiscordUser { + id?: string; + name?: string; + bot?: boolean; +} diff --git a/kdb-web/src/app/models/data/server.model.ts b/kdb-web/src/app/models/data/server.model.ts index cc160e68..a045186d 100644 --- a/kdb-web/src/app/models/data/server.model.ts +++ b/kdb-web/src/app/models/data/server.model.ts @@ -4,6 +4,11 @@ import {Level} from "./level.model"; import {Client} from "./client.model"; import { AutoRole } from "./auto_role.model"; +export interface GameServer { + id?: number; + name?: string; +} + export interface Server extends Data { id?: number; discordId?: String; diff --git a/kdb-web/src/app/models/data/user.model.ts b/kdb-web/src/app/models/data/user.model.ts index fb36964e..6d649da4 100644 --- a/kdb-web/src/app/models/data/user.model.ts +++ b/kdb-web/src/app/models/data/user.model.ts @@ -4,12 +4,15 @@ import { Server, ServerFilter } from "./server.model"; import { UserJoinedServer } from "./user_joined_server.model"; import { UserJoinedVoiceChannel } from "./user_joined_voice_channel.model"; import { UserJoinedGameServer } from "./user_joined_game_server.model"; +import { Achievement } from "./achievement.model"; export interface User extends DataWithHistory { id?: number; discordId?: number; name?: string; xp?: number; + message_count?: number; + reaction_count?: number; ontime?: number; level?: Level; server?: Server; @@ -23,6 +26,9 @@ export interface User extends DataWithHistory { userJoinedGameServerCount?: number; userJoinedGameServers?: UserJoinedGameServer[]; + + achievementCount?: number; + achievements?: Achievement[]; } export interface UserFilter { diff --git a/kdb-web/src/app/models/graphql/mutations.model.ts b/kdb-web/src/app/models/graphql/mutations.model.ts index 0cba6ef3..20263626 100644 --- a/kdb-web/src/app/models/graphql/mutations.model.ts +++ b/kdb-web/src/app/models/graphql/mutations.model.ts @@ -121,4 +121,149 @@ export class Mutations { } } `; + + static createAchievement = ` + mutation createAchievement($name: String, $description: String, $attribute: String, $operator: String, $value: String, $serverId: ID) { + achievement { + createAchievement(input: { name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value, serverId: $serverId}) { + id + name + description + attribute + operator + value + server { + id + } + } + } + } + `; + + static updateAchievement = ` + mutation updateAchievement($id: ID, $name: String, $description: String, $attribute: String, $operator: String, $value: String) { + achievement { + updateAchievement(input: { id: $id, name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value}) { + id + name + description + attribute + operator + value + } + } + } + `; + + static deleteAchievement = ` + mutation deleteAchievement($id: ID) { + achievement { + deleteAchievement(id: $id) { + id + name + } + } + } + `; + + static updateTechnicianConfig = ` + mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $featureFlags: [FeatureFlagInput], $pingURLs: [String], $technicianIds: [String]) { + technicianConfig { + updateTechnicianConfig(input: { + id: $id, + helpCommandReferenceUrl: $helpCommandReferenceUrl, + waitForRestart: $waitForRestart, + waitForShutdown: $waitForShutdown, + cacheMaxMessages: $cacheMaxMessages, + featureFlags: $featureFlags, + pingURLs: $pingURLs, + technicianIds: $technicianIds + }) { + id + helpCommandReferenceUrl + waitForRestart + waitForShutdown + cacheMaxMessages + featureFlags { + key + value + } + pingURLs + technicianIds + } + } + } + `; + + static updateServerConfig = ` + mutation updateServerConfig( + $id: ID, + $messageDeleteTimer: Int, + $notificationChatId: String, + $maxVoiceStateHours: Int, + $xpPerMessage: Int, + $xpPerReaction: Int, + $maxMessageXpPerHour: Int, + $xpPerOntimeHour: Int, + $xpPerEventParticipation: Int, + $xpPerAchievement: Int, + $afkCommandChannelId: String, + $helpVoiceChannelId: String, + $teamChannelId: String, + $loginMessageChannelId: String, + $featureFlags: [FeatureFlagInput], + $afkChannelIds: [String], + $moderatorRoleIds: [String], + $adminRoleIds: [String] + ) { + serverConfig { + updateServerConfig(input: { + id: $id, + messageDeleteTimer: $messageDeleteTimer, + notificationChatId: $notificationChatId, + maxVoiceStateHours: $maxVoiceStateHours, + xpPerMessage: $xpPerMessage, + xpPerReaction: $xpPerReaction, + maxMessageXpPerHour: $maxMessageXpPerHour, + xpPerOntimeHour: $xpPerOntimeHour, + xpPerEventParticipation: $xpPerEventParticipation, + xpPerAchievement: $xpPerAchievement, + afkCommandChannelId: $afkCommandChannelId, + helpVoiceChannelId: $helpVoiceChannelId, + teamChannelId: $teamChannelId, + loginMessageChannelId: $loginMessageChannelId, + featureFlags: $featureFlags, + afkChannelIds: $afkChannelIds, + moderatorRoleIds: $moderatorRoleIds, + adminRoleIds: $adminRoleIds + }) { + id + messageDeleteTimer + notificationChatId + maxVoiceStateHours + xpPerMessage + xpPerReaction + maxMessageXpPerHour + xpPerOntimeHour + xpPerEventParticipation + xpPerAchievement + afkCommandChannelId + helpVoiceChannelId + teamChannelId + loginMessageChannelId + featureFlags { + key + value + } + afkChannelIds + moderatorRoleIds + adminRoleIds + + server { + id + } + } + } + } + `; } diff --git a/kdb-web/src/app/models/graphql/queries.model.ts b/kdb-web/src/app/models/graphql/queries.model.ts index f3d68257..bd83b01d 100644 --- a/kdb-web/src/app/models/graphql/queries.model.ts +++ b/kdb-web/src/app/models/graphql/queries.model.ts @@ -1,24 +1,59 @@ export class Queries { static guildsQuery = ` - query GuildsQuery($id: ID) { - guilds(filter: {id: $id}) { - id - name + query GuildsQuery($id: ID, $filter: ChannelFilter) { + discord { + guilds(filter: {id: $id}) { + id + name - channels { - id - name - type + channels(filter: $filter) { + id + name + type + } + roles { + id + name + } + emojis { + id + name + url + } } - roles { + } + } + `; + + static discordUsersQuery = ` + query DiscordUsersQuery { + discord { + users { id name } - emojis { + } + } + `; + + static serverConfigDiscordQuery = ` + query ServerConfigDiscordQuery($id: ID) { + discord { + guilds(filter: {id: $id}) { id name - url + + roles { + id + name + } + + channels { + id + name + type + } } } } @@ -29,10 +64,11 @@ export class Queries { serverCount servers(filter: $filter, page: $page, sort: $sort) { id + discordId name iconURL userCount - clients{ + clients { id discordId name @@ -46,6 +82,16 @@ export class Queries { } `; + static gameServerQuery = ` + query GameServersList($serverId: ID) { + servers(filter: {id: $serverId}) { + gameServers { + name + } + } + } + `; + static levelQuery = ` query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) { servers(filter: {id: $serverId}) { @@ -90,6 +136,62 @@ export class Queries { } `; + static achievementTypeQuery = ` + query AchievementType { + achievementOperators + achievementAttributes { + name + type + } + } + `; + + static achievementQuery = ` + query AchievementList($serverId: ID, $filter: AchievementFilter, $page: Page, $sort: Sort) { + servers(filter: {id: $serverId}) { + achievementCount + achievements(filter: $filter, page: $page, sort: $sort) { + id + name + description + attribute + operator + value + server { + id + name + } + createdAt + modifiedAt + } + } + } + `; + + static achievementWithHistoryQuery = ` + query AchievementHistory($serverId: ID, $id: ID) { + servers(filter: {id: $serverId}) { + achievementCount + achievements(filter: {id: $id}) { + id + + history { + id + name + description + attribute + operator + value + server + deleted + dateFrom + dateTo + } + } + } + } + `; + static usersQuery = ` query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) { servers(filter: {id: $serverId}) { @@ -158,6 +260,15 @@ export class Queries { joinedOn leavedOn } + + achievements { + id + name + createdAt + } + + createdAt + modifiedAt } } } @@ -272,4 +383,60 @@ export class Queries { } } `; + + static technicianConfigQuery = ` + query technicianConfigQuery { + technicianConfig { + id + helpCommandReferenceUrl + waitForRestart + waitForShutdown + cacheMaxMessages + featureFlags { + key + value + } + pingURLs + technicianIds + + createdAt + modifiedAt + } + } + `; + + static serverConfigQuery = ` + query serverConfigQuery($serverId: ID) { + servers(filter: { id: $serverId }) { + name + config { + id + messageDeleteTimer + notificationChatId + maxVoiceStateHours + xpPerMessage + xpPerReaction + maxMessageXpPerHour + xpPerOntimeHour + xpPerEventParticipation + xpPerAchievement + afkCommandChannelId + helpVoiceChannelId + teamChannelId + loginMessageChannelId + featureFlags { + key + value + } + afkChannelIds + moderatorRoleIds + adminRoleIds + + server { + id + } + } + } + } + `; } diff --git a/kdb-web/src/app/models/graphql/query.model.ts b/kdb-web/src/app/models/graphql/query.model.ts index 3f7e85a1..fe0c9683 100644 --- a/kdb-web/src/app/models/graphql/query.model.ts +++ b/kdb-web/src/app/models/graphql/query.model.ts @@ -1,16 +1,27 @@ -import { Server } from "../data/server.model"; +import { GameServer, Server } from "../data/server.model"; import { User } from "../data/user.model"; import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; -import { Guild } from "../data/discord.model"; +import { Discord, Guild } from "../data/discord.model"; import { Level } from "../data/level.model"; +import { Achievement, AchievementAttribute } from "../data/achievement.model"; +import { TechnicianConfig } from "../config/technician-config.model"; +import { ServerConfig } from "../config/server-config.model"; export interface Query { serverCount: number; servers: Server[]; } +export interface TechnicianConfigQuery { + technicianConfig: TechnicianConfig; +} + +export interface ServerConfigQuery { + config: ServerConfig; +} + export interface SingleDiscordQuery { - guilds: Guild[]; + discord: Discord; } export interface UserListQuery { @@ -18,11 +29,26 @@ export interface UserListQuery { users: User[]; } +export interface GameServerListQuery { + gameServerCount: number; + gameServers: GameServer[]; +} + export interface LevelListQuery { levelCount: number; levels: Level[]; } +export interface AchievementTypeQuery { + achievementAttributes: AchievementAttribute[]; + achievementOperators: string[]; +} + +export interface AchievementListQuery { + achievementCount: number; + achievements: Achievement[]; +} + export interface AutoRoleQuery { autoRoleCount: number; autoRoles: AutoRole[]; @@ -33,3 +59,8 @@ export interface AutoRoleRuleQuery { autoRoleRules: AutoRoleRule[]; } + +export interface PossibleFeatureFlagsQuery { + possibleFeatureFlags: string[]; +} + diff --git a/kdb-web/src/app/models/graphql/result.model.ts b/kdb-web/src/app/models/graphql/result.model.ts index d3d6e4a4..573f98ce 100644 --- a/kdb-web/src/app/models/graphql/result.model.ts +++ b/kdb-web/src/app/models/graphql/result.model.ts @@ -2,6 +2,9 @@ import { User } from "../data/user.model"; import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; import { Level } from "../data/level.model"; import { Server } from "../data/server.model"; +import { Achievement } from "../data/achievement.model"; +import { TechnicianConfig } from "../config/technician-config.model"; +import { ServerConfig } from "../config/server-config.model"; export interface GraphQLResult { data: { @@ -45,3 +48,23 @@ export interface LevelMutationResult { deleteLevel?: Level }; } + +export interface TechnicianConfigMutationResult { + technicianConfig: { + updateTechnicianConfig?: TechnicianConfig + }; +} + +export interface ServerConfigMutationResult { + serverConfig: { + updateServerConfig?: ServerConfig + }; +} + +export interface AchievementMutationResult { + achievement: { + createAchievement?: Achievement + updateAchievement?: Achievement + deleteAchievement?: Achievement + }; +} diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html index 58f5eb9d..a61ff338 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html @@ -3,17 +3,20 @@
-
-
- {{users.length}} {{'common.of' | translate}} - {{dt.totalRecords}} - - {{'admin.auth_users.users' | translate}} +
+
+ {{users.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'admin.auth_users.users' | translate}} +
+
@@ -29,44 +32,44 @@ - +
-
{{'admin.auth_users.headers.first_name' | translate}}
+
{{'common.first_name' | translate}}
- +
-
{{'admin.auth_users.headers.last_name' | translate}}
+
{{'common.last_name' | translate}}
- +
{{'common.email' | translate}}
- +
-
{{'admin.auth_users.headers.active' | translate}}
+
{{'common.active' | translate}}
- +
-
{{'admin.auth_users.headers.role' | translate}}
+
{{'common.role' | translate}}
- +
-
{{'admin.auth_users.headers.password' | translate}}
+
{{'common.password' | translate}}
@@ -89,28 +92,28 @@ - +
- +
- +
- +
- +
- - + +
- +
- + @@ -119,7 +122,8 @@ - + + {{'common.first_name' | translate}}: - + + {{'common.last_name' | translate}}: - + + {{'common.email' | translate}}: - + + {{'common.active' | translate}}: - + + {{'common.auth_role' | translate}}: - + + {{'common.password' | translate}}: + {{'common.created_at' | translate}}: {{user.createdAt | date:'dd.MM.yy HH:mm'}} @@ -197,6 +207,7 @@ + {{'common.modified_at' | translate}}: {{user.modifiedAt | date:'dd.MM.yy HH:mm'}} diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts index e4a53ca7..1dcfceaa 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts @@ -15,6 +15,7 @@ import { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth import { LazyLoadEvent } from "primeng/api"; import { Subject, throwError } from "rxjs"; import { TranslateService } from "@ngx-translate/core"; +import { ComponentWithTable } from "../../../../../base/component-with-table"; @Component({ @@ -22,7 +23,7 @@ import { TranslateService } from "@ngx-translate/core"; templateUrl: "./auth-user.component.html", styleUrls: ["./auth-user.component.scss"] }) -export class AuthUserComponent implements OnInit, OnDestroy { +export class AuthUserComponent extends ComponentWithTable implements OnInit, OnDestroy { users!: AuthUserDTO[]; statuses!: any[]; @@ -72,6 +73,7 @@ export class AuthUserComponent implements OnInit, OnDestroy { private fb: FormBuilder, private translate: TranslateService ) { + super("auth-users", ["first_name", "last_name", "email", "active", "auth_role", "password"]); } ngOnInit(): void { @@ -263,7 +265,7 @@ export class AuthUserComponent implements OnInit, OnDestroy { this.spinnerService.hideSpinner(); this.toastService.error(this.translate.instant("admin.auth_users.message.user_change_failed"), this.translate.instant("admin.auth_users.message.user_change_failed_d", { email: newUser.email })); this.initUserList(); - return throwError(() => error); + return throwError(() => error); })) .subscribe(_ => { this.initUserList(); @@ -317,7 +319,7 @@ export class AuthUserComponent implements OnInit, OnDestroy { addUser(table: Table) { const newUser = JSON.parse(JSON.stringify(this.newUserTemplate)); - newUser.id = Math.max.apply(Math, this.users.map(u => { + newUser.id = this.users.length == 0 ? 1 : Math.max.apply(Math, this.users.map(u => { return u.id ?? 0; })) + 1; diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html index 3c78d1a6..121cdc0f 100644 --- a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html @@ -1,125 +1,182 @@

- {{'admin.settings.header' | translate}} + {{'admin.settings.header' | translate}}

-
-

- {{'admin.settings.website.header' | translate}} -

+
+

+ {{'admin.settings.website.header' | translate}} +

+
+ +
+
+
+
{{'admin.settings.website.frontend_version' | translate}}:
+
{{data.webVersion}}
+
-
-
-
-
{{'admin.settings.website.frontend_version' | translate}}:
-
{{data.webVersion}}
-
-
- -
-
-
{{'admin.settings.website.backend_version' | translate}}:
-
{{data.apiVersion}}
-
-
- -
-
-
{{'admin.settings.website.config_path' | translate}}:
-
{{data.configPath}}
-
-
- -
-
-
{{'admin.settings.website.frontend_base_url' | translate}}:
-
{{data.webBaseURL}}
-
-
- -
-
-
{{'admin.settings.website.backend_base_url' | translate}}:
-
{{data.apiBaseURL}}
-
-
- -
- -
-
-
{{'admin.settings.website.token_expire_time' | translate}}:
-
{{data.tokenExpireTime}} {{'general.minutes' | translate}}
-
-
- -
-
-
{{'admin.settings.website.refresh_token_expire_time' | translate}}:
-
{{data.refreshTokenExpireTime}} {{'general.days' | translate}}
-
-
+
+
+
{{'admin.settings.website.backend_version' | translate}}:
+
{{data.apiVersion}}
+
+ +
+
+
{{'admin.settings.website.config_path' | translate}}:
+
{{data.configPath}}
+
+
+ +
+
+
{{'admin.settings.website.frontend_base_url' | translate}}:
+
{{data.webBaseURL}}
+
+
+ +
+
+
{{'admin.settings.website.backend_base_url' | translate}}:
+
{{data.apiBaseURL}}
+
+
+ +
+ +
+
+
{{'admin.settings.website.token_expire_time' | translate}}:
+
{{data.tokenExpireTime}} {{'general.minutes' | translate}}
+
+
+ +
+
+
{{'admin.settings.website.refresh_token_expire_time' | translate}}:
+
{{data.refreshTokenExpireTime}} {{'general.days' | translate}}
+
+
+
-
-

- {{'admin.settings.email.header' | translate}} -

+
+

+ {{'admin.settings.email.header' | translate}} +

+
+ +
+
+
+
{{'admin.settings.email.user' | translate}}:
+
{{data.mailUser}}
+
-
-
-
-
{{'admin.settings.email.user' | translate}}:
-
{{data.mailUser}}
-
-
- -
-
-
{{'admin.settings.email.host' | translate}}:
-
{{data.mailHost}}
-
-
- -
-
-
{{'admin.settings.email.port' | translate}}:
-
{{data.mailPort}}
-
-
- -
-
-
{{'admin.settings.email.transceiver' | translate}}:
-
{{data.mailTransceiver}}
-
-
- -
-
-
{{'admin.settings.email.email_address' | translate}}:
-
{{data.mailTransceiverAddress}}
-
-
- -
-
-
-
- -
-
- -
- -
-
-
+
+
+
{{'admin.settings.email.host' | translate}}:
+
{{data.mailHost}}
+
+ +
+
+
{{'admin.settings.email.port' | translate}}:
+
{{data.mailPort}}
+
+
+ +
+
+
{{'admin.settings.email.transceiver' | translate}}:
+
{{data.mailTransceiver}}
+
+
+ +
+
+
{{'admin.settings.email.email_address' | translate}}:
+
{{data.mailTransceiverAddress}}
+
+
+ +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+ +
+
+

+ {{'admin.settings.bot.header' | translate}} +

+
+ +
+
+
+
{{'admin.settings.bot.help_url' | translate}}:
+
+ +
+
+
+ +
+
+
{{'admin.settings.bot.wait_for_restart' | translate}}:
+
+ +
+
+
+ +
+
+
{{'admin.settings.bot.wait_for_shutdown' | translate}}:
+
+ +
+
+
+ +
+
+
{{'admin.settings.bot.cache_max_messages' | translate}}:
+
+ +
+
+
+ +
+ + + + +
+ +
+
diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts index 0f88f42e..38258504 100644 --- a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts @@ -9,65 +9,96 @@ import { GuiService } from "src/app/services/gui/gui.service"; import { SettingsService } from "src/app/services/settings/settings.service"; import { SpinnerService } from "src/app/services/spinner/spinner.service"; import { ToastService } from "src/app/services/toast/toast.service"; -import { throwError } from "rxjs"; +import { forkJoin, throwError } from "rxjs"; +import { TechnicianConfig } from "../../../../../models/config/technician-config.model"; +import { SingleDiscordQuery, TechnicianConfigQuery } from "../../../../../models/graphql/query.model"; +import { Queries } from "../../../../../models/graphql/queries.model"; +import { DataService } from "../../../../../services/data/data.service"; +import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model"; +import { Mutations } from "../../../../../models/graphql/mutations.model"; +import { AuthService } from "../../../../../services/auth/auth.service"; +import { ChannelType, DiscordUser } from "../../../../../models/data/discord.model"; + @Component({ - selector: 'app-settings', - templateUrl: './settings.component.html', - styleUrls: ['./settings.component.scss'] + selector: "app-settings", + templateUrl: "./settings.component.html", + styleUrls: ["./settings.component.scss"] }) export class SettingsComponent implements OnInit { testMailForm!: FormGroup; data: SettingsDTO = { - webVersion: '', - apiVersion: '', - configPath: '', - webBaseURL: '', - apiBaseURL: '', + webVersion: "", + apiVersion: "", + configPath: "", + webBaseURL: "", + apiBaseURL: "", tokenExpireTime: 0, refreshTokenExpireTime: 0, - mailUser: '', + mailUser: "", mailPort: 0, - mailHost: '', - mailTransceiver: '', - mailTransceiverAddress: '', + mailHost: "", + mailTransceiver: "", + mailTransceiverAddress: "" }; + config: TechnicianConfig = { + helpCommandReferenceUrl: "", + waitForRestart: 0, + waitForShutdown: 0, + cacheMaxMessages: 0, + featureFlags: [], + pingURLs: [], + technicianIds: [] + }; + + possibleTechnicians?: DiscordUser[]; + constructor( + private dataService: DataService, private settingsService: SettingsService, private spinnerService: SpinnerService, private guiService: GuiService, private formBuilder: FormBuilder, private toastService: ToastService, - private translate: TranslateService - ) { } + private translate: TranslateService, + private authService: AuthService, + private spinner: SpinnerService + ) { + } ngOnInit(): void { this.spinnerService.showSpinner(); this.initForms(); - this.guiService.getSettings() - .pipe(catchError(error => { + forkJoin([ + this.guiService.getSettings().pipe(catchError(error => { this.spinnerService.hideSpinner(); return throwError(() => error); - })) - .subscribe(settings => { - this.spinnerService.hideSpinner(); - this.data = settings; - this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? '0.0.0'; - this.data.apiBaseURL = this.settingsService.getApiURL(); - if (!this.data.apiBaseURL.endsWith('/')) { - this.data.apiBaseURL += '/'; - } - }); + })), + this.dataService.query(Queries.technicianConfigQuery), + this.dataService.query(Queries.discordUsersQuery) + ]).subscribe(data => { + this.data = data[0]; + this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? "0.0.0"; + this.data.apiBaseURL = this.settingsService.getApiURL(); + if (!this.data.apiBaseURL.endsWith("/")) { + this.data.apiBaseURL += "/"; + } + + this.config = data[1].technicianConfig; + this.possibleTechnicians = data[2].discord.users ?? undefined; + this.initForms(); + this.spinnerService.hideSpinner(); + }); } initForms(): void { this.testMailForm = this.formBuilder.group({ - mail: [null, [Validators.required, Validators.email]], + mail: [null, [Validators.required, Validators.email]] }); } @@ -82,27 +113,27 @@ export class SettingsComponent implements OnInit { this.guiService.sendTestMail(mail) .pipe(catchError(error => { - let header = this.translate.instant('admin.settings.message.error'); - let message = this.translate.instant('admin.settings.message.could_not_send_mail'); + let header = this.translate.instant("admin.settings.message.error"); + let message = this.translate.instant("admin.settings.message.could_not_send_mail"); if (error.error !== null) { const err: ErrorDTO = error.error; if (err.errorCode === ServiceErrorCode.ConnectionFailed) { - header = this.translate.instant('admin.settings.message.connection_failed'); - message = this.translate.instant('admin.settings.message.connection_to_mail_failed'); + header = this.translate.instant("admin.settings.message.connection_failed"); + message = this.translate.instant("admin.settings.message.connection_to_mail_failed"); error.error = null; } if (err.errorCode === ServiceErrorCode.InvalidUser) { - header = this.translate.instant('admin.settings.message.connection_failed'); - message = this.translate.instant('admin.settings.message.mail_login_failed'); + header = this.translate.instant("admin.settings.message.connection_failed"); + message = this.translate.instant("admin.settings.message.mail_login_failed"); error.error = null; } if (err.errorCode === ServiceErrorCode.MailError) { - header = this.translate.instant('admin.settings.message.send_failed'); - message = this.translate.instant('admin.settings.message.test_mail_not_send'); + header = this.translate.instant("admin.settings.message.send_failed"); + message = this.translate.instant("admin.settings.message.test_mail_not_send"); error.error = null; } } @@ -113,9 +144,30 @@ export class SettingsComponent implements OnInit { })) .subscribe(res => { this.spinnerService.hideSpinner(); - this.toastService.success(this.translate.instant('admin.settings.message.success'), this.translate.instant('admin.settings.message.send_mail')); + this.toastService.success(this.translate.instant("admin.settings.message.success"), this.translate.instant("admin.settings.message.send_mail")); this.testMailForm.reset(); }); } + saveTechnicianConfig() { + this.spinner.showSpinner(); + this.dataService.mutation(Mutations.updateTechnicianConfig, { + helpCommandReferenceUrl: this.config.helpCommandReferenceUrl, + waitForRestart: this.config.waitForRestart, + waitForShutdown: this.config.waitForShutdown, + cacheMaxMessages: this.config.cacheMaxMessages, + featureFlags: this.config.featureFlags, + pingURLs: this.config.pingURLs, + technicianIds: this.config.technicianIds + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(result => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("admin.settings.message.technician_config_create"), this.translate.instant("admin.settings.message.technician_config_create_d")); + }); + } + + } diff --git a/kdb-web/src/app/modules/shared/base/hideable-component.spec.ts b/kdb-web/src/app/modules/shared/base/hideable-component.spec.ts new file mode 100644 index 00000000..51af6d5c --- /dev/null +++ b/kdb-web/src/app/modules/shared/base/hideable-component.spec.ts @@ -0,0 +1,7 @@ +import { HideableComponent } from './hideable-component'; + +describe('HideableComponent', () => { + it('should create an instance', () => { + expect(new HideableComponent()).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/base/hideable-component.ts b/kdb-web/src/app/modules/shared/base/hideable-component.ts new file mode 100644 index 00000000..4421ff91 --- /dev/null +++ b/kdb-web/src/app/modules/shared/base/hideable-component.ts @@ -0,0 +1,13 @@ +import { Directive, HostBinding, Input } from "@angular/core"; +import { ComponentWithTable } from "../../../base/component-with-table"; + +@Directive() +export class HideableComponent { + @HostBinding("class.hidden-column") + get hidden() { + return !(this.parent?.isColumnVisible(this.column) ?? true); + }; + + @Input() column!: string; + @Input() parent!: ComponentWithTable; +} diff --git a/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.html b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.html new file mode 100644 index 00000000..67e7a114 --- /dev/null +++ b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.html @@ -0,0 +1,17 @@ +
+ + +
+
+ {{'common.' + item.name | translate}}, +
+
{{'common.hidden_columns' | translate}}
+
+
{{'common.hidden_columns' | translate}}
+
+ + +
{{'common.' + item.name | translate}}
+
+
+
diff --git a/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.scss b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.spec.ts b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.spec.ts new file mode 100644 index 00000000..fd671a32 --- /dev/null +++ b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MultiSelectColumnsComponent } from './multi-select-columns.component'; + +describe('MultiSelectColumnsComponent', () => { + let component: MultiSelectColumnsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MultiSelectColumnsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MultiSelectColumnsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.ts b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.ts new file mode 100644 index 00000000..8a38ce91 --- /dev/null +++ b/kdb-web/src/app/modules/shared/base/multi-select-columns/multi-select-columns.component.ts @@ -0,0 +1,30 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { Column } from "../../../../base/component-with-table"; + +@Component({ + selector: "app-multi-select-columns", + templateUrl: "./multi-select-columns.component.html", + styleUrls: ["./multi-select-columns.component.scss"] +}) +export class MultiSelectColumnsComponent { + @Input() columns: Column[] = []; + + private _hiddenColumns: Column[] = []; + @Input() table: String = ""; + + @Input() + set hiddenColumns(columns: Column[]) { + this._hiddenColumns = columns; + this.hiddenColumnsChange.emit(columns); + } + + get hiddenColumns(): Column[] { + return this._hiddenColumns; + } + + get visibleHiddenColumns(): Column[] { + return this._hiddenColumns.filter(x => x.key?.startsWith(`${this.table}_`)); + } + + @Output() hiddenColumnsChange: EventEmitter = new EventEmitter(); +} diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html new file mode 100644 index 00000000..4e64ae73 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html @@ -0,0 +1,48 @@ +
+
+
+ {{translationKey | translate}}: +
+
+ + +
+
+ +
+
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+
+
+
+
+
diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.scss b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts new file mode 100644 index 00000000..b571849c --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfigListComponent } from './config-list.component'; + +describe('ConfigListComponent', () => { + let component: ConfigListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfigListComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConfigListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts new file mode 100644 index 00000000..dc83ca48 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts @@ -0,0 +1,75 @@ +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Table } from "primeng/table"; + +@Component({ + selector: "app-config-list", + templateUrl: "./config-list.component.html", + styleUrls: ["./config-list.component.scss"] +}) +export class ConfigListComponent { + internal_data: any[] = []; + + @Input() translationKey: string = ""; + @Input() options?: Array; + @Input() optionLabel?: string = ""; + @Input() optionValue?: string = ""; + + @Input() + set data(val: any[]) { + this.dataChange.emit(val); + let id = 0; + this.internal_data = val.map(value => { + value = { id: id, value: value }; + id++; + return value; + }); + } + + get data() { + return this.getData(); + } + + @Output() dataChange: EventEmitter = new EventEmitter(); + clonedData: { [s: number]: any } = {}; + + private getData(): any[] { + return this.internal_data.map(value => { + return value.value; + }); + } + + addNew(table: Table) { + const id = this.internal_data.length == 0 ? 1 : Math.max.apply(Math, this.internal_data.map(value => { + return value.id ?? 0; + })) + 1; + const newItem = { id: id, value: "" }; + this.internal_data.push(newItem); + + table.initRowEdit(newItem); + const index = this.internal_data.findIndex(l => l.id == newItem.id); + this.editInit(newItem, index); + } + + editInit(value: any, index: number) { + this.clonedData[index] = { ...value }; + } + + delete(index: number) { + this.internal_data.splice(index, 1); + this.dataChange.emit(this.getData()); + } + + editSave(value: any, index: number) { + if (!value.value || this.internal_data[index] == this.clonedData[index]) { + return; + } + + delete this.clonedData[index]; + this.dataChange.emit(this.getData()); + } + + editCancel(index: number) { + this.internal_data[index] = this.clonedData[index]; + delete this.clonedData[index]; + } +} diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.html b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.html new file mode 100644 index 00000000..e0e48d00 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.html @@ -0,0 +1,55 @@ +
+
+
+ {{'common.feature_flags' | translate}}: +
+
+ + +
+
+ +
+
+
+ + + + + + + + + {{value.value.key}} + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+
+
+
+
+
diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.scss b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts new file mode 100644 index 00000000..4151e4d6 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FeatureFlagListComponent } from './feature-flag-list.component'; + +describe('FeatureFlagListComponent', () => { + let component: FeatureFlagListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FeatureFlagListComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FeatureFlagListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts new file mode 100644 index 00000000..e03cb29d --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from "@angular/core"; +import { ConfigListComponent } from "../config-list/config-list.component"; +import { Table } from "primeng/table"; +import { PossibleFeatureFlagsQuery } from "../../../../models/graphql/query.model"; +import { DataService } from "../../../../services/data/data.service"; + +@Component({ + selector: "app-feature-flag-list", + templateUrl: "./feature-flag-list.component.html", + styleUrls: ["./feature-flag-list.component.scss"] +}) +export class FeatureFlagListComponent extends ConfigListComponent implements OnInit { + featureFlags: string[] = []; + + + constructor( + private dataService: DataService + ) { + super(); + } + + ngOnInit() { + this.dataService.query("{possibleFeatureFlags}" + ).subscribe(data => { + this.featureFlags = data.possibleFeatureFlags; + }); + } + + override addNew(table: Table) { + const id = this.internal_data.length == 0 ? 1 : Math.max.apply(Math, this.internal_data.map(value => { + return value.id ?? 0; + })) + 1; + const newItem = { id: id, value: { key: "", value: false } }; + this.internal_data.push(newItem); + + table.initRowEdit(newItem); + const index = this.internal_data.findIndex(l => l.id == newItem.id); + this.editInit(newItem, index); + } +} diff --git a/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.html b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.html new file mode 100644 index 00000000..6dbc7430 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.html @@ -0,0 +1 @@ + diff --git a/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.scss b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.scss new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.scss @@ -0,0 +1 @@ + diff --git a/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.spec.ts b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.spec.ts new file mode 100644 index 00000000..dfa08452 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HideableColumnComponent } from './hideable-column.component'; + +describe('HideableColumnComponent', () => { + let component: HideableColumnComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HideableColumnComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HideableColumnComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.ts b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.ts new file mode 100644 index 00000000..4cba495c --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-column/hideable-column.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from "@angular/core"; +import { HideableComponent } from "../../base/hideable-component"; + +@Component({ + selector: "[hideable-td]", + templateUrl: "./hideable-column.component.html", + styleUrls: ["./hideable-column.component.scss"] +}) +export class HideableColumnComponent extends HideableComponent { + @Input("hideable-td") override column!: string; + + constructor() { + super(); + } + +} diff --git a/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.html b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.html new file mode 100644 index 00000000..f35a2a42 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.html @@ -0,0 +1,11 @@ + + + + +
+ +
+ + + + diff --git a/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.scss b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.spec.ts b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.spec.ts new file mode 100644 index 00000000..27570e8b --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HideableHeaderComponent } from './hideable-header.component'; + +describe('HideableHeaderComponent', () => { + let component: HideableHeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HideableHeaderComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HideableHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.ts b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.ts new file mode 100644 index 00000000..bd7f5597 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/hideable-header/hideable-header.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from "@angular/core"; +import { HideableComponent } from "../../base/hideable-component"; + +@Component({ + selector: "[hideable-th]", + templateUrl: "./hideable-header.component.html", + styleUrls: ["./hideable-header.component.scss"] +}) +export class HideableHeaderComponent extends HideableComponent { + @Input("hideable-th") override column!: string; + @Input() sortable: boolean = false; + constructor() { + super(); + } + +} diff --git a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts index deb1be40..28769084 100644 --- a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts +++ b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts @@ -1,17 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; -import { catchError } from 'rxjs/operators'; -import { AuthService } from 'src/app/services/auth/auth.service'; -import { ThemeService } from 'src/app/services/theme/theme.service'; +import { Injectable } from "@angular/core"; +import { ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router"; +import { catchError } from "rxjs/operators"; +import { AuthService } from "src/app/services/auth/auth.service"; +import { ThemeService } from "src/app/services/theme/theme.service"; +import { SidebarService } from "../../../../services/sidebar/sidebar.service"; +import { MemberRoles } from "../../../../models/auth/auth-user.dto"; +import { DataService } from "../../../../services/data/data.service"; @Injectable({ - providedIn: 'root' + providedIn: "root" }) export class AuthGuard implements CanActivate { constructor( private router: Router, private authService: AuthService, - private themeService: ThemeService + private themeService: ThemeService, + private sidebarService: SidebarService, + private data: DataService, + private route: ActivatedRoute ) { } @@ -26,16 +32,46 @@ export class AuthGuard implements CanActivate { return false; } - const role = route.data['role']; - if (role) { - this.authService.hasUserPermission(role).then(hasPermission => { - if (!hasPermission) { - this.router.navigate(['/dashboard']); + const role = route.data["role"]; + const memberRole = route.data["memberRole"]; + if (role !== undefined) { + this.authService.hasUserPermission(role).then(async hasPermission => { + let authUser = await this.authService.getLoggedInUser(); + let isTechnician = authUser?.users?.map(u => u.isTechnician).filter(u => u) ?? []; + + if (!hasPermission && !isTechnician) { + this.router.navigate(["/dashboard"]); return false; } return true; }); } + + if (memberRole !== undefined) { + let userHasAccess = false; + let authUser = await this.authService.getLoggedInUser(); + let server = route.params["serverId"]; + + if (!authUser || !authUser.users) { + return true; + } + authUser?.users?.forEach(u => { + if (u.server === +(server ?? 0)) { + if ( + memberRole === MemberRoles.Moderator && u.isModerator || + memberRole === MemberRoles.Admin && u.isAdmin || + memberRole === MemberRoles.Technician && u.isTechnician + ) { + userHasAccess = true; + } + } + }); + if (!userHasAccess) { + this.router.navigate(["/dashboard"]); + return false; + } + return true; + } return true; } } diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts index e2d14884..9cea8e7f 100644 --- a/kdb-web/src/app/modules/shared/shared.module.ts +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -13,7 +13,7 @@ import { InputTextModule } from "primeng/inputtext"; import { MenuModule } from "primeng/menu"; import { PasswordModule } from "primeng/password"; import { ProgressSpinnerModule } from "primeng/progressspinner"; -import { TableModule } from "primeng/table"; +import { SortableColumn, TableModule } from "primeng/table"; import { ToastModule } from "primeng/toast"; import { AuthRolePipe } from "./pipes/auth-role.pipe"; import { IpAddressPipe } from "./pipes/ip-address.pipe"; @@ -23,7 +23,15 @@ import { PanelModule } from "primeng/panel"; import { InputNumberModule } from "primeng/inputnumber"; import { ImageModule } from "primeng/image"; import { SidebarModule } from "primeng/sidebar"; -import { HistoryBtnComponent } from './components/history-btn/history-btn.component'; +import { HistoryBtnComponent } from "./components/history-btn/history-btn.component"; +import { DataViewModule, DataViewLayoutOptions } from "primeng/dataview"; +import { ConfigListComponent } from "./components/config-list/config-list.component"; +import { MultiSelectModule } from "primeng/multiselect"; +import { HideableColumnComponent } from './components/hideable-column/hideable-column.component'; +import { HideableHeaderComponent } from './components/hideable-header/hideable-header.component'; +import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-select-columns.component'; +import { FeatureFlagListComponent } from './components/feature-flag-list/feature-flag-list.component'; +import { InputSwitchModule } from "primeng/inputswitch"; @NgModule({ @@ -32,6 +40,11 @@ import { HistoryBtnComponent } from './components/history-btn/history-btn.compon IpAddressPipe, BoolPipe, HistoryBtnComponent, + ConfigListComponent, + HideableColumnComponent, + HideableHeaderComponent, + MultiSelectColumnsComponent, + FeatureFlagListComponent, ], imports: [ CommonModule, @@ -56,6 +69,9 @@ import { HistoryBtnComponent } from './components/history-btn/history-btn.compon InputNumberModule, ImageModule, SidebarModule, + DataViewModule, + MultiSelectModule, + InputSwitchModule, ], exports: [ ButtonModule, @@ -82,7 +98,17 @@ import { HistoryBtnComponent } from './components/history-btn/history-btn.compon InputNumberModule, ImageModule, SidebarModule, - HistoryBtnComponent + HistoryBtnComponent, + DataViewModule, + DataViewLayoutOptions, + ConfigListComponent, + MultiSelectModule, + HideableColumnComponent, + HideableHeaderComponent, + MultiSelectColumnsComponent, + FeatureFlagListComponent, + InputSwitchModule, ] }) -export class SharedModule { } +export class SharedModule { +} diff --git a/kdb-web/src/app/modules/view/server/achievements/achievements-routing.module.ts b/kdb-web/src/app/modules/view/server/achievements/achievements-routing.module.ts new file mode 100644 index 00000000..ae847fb9 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/achievements/achievements-routing.module.ts @@ -0,0 +1,16 @@ +import {NgModule} from "@angular/core"; +import {RouterModule, Routes} from "@angular/router"; +import { AchievementComponent } from "./components/achievement/achievement.component"; + +const routes: Routes = [ + + {path: '', component: AchievementComponent}, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AchievementsRoutingModule { + +} diff --git a/kdb-web/src/app/modules/view/server/achievements/achievements.module.ts b/kdb-web/src/app/modules/view/server/achievements/achievements.module.ts new file mode 100644 index 00000000..ccf7e426 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/achievements/achievements.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { AchievementComponent } from "./components/achievement/achievement.component"; +import { AchievementsRoutingModule } from "./achievements-routing.module"; +import { SharedModule } from "../../../shared/shared.module"; + + +@NgModule({ + declarations: [ + AchievementComponent + ], + imports: [ + CommonModule, + AchievementsRoutingModule, + SharedModule + ] +}) +export class AchievementsModule { } diff --git a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html new file mode 100644 index 00000000..a84d3fbc --- /dev/null +++ b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html @@ -0,0 +1,270 @@ +

+ {{'view.server.achievements.header' | translate}} +

+
+
+ + + +
+
+
+ {{achievements.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'view.server.achievements.achievements' | translate}} +
+ + +
+ +
+ + +
+
+
+ + + + +
+
{{'common.id' | translate}}
+ +
+ + + +
+
{{'common.name' | translate}}
+ +
+ + + +
+
{{'common.description' | translate}}
+ +
+ + + +
+
{{'common.attribute' | translate}}
+ +
+ + + +
+
{{'common.operator' | translate}}
+ +
+ + + +
+
{{'common.value' | translate}}
+ +
+ + + +
+
{{'common.created_at' | translate}}
+
+ + + +
+
{{'common.modified_at' | translate}}
+
+ + + +
+
{{'common.actions' | translate}}
+
+ + + + + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + + + + + +
+ + + + + {{'common.id' | translate}}: + + + {{achievement.id}} + + + {{achievement.id}} + + + + + + {{'common.name' | translate}}: + + + + + + {{achievement.name}} + + + + + + {{'common.description' | translate}}: + + + + + + {{achievement.description}} + + + + + + {{'common.attribute' | translate}}: + + + + + + {{achievement.attribute}} + + + + + + {{'common.operator' | translate}}: + + + + + + {{achievement.operator}} + + + + + + {{'common.value' | translate}}: + + + + + + {{achievement.value}} + + + + + + + + + {{achievement.value}} + + + + + + + + + {{achievement.value}} + + + + + + {{'common.created_at' | translate}}: + + + {{achievement.createdAt | date:'dd.MM.yy HH:mm'}} + + + {{achievement.createdAt | date:'dd.MM.yy HH:mm'}} + + + + + {{'common.modified_at' | translate}}: + + + {{achievement.modifiedAt | date:'dd.MM.yy HH:mm'}} + + + {{achievement.modifiedAt | date:'dd.MM.yy HH:mm'}} + + + + +
+ + + + + + +
+ + +
+ + + + + {{'common.no_entries_found' | translate}} + + + + + + +
+
+
+ diff --git a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.scss b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.spec.ts b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.spec.ts new file mode 100644 index 00000000..771e808b --- /dev/null +++ b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AchievementComponent } from './achievement.component'; + +describe('AchievementComponent', () => { + let component: AchievementComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AchievementComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AchievementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts new file mode 100644 index 00000000..127848fd --- /dev/null +++ b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts @@ -0,0 +1,324 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Achievement, AchievementAttribute, AchievementFilter } from "../../../../../../models/data/achievement.model"; +import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; +import { Page } from "../../../../../../models/graphql/filter/page.model"; +import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model"; +import { Subject, throwError } from "rxjs"; +import { Server } from "../../../../../../models/data/server.model"; +import { UserDTO } from "../../../../../../models/auth/auth-user.dto"; +import { Queries } from "../../../../../../models/graphql/queries.model"; +import { AuthService } from "../../../../../../services/auth/auth.service"; +import { SpinnerService } from "../../../../../../services/spinner/spinner.service"; +import { ToastService } from "../../../../../../services/toast/toast.service"; +import { ConfirmationDialogService } from "../../../../../../services/confirmation-dialog/confirmation-dialog.service"; +import { TranslateService } from "@ngx-translate/core"; +import { DataService } from "../../../../../../services/data/data.service"; +import { SidebarService } from "../../../../../../services/sidebar/sidebar.service"; +import { ActivatedRoute } from "@angular/router"; +import { AchievementListQuery, AchievementTypeQuery, GameServerListQuery, LevelListQuery, Query } from "../../../../../../models/graphql/query.model"; +import { catchError, debounceTime, takeUntil } from "rxjs/operators"; +import { LazyLoadEvent, MenuItem } from "primeng/api"; +import { Table } from "primeng/table"; +import { User } from "../../../../../../models/data/user.model"; +import { AchievementMutationResult } from "../../../../../../models/graphql/result.model"; +import { Mutations } from "../../../../../../models/graphql/mutations.model"; +import { ComponentWithTable } from "../../../../../../base/component-with-table"; + +@Component({ + selector: "app-achievement", + templateUrl: "./achievement.component.html", + styleUrls: ["./achievement.component.scss"] +}) +export class AchievementComponent extends ComponentWithTable implements OnInit, OnDestroy { + public achievements: Achievement[] = []; + public loading = true; + + public isEditingNew: boolean = false; + + public filterForm!: FormGroup<{ + id: FormControl, + name: FormControl, + description: FormControl, + color: FormControl, + min_xp: FormControl, + permissions: FormControl, + }>; + + public filter: AchievementFilter = {}; + public page: Page = { + pageSize: undefined, + pageIndex: undefined + }; + public sort: Sort = { + sortColumn: undefined, + sortDirection: undefined + }; + + public totalRecords: number = 0; + + public clonedAchievements: { [s: string]: Achievement; } = {}; + + private unsubscriber = new Subject(); + private server: Server = {}; + public user: UserDTO | null = null; + + public operators: string[] = []; + public attributes: MenuItem[] = []; + private achievementsAttributes: AchievementAttribute[] = []; + + public levels!: MenuItem[]; + public gameServers!: MenuItem[]; + + query: string = Queries.achievementWithHistoryQuery; + + public constructor( + private authService: AuthService, + private spinner: SpinnerService, + private toastService: ToastService, + private confirmDialog: ConfirmationDialogService, + private fb: FormBuilder, + private translate: TranslateService, + private data: DataService, + private sidebar: SidebarService, + private route: ActivatedRoute) { + super("achievement", ["id", "name", "description", "attribute", "operator", "value"]); + } + + public ngOnInit(): void { + this.loading = true; + this.setFilterForm(); + this.data.getServerFromRoute(this.route).then(async server => { + this.server = server; + let authUser = await this.authService.getLoggedInUser(); + this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null; + }); + } + + public ngOnDestroy(): void { + this.unsubscriber.next(); + this.unsubscriber.complete(); + } + + private loadLevels() { + this.data.query(Queries.levelQuery, { + serverId: this.server.id + }, + (data: Query) => { + return data.servers[0]; + } + ).subscribe(data => { + this.levels = data.levels.map(level => { + return { label: level.name, value: level.name }; + }); + }); + } + + private loadGameServers() { + this.data.query(Queries.gameServerQuery, { + serverId: this.server.id + }, + (data: Query) => { + return data.servers[0]; + } + ).subscribe(data => { + this.gameServers = data.gameServers.map(gameServer => { + return { label: gameServer.name, value: gameServer.name }; + }); + }); + } + + private loadNextData() { + this.data.query(Queries.achievementQuery, { + serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort + }, + (data: Query) => { + return data.servers[0]; + } + ).subscribe(data => { + this.totalRecords = data.achievementCount; + this.achievements = data.achievements; + this.spinner.hideSpinner(); + this.loading = false; + }); + } + + public loadNextPage(): void { + this.data.query(Queries.achievementTypeQuery + ).subscribe(data => { + this.operators = data.achievementOperators; + this.achievementsAttributes = data.achievementAttributes; + this.attributes = data.achievementAttributes.map(attribute => { + return { label: attribute.name, value: attribute.name }; + }); + this.loadLevels(); + this.loadGameServers(); + this.loadNextData(); + }); + } + + public setFilterForm(): void { + this.filterForm = this.fb.group({ + id: new FormControl(null), + name: new FormControl(null), + description: new FormControl(null), + color: new FormControl(null), + min_xp: new FormControl(null), + permissions: new FormControl(null) + }); + + this.filterForm.valueChanges.pipe( + takeUntil(this.unsubscriber), + debounceTime(600) + ).subscribe(changes => { + if (changes.id) { + this.filter.id = changes.id; + } else { + this.filter.id = undefined; + } + + if (changes.name) { + this.filter.name = changes.name; + } else { + this.filter.name = undefined; + } + + if (this.page.pageSize) + this.page.pageSize = 10; + + if (this.page.pageIndex) + this.page.pageIndex = 0; + + this.loadNextPage(); + }); + } + + public newAchievementTemplate: Achievement = { + id: 0, + createdAt: "", + modifiedAt: "" + }; + + public nextPage(event: LazyLoadEvent): void { + this.page.pageSize = event.rows ?? 0; + if (event.first != null && event.rows != null) + this.page.pageIndex = event.first / event.rows; + this.sort.sortColumn = event.sortField ?? undefined; + this.sort.sortDirection = event.sortOrder === 1 ? SortDirection.ASC : event.sortOrder === -1 ? SortDirection.DESC : SortDirection.ASC; + + this.loadNextPage(); + } + + public resetFilters(): void { + this.filterForm.reset(); + } + + public onRowEditInit(table: Table, user: User, index: number): void { + this.clonedAchievements[index] = { ...user }; + } + + public onRowEditSave(table: Table, newAchievement: Achievement, index: number): void { + if (this.isEditingNew && JSON.stringify(newAchievement) === JSON.stringify(this.newAchievementTemplate)) { + this.isEditingNew = false; + this.achievements.splice(index, 1); + return; + } + + if (!newAchievement.id && !this.isEditingNew || !newAchievement.name && !newAchievement.attribute && !newAchievement?.operator && !newAchievement?.value) { + return; + } + + if (this.isEditingNew) { + this.spinner.showSpinner(); + this.data.mutation(Mutations.createAchievement, { + name: newAchievement.name, + description: newAchievement.description, + attribute: newAchievement.attribute, + operator: newAchievement.operator, + value: newAchievement.value + "", + serverId: this.server.id + } + ).pipe(catchError(err => { + this.isEditingNew = false; + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(result => { + this.isEditingNew = false; + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.achievements.message.achievement_create"), this.translate.instant("view.server.achievements.message.achievement_create_d", { name: result.achievement.createAchievement?.name })); + this.loadNextPage(); + }); + return; + } + + this.spinner.showSpinner(); + this.data.mutation(Mutations.updateAchievement, { + id: newAchievement.id, + name: newAchievement.name, + description: newAchievement.description, + attribute: newAchievement.attribute, + operator: newAchievement.operator, + value: newAchievement.value + "" + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(_ => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.achievements.message.achievement_update"), this.translate.instant("view.server.achievements.message.achievement_update_d", { name: newAchievement.name })); + this.loadNextPage(); + }); + + } + + public onRowEditCancel(index: number): void { + if (this.isEditingNew) { + this.achievements.splice(index, 1); + delete this.clonedAchievements[index]; + this.isEditingNew = false; + return; + } + + this.achievements[index] = this.clonedAchievements[index]; + delete this.clonedAchievements[index]; + } + + public deleteAchievement(achievement: Achievement): void { + this.confirmDialog.confirmDialog( + this.translate.instant("view.server.achievements.message.achievement_delete"), this.translate.instant("view.server.achievements.message.achievement_delete_q", { name: achievement.name }), + () => { + this.spinner.showSpinner(); + this.data.mutation(Mutations.deleteAchievement, { + id: achievement.id + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(l => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.achievements.message.achievement_deleted"), this.translate.instant("view.server.achievements.message.achievement_deleted_d", { name: achievement.name })); + this.loadNextPage(); + }); + }); + } + + public addAchievement(table: Table): void { + const newAchievement = JSON.parse(JSON.stringify(this.newAchievementTemplate)); + newAchievement.id = this.achievements.length == 0 ? 1 : Math.max.apply(Math, this.achievements.map(l => { + return l.id ?? 0; + })) + 1; + + this.achievements.push(newAchievement); + + table.initRowEdit(newAchievement); + + const index = this.achievements.findIndex(l => l.id == newAchievement.id); + this.onRowEditInit(table, newAchievement, index); + + this.isEditingNew = true; + } + + public getAchievementAttributeByName(name: string): AchievementAttribute { + const [found] = this.achievementsAttributes.filter(x => x.name === name); + return found; + } +} diff --git a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html index c44d7208..432bdfae 100644 --- a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html +++ b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html @@ -3,22 +3,25 @@
-
-
- {{rules.length}} {{'common.of' | translate}} - {{dt.totalRecords}} - - {{'view.server.auto_roles.rules.auto_roles' | translate}} +
+
+ {{rules.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'view.server.auto_roles.rules.auto_roles' | translate}} +
+
diff --git a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts index 5fab0523..8ef1cc57 100644 --- a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts +++ b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts @@ -22,13 +22,14 @@ import { Table } from "primeng/table"; import { AutoRoleMutationResult, AutoRoleRuleMutationResult } from "../../../../../../models/graphql/result.model"; import { Mutations } from "../../../../../../models/graphql/mutations.model"; import { Subject, throwError } from "rxjs"; +import { ComponentWithTable } from "../../../../../../base/component-with-table"; @Component({ selector: "app-auto-roles-rules", templateUrl: "./auto-roles-rules.component.html", styleUrls: ["./auto-roles-rules.component.scss"] }) -export class AutoRolesRulesComponent implements OnInit, OnDestroy { +export class AutoRolesRulesComponent extends ComponentWithTable implements OnInit, OnDestroy { rules: AutoRoleRule[] = []; guild: Guild = { channels: [], emojis: [], roles: [] }; @@ -80,6 +81,7 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private router: Router ) { + super("auto-role-rules", ["id", "role", "emoji"]); } public getEmojiUrl(name: string): string { @@ -100,20 +102,20 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { this.spinner.showSpinner(); this.data.query(Queries.guildsQuery, { - filter: { - id: server.discordId - } + id: server?.discordId } ).subscribe(data => { - this.guild = data.guilds[0]; - this.emojis = this.guild.emojis - .map(x => { - return { label: x.name, value: x }; - }); - this.roles = this.guild.roles - .map(x => { - return { label: x.name, value: x }; - }); + if (data.discord.guilds) { + this.guild = data.discord.guilds[0]; + this.emojis = this.guild.emojis + .map(x => { + return { label: x.name, value: x }; + }); + this.roles = this.guild.roles + .map(x => { + return { label: x.name, value: x }; + }); + } this.spinner.hideSpinner(); }); this.loadNextPage(); @@ -222,7 +224,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { ).pipe(catchError(err => { this.isEditingNew = false; this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_failed_d")); return throwError(err); })).subscribe(result => { this.isEditingNew = false; @@ -241,7 +242,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_update_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_update_failed_d")); return throwError(err); })).subscribe(result => { this.spinner.hideSpinner(); @@ -272,7 +272,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_delete_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_delete_failed_d", { id: autoRoleRule.id })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); @@ -284,7 +283,7 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { public addAutoRoleRule(table: Table): void { const newAutoRole = JSON.parse(JSON.stringify(this.newAutoRoleTemplate)); - newAutoRole.id = Math.max.apply(Math, this.rules.map(u => { + newAutoRole.id = this.rules.length == 0 ? 1 : Math.max.apply(Math, this.rules.map(u => { return u.id ?? 0; })) + 1; diff --git a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html index b30ff372..12f1448c 100644 --- a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html +++ b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html @@ -3,22 +3,26 @@
-
-
- {{auto_roles.length}} {{'common.of' | translate}} - {{dt.totalRecords}} - - {{'view.server.auto_roles.auto_roles' | translate}} +
+
+ {{auto_roles.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'view.server.auto_roles.auto_roles' | translate}} +
+ +
+
+ +
+
diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.scss b/kdb-web/src/app/modules/view/server/config/components/config/config.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts b/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts new file mode 100644 index 00000000..94dd19ba --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfigComponent } from './config.component'; + +describe('ConfigComponent', () => { + let component: ConfigComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfigComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConfigComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts b/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts new file mode 100644 index 00000000..70855220 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts @@ -0,0 +1,134 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; +import { TranslateService } from "@ngx-translate/core"; +import { catchError } from "rxjs/operators"; +import { GuiService } from "src/app/services/gui/gui.service"; +import { SettingsService } from "src/app/services/settings/settings.service"; +import { SpinnerService } from "src/app/services/spinner/spinner.service"; +import { ToastService } from "src/app/services/toast/toast.service"; +import { forkJoin, throwError } from "rxjs"; +import { Query, ServerConfigQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model"; +import { Queries } from "../../../../../../models/graphql/queries.model"; +import { DataService } from "../../../../../../services/data/data.service"; +import { ServerConfigMutationResult } from "../../../../../../models/graphql/result.model"; +import { Mutations } from "../../../../../../models/graphql/mutations.model"; +import { AuthService } from "../../../../../../services/auth/auth.service"; +import { ServerConfig } from "../../../../../../models/config/server-config.model"; +import { Server } from "../../../../../../models/data/server.model"; +import { ActivatedRoute } from "@angular/router"; +import { Channel, ChannelType, Role } from "../../../../../../models/data/discord.model"; + +type AFKChannelId = { + id: number; + value: string; +}; + +@Component({ + selector: "app-config", + templateUrl: "./config.component.html", + styleUrls: ["./config.component.scss"] +}) +export class ConfigComponent implements OnInit { + config: ServerConfig = { + messageDeleteTimer: 0, + featureFlags: [], + afkChannelIds: [], + moderatorRoleIds: [], + adminRoleIds: [] + }; + + + afkChannelIds: AFKChannelId[] = []; + clonedAfkChannelIds: { [s: number]: AFKChannelId } = {}; + + private server: Server = {}; + isTechnician: boolean = false; + roles?: Role[]; + voiceChannels: Channel[] = []; + textChannels: Channel[] = []; + + constructor( + private data: DataService, + private settingsService: SettingsService, + private spinnerService: SpinnerService, + private guiService: GuiService, + private formBuilder: FormBuilder, + private toastService: ToastService, + private translate: TranslateService, + private authService: AuthService, + private spinner: SpinnerService, + private route: ActivatedRoute + ) { + } + + ngOnInit(): void { + this.spinnerService.showSpinner(); + + this.data.getServerFromRoute(this.route).then(async server => { + this.server = server; + this.loadConfig(); + let authUser = await this.authService.getLoggedInUser(); + let isUserTechnicianList = authUser?.users?.map(u => u.isTechnician).filter(u => u) ?? []; + this.isTechnician = isUserTechnicianList.length > 0; + }); + } + + loadConfig() { + forkJoin([ + this.data.query(Queries.serverConfigQuery, { + serverId: this.server.id + }, + (data: Query) => { + return data.servers[0]; + }), + this.data.query(Queries.serverConfigDiscordQuery) + ]).subscribe(data => { + this.config = data[0].config; + let id = 0; + for (const afkChannelId of this.config.afkChannelIds ?? []) { + this.afkChannelIds.push({ id: id, value: afkChannelId }); + id++; + } + + if (data[1].discord.guilds) { + const guild = data[1].discord.guilds.filter(g => g.id === this.server.discordId)[0]; + this.roles = guild.roles ?? undefined; + this.voiceChannels = guild.channels.filter(x => x.type == ChannelType.voice) ?? undefined; + this.textChannels = guild.channels.filter(x => x.type == ChannelType.text) ?? undefined; + } + + this.spinnerService.hideSpinner(); + }); + } + + saveServerConfig() { + this.spinner.showSpinner(); + this.data.mutation(Mutations.updateServerConfig, { + id: this.config.id, + messageDeleteTimer: this.config.messageDeleteTimer, + notificationChatId: this.config.notificationChatId, + maxVoiceStateHours: this.config.maxVoiceStateHours, + xpPerMessage: this.config.xpPerMessage, + xpPerReaction: this.config.xpPerReaction, + maxMessageXpPerHour: this.config.maxMessageXpPerHour, + xpPerOntimeHour: this.config.xpPerOntimeHour, + xpPerEventParticipation: this.config.xpPerEventParticipation, + xpPerAchievement: this.config.xpPerAchievement, + afkCommandChannelId: this.config.afkCommandChannelId, + helpVoiceChannelId: this.config.helpVoiceChannelId, + teamChannelId: this.config.teamChannelId, + loginMessageChannelId: this.config.loginMessageChannelId, + featureFlags: this.config.featureFlags, + afkChannelIds: this.config.afkChannelIds, + moderatorRoleIds: this.config.moderatorRoleIds, + adminRoleIds: this.config.adminRoleIds + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(result => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.config.message.server_config_create"), this.translate.instant("view.server.config.message.server_config_create_d")); + }); + } +} diff --git a/kdb-web/src/app/modules/view/server/config/config-routing.module.ts b/kdb-web/src/app/modules/view/server/config/config-routing.module.ts new file mode 100644 index 00000000..afbbd84c --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/config-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ConfigComponent } from "./components/config/config.component"; + +const routes: Routes = [ + {path: '', component: ConfigComponent}, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ConfigRoutingModule { } diff --git a/kdb-web/src/app/modules/view/server/config/config.module.ts b/kdb-web/src/app/modules/view/server/config/config.module.ts new file mode 100644 index 00000000..0dd2131b --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/config.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ConfigRoutingModule } from './config-routing.module'; +import { ConfigComponent } from './components/config/config.component'; +import { SharedModule } from "../../../shared/shared.module"; + + +@NgModule({ + declarations: [ + ConfigComponent + ], + imports: [ + CommonModule, + ConfigRoutingModule, + SharedModule, + ] +}) +export class ConfigModule { } diff --git a/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.html b/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.html index 0c8b1945..5d9f5912 100644 --- a/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.html +++ b/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.html @@ -3,22 +3,26 @@
-
-
- {{levels.length}} {{'common.of' | translate}} - {{dt.totalRecords}} - - {{'view.server.levels.levels' | translate}} +
+
+ {{levels.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'view.server.levels.levels' | translate}} +
+ +