diff --git a/kdb-bot/src/bot/config/feature-flags.json b/kdb-bot/src/bot/config/feature-flags.json index 68bf6e002c..da07fa2398 100644 --- a/kdb-bot/src/bot/config/feature-flags.json +++ b/kdb-bot/src/bot/config/feature-flags.json @@ -1,6 +1,6 @@ { "FeatureFlags": { - "ApiModule": true, + "ApiModule": false, "AdminModule": true, "AutoRoleModule": true, "BaseModule": true, @@ -9,6 +9,7 @@ "CoreExtensionModule": true, "DatabaseModule": true, "ModeratorModule": true, + "LevelModule": true, "PermissionModule": true, "PresenceModule": true } diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index b818ad4bcd..03b8307cda 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -9,6 +9,7 @@ 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.database.database_module import DatabaseModule +from modules.level.level_module import LevelModule from modules.moderator.moderator_module import ModeratorModule from modules.permission.permission_module import PermissionModule @@ -25,6 +26,7 @@ class ModuleList: AutoRoleModule, BaseModule, DatabaseModule, + LevelModule, ModeratorModule, PermissionModule, ApiModule, diff --git a/kdb-bot/src/bot_data/abc/data_seeder_abc.py b/kdb-bot/src/bot_data/abc/data_seeder_abc.py new file mode 100644 index 0000000000..a4fecdd77e --- /dev/null +++ b/kdb-bot/src/bot_data/abc/data_seeder_abc.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + + +class DataSeederABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def seed(self): pass diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index bf8786e529..0668bffed8 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -19,6 +19,7 @@ from bot_data.service.auto_role_repository_service import AutoRoleRepositoryServ from bot_data.service.client_repository_service import ClientRepositoryService 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_repository_service import ServerRepositoryService from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService @@ -43,3 +44,5 @@ class DataModule(ModuleABC): services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService) services.add_transient(LevelRepositoryABC, LevelRepositoryService) + + services.add_transient(SeederService) diff --git a/kdb-bot/src/bot_data/migration/level_migration.py b/kdb-bot/src/bot_data/migration/level_migration.py index 71d618aef4..5b1dbc0fc3 100644 --- a/kdb-bot/src/bot_data/migration/level_migration.py +++ b/kdb-bot/src/bot_data/migration/level_migration.py @@ -20,10 +20,12 @@ class LevelMigration(MigrationABC): CREATE TABLE IF NOT EXISTS `Levels` ( `Id` BIGINT NOT NULL AUTO_INCREMENT, `Name` VARCHAR(255) NOT NULL, - `Color` VARCHAR(7) NOT NULL, + `Color` VARCHAR(8) NOT NULL, `MinXp` BIGINT NOT NULL, `PermissionInt` BIGINT NOT NULL, `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), PRIMARY KEY(`Id`), FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) ); diff --git a/kdb-bot/src/bot_data/model/level.py b/kdb-bot/src/bot_data/model/level.py index fcde27bfcf..9cfc63f179 100644 --- a/kdb-bot/src/bot_data/model/level.py +++ b/kdb-bot/src/bot_data/model/level.py @@ -56,8 +56,12 @@ class Level(TableABC): self._permissions = value @property - def server(self) -> Optional[Server]: + def server(self) -> Server: return self._server + + @server.setter + def server(self, value: Server): + self._server = value @staticmethod def get_select_all_string() -> str: @@ -85,8 +89,8 @@ class Level(TableABC): INSERT INTO `Levels` ( `Name`, `Color`, `MinXp`, `PermissionInt`, `ServerId`, `CreatedAt`, `LastModifiedAt` ) VALUES ( - `{self._name}`, - `{self._color}`, + '{self._name}', + '{self._color}', {self._min_xp}, {self._permissions}, {self._server.server_id}, @@ -99,9 +103,8 @@ class Level(TableABC): def udpate_string(self) -> str: return str(f""" UPDATE `Levels` - SET `Name` = {self._name}, - `Name` = {self._name}, - `Color` = {self._color}, + SET `Name` = '{self._name}', + `Color` = '{self._color}', `MinXp` = {self._min_xp}, `PermissionInt` = {self._permissions}, `LastModifiedAt` = '{self._modified_at}' diff --git a/kdb-bot/src/bot_data/service/seeder_service.py b/kdb-bot/src/bot_data/service/seeder_service.py new file mode 100644 index 0000000000..6d0f9b7df4 --- /dev/null +++ b/kdb-bot/src/bot_data/service/seeder_service.py @@ -0,0 +1,25 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.data_seeder_abc import DataSeederABC + + +class SeederService: + + def __init__(self, logger: DatabaseLogger, services: ServiceProviderABC, db: DatabaseContextABC): + self._logger = logger + self._services = services + + self._db = db + + self._seeder = List(type, DataSeederABC.__subclasses__()) + + async def seed(self): + self._logger.info(__name__, f"Seed data") + for seeder in self._seeder: + seeder_as_service: DataSeederABC = self._services.get_service(seeder) + self._logger.debug(__name__, f"Starting seeder {seeder.__name__}") + await seeder_as_service.seed() + self._db.save_changes() diff --git a/kdb-bot/src/modules/database/database_extension.py b/kdb-bot/src/modules/database/database_extension.py index 8bc111e9a9..1cc7a77fe5 100644 --- a/kdb-bot/src/modules/database/database_extension.py +++ b/kdb-bot/src/modules/database/database_extension.py @@ -9,6 +9,7 @@ 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.service.migration_service import MigrationService +from bot_data.service.seeder_service import SeederService class DatabaseExtension(ApplicationExtensionABC): 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 9176a26c15..96f013eefc 100644 --- a/kdb-bot/src/modules/database/database_on_ready_event.py +++ b/kdb-bot/src/modules/database/database_on_ready_event.py @@ -20,6 +20,7 @@ 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_data.service.seeder_service import SeederService from bot_data.service.user_repository_service import ServerRepositoryABC from modules.base.configuration.base_server_settings import BaseServerSettings @@ -30,6 +31,7 @@ class DatabaseOnReadyEvent(OnReadyABC): self, config: ConfigurationABC, logger: DatabaseLogger, + seeder: SeederService, bot: DiscordBotServiceABC, db_context: DatabaseContextABC, server_repo: ServerRepositoryABC, @@ -43,6 +45,7 @@ class DatabaseOnReadyEvent(OnReadyABC): self._config = config self._logger = logger + self._seeder = seeder self._bot = bot self._db_context = db_context self._servers = server_repo @@ -303,6 +306,8 @@ class DatabaseOnReadyEvent(OnReadyABC): async def on_ready(self): self._logger.debug(__name__, f'Module {type(self)} started') + await self._seeder.seed() + self._check_known_users() self._check_servers() self._check_clients() diff --git a/kdb-bot/src/modules/level/configuration/__init__.py b/kdb-bot/src/modules/level/configuration/__init__.py new file mode 100644 index 0000000000..425ab6c146 --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/level/configuration/level_settings.py b/kdb-bot/src/modules/level/configuration/level_settings.py new file mode 100644 index 0000000000..95dda2c862 --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/level_settings.py @@ -0,0 +1,33 @@ +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 LevelSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._levels = List(Level) + + @property + def levels(self) -> List[Level]: + return self._levels + + def from_dict(self, settings: dict): + try: + for level in settings: + 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/default-level.json b/kdb-bot/src/modules/level/default-level.json new file mode 100644 index 0000000000..04cb874550 --- /dev/null +++ b/kdb-bot/src/modules/level/default-level.json @@ -0,0 +1,28 @@ +{ + "Level": [ + { + "Name": "Newbie", + "Color": "0x1abc9c", + "MinXp": 0, + "Permissions": 968552209984 + }, + { + "Name": "Keks", + "Color": "0x2ecc71", + "MinXp": 100, + "Permissions": 1002928856640 + }, + { + "Name": "Doppelkeks", + "Color": "0x3498db", + "MinXp": 200, + "Permissions": 1071849660224 + }, + { + "Name": "Auror", + "Color": "0xf1c40f", + "MinXp": 300, + "Permissions": 1089042120513 + } + ] +} \ No newline at end of file diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index dfdd9bdae3..565297436f 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -1,3 +1,5 @@ +import os + from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC @@ -5,6 +7,7 @@ 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.level.level_seeder import LevelSeeder class LevelModule(ModuleABC): @@ -13,7 +16,10 @@ class LevelModule(ModuleABC): ModuleABC.__init__(self, dc, FeatureFlagsEnum.level_module) def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass + cwd = env.working_directory + env.set_working_directory(os.path.dirname(os.path.realpath(__file__))) + config.add_json_file(f'default-level.json', optional=False) + env.set_working_directory(cwd) - def configure_services(self, service: ServiceCollectionABC, env: ApplicationEnvironmentABC): - pass + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(LevelSeeder) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py new file mode 100644 index 0000000000..a8bcd5db94 --- /dev/null +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -0,0 +1,46 @@ +from cpl_discord.service import DiscordBotServiceABC +from discord import Permissions, Colour + +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.service.level_repository_service import LevelRepositoryService +from modules.level.configuration.level_settings import LevelSettings + + +class LevelSeeder(DataSeederABC): + + def __init__(self, logger: DatabaseLogger, levels: LevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): + DataSeederABC.__init__(self) + + self._logger = logger + self._default_levels = levels.levels.order_by_descending(lambda l: l.min_xp) + self._levels = level_repo + self._servers = servers + self._bot = bot + + async def seed(self): + if self._levels.get_levels().count() > 0: + return + + for guild in self._bot.guilds: + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + continue + + levels = self._levels.find_levels_by_server_id(server.server_id) + if levels is not None and levels.count() > 0: + continue + + for level in self._default_levels: + try: + if guild.roles.where(lambda r: r.name == level.name).count() == 0: + await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) + + self._logger.info(__name__, f'Created default level {level.name}') + level.server = server + self._levels.add_level(level) + except Exception as e: + self._logger.error(__name__, f'Creating default role failed', e) + + self._logger.debug(__name__, f'Seeded default levels')