Merge pull request '#268_achievements' (#325) from #268_achievements into 1.1.0
Reviewed-on: sh-edraft.de/kd_discord_bot#325 Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
This commit is contained in:
		| @@ -7,6 +7,7 @@ | |||||||
|       "bot-core": "src/bot_core/bot-core.json", |       "bot-core": "src/bot_core/bot-core.json", | ||||||
|       "bot-data": "src/bot_data/bot-data.json", |       "bot-data": "src/bot_data/bot-data.json", | ||||||
|       "bot-graphql": "src/bot_graphql/bot-graphql.json", |       "bot-graphql": "src/bot_graphql/bot-graphql.json", | ||||||
|  |       "achievements": "src/modules/achievements/achievements.json", | ||||||
|       "auto-role": "src/modules/auto_role/auto-role.json", |       "auto-role": "src/modules/auto_role/auto-role.json", | ||||||
|       "base": "src/modules/base/base.json", |       "base": "src/modules/base/base.json", | ||||||
|       "boot-log": "src/modules/boot_log/boot-log.json", |       "boot-log": "src/modules/boot_log/boot-log.json", | ||||||
| @@ -21,25 +22,18 @@ | |||||||
|     }, |     }, | ||||||
|     "Scripts": { |     "Scripts": { | ||||||
|       "format": "black ./", |       "format": "black ./", | ||||||
|  |  | ||||||
|       "sv": "cpl set-version $ARGS", |       "sv": "cpl set-version $ARGS", | ||||||
|       "set-version": "cpl run set-version $ARGS --dev; echo '';", |       "set-version": "cpl run set-version $ARGS --dev; echo '';", | ||||||
|  |  | ||||||
|       "gv": "cpl get-version", |       "gv": "cpl get-version", | ||||||
|       "get-version": "export VERSION=$(cpl run get-version --dev); echo $VERSION;", |       "get-version": "export VERSION=$(cpl run get-version --dev); echo $VERSION;", | ||||||
|  |  | ||||||
|       "pre-build": "cpl set-version $ARGS; black ./;", |       "pre-build": "cpl set-version $ARGS; black ./;", | ||||||
|       "post-build": "cpl run post-build --dev; black ./;", |       "post-build": "cpl run post-build --dev; black ./;", | ||||||
|  |  | ||||||
|       "pre-prod": "cpl build", |       "pre-prod": "cpl build", | ||||||
|       "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", |       "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", | ||||||
|  |  | ||||||
|       "pre-stage": "cpl build", |       "pre-stage": "cpl build", | ||||||
|       "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", |       "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", | ||||||
|  |  | ||||||
|       "pre-dev": "cpl build", |       "pre-dev": "cpl build", | ||||||
|       "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", |       "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) .;", |       "docker-build": "cpl build $ARGS; docker build -t kdb-bot/kdb-bot:$(cpl gv) .;", | ||||||
|       "dc-up": "docker-compose up -d", |       "dc-up": "docker-compose up -d", | ||||||
|       "dc-down": "docker-compose down", |       "dc-down": "docker-compose down", | ||||||
|   | |||||||
 Submodule kdb-bot/src/bot/config updated: e1c1efac98...440fb3bd35
									
								
							| @@ -5,6 +5,7 @@ from bot_core.core_extension.core_extension_module import CoreExtensionModule | |||||||
| from bot_core.core_module import CoreModule | from bot_core.core_module import CoreModule | ||||||
| from bot_data.data_module import DataModule | from bot_data.data_module import DataModule | ||||||
| from bot_graphql.graphql_module import GraphQLModule | 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.auto_role.auto_role_module import AutoRoleModule | ||||||
| from modules.base.base_module import BaseModule | from modules.base.base_module import BaseModule | ||||||
| from modules.boot_log.boot_log_module import BootLogModule | from modules.boot_log.boot_log_module import BootLogModule | ||||||
| @@ -31,6 +32,7 @@ class ModuleList: | |||||||
|                 LevelModule, |                 LevelModule, | ||||||
|                 ApiModule, |                 ApiModule, | ||||||
|                 TechnicianModule, |                 TechnicianModule, | ||||||
|  |                 AchievementsModule, | ||||||
|                 # has to be last! |                 # has to be last! | ||||||
|                 BootLogModule, |                 BootLogModule, | ||||||
|                 CoreExtensionModule, |                 CoreExtensionModule, | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ from cpl_core.dependency_injection import ServiceCollectionABC | |||||||
| from cpl_core.environment import ApplicationEnvironmentABC | from cpl_core.environment import ApplicationEnvironmentABC | ||||||
|  |  | ||||||
| from bot_data.abc.migration_abc import MigrationABC | 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_key_migration import ApiKeyMigration | ||||||
| from bot_data.migration.api_migration import ApiMigration | from bot_data.migration.api_migration import ApiMigration | ||||||
| from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration | from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration | ||||||
| @@ -42,3 +43,4 @@ class StartupMigrationExtension(StartupExtensionABC): | |||||||
|         services.add_transient(MigrationABC, RemoveStatsMigration)  # 19.02.2023 #190 - 1.0.0 |         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, UserWarningMigration)  # 21.02.2023 #35 - 1.0.0 | ||||||
|         services.add_transient(MigrationABC, DBHistoryMigration)  # 06.03.2023 #246 - 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 | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ from bot_core.configuration.bot_logging_settings import BotLoggingSettings | |||||||
| from bot_core.configuration.bot_settings import BotSettings | from bot_core.configuration.bot_settings import BotSettings | ||||||
| from modules.base.configuration.base_settings import BaseSettings | from modules.base.configuration.base_settings import BaseSettings | ||||||
| from modules.boot_log.configuration.boot_log_settings import BootLogSettings | 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 | from modules.permission.configuration.permission_settings import PermissionSettings | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -37,7 +36,6 @@ class StartupSettingsExtension(StartupExtensionABC): | |||||||
|         self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id) |         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, 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, 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( |         self._configure_settings_with_sub_settings( | ||||||
|             configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id |             configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id | ||||||
|         ) |         ) | ||||||
| @@ -50,9 +48,9 @@ class StartupSettingsExtension(StartupExtensionABC): | |||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _configure_settings_with_sub_settings( |     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: |         if settings is None: | ||||||
|             return |             return | ||||||
|  |  | ||||||
|   | |||||||
| @@ -93,6 +93,12 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "modules": { |   "modules": { | ||||||
|  |     "achievements": { | ||||||
|  |       "got_new_achievement": "{} hat die Errungenschaft {} freigeschaltet :D", | ||||||
|  |       "commands": { | ||||||
|  |         "check": "Alles klar, ich schaue eben nach... nom nom" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "auto_role": { |     "auto_role": { | ||||||
|       "add": { |       "add": { | ||||||
|         "error": { |         "error": { | ||||||
|   | |||||||
| @@ -1,21 +1,30 @@ | |||||||
| import traceback |  | ||||||
|  |  | ||||||
| from cpl_core.configuration import ConfigurationModelABC | from cpl_core.configuration import ConfigurationModelABC | ||||||
| from cpl_core.console import Console |  | ||||||
| from cpl_query.extension import List | from cpl_query.extension import List | ||||||
|  |  | ||||||
|  | from bot_api.json_processor import JSONProcessor | ||||||
| from bot_core.configuration.server_settings import ServerSettings | from bot_core.configuration.server_settings import ServerSettings | ||||||
|  |  | ||||||
|  |  | ||||||
| class BotSettings(ConfigurationModelABC): | class BotSettings(ConfigurationModelABC): | ||||||
|     def __init__(self): |     def __init__( | ||||||
|  |         self, | ||||||
|  |         technicians: list = None, | ||||||
|  |         wait_for_restart: int = 2, | ||||||
|  |         wait_for_shutdown: int = 2, | ||||||
|  |         cache_max_messages: int = 1000, | ||||||
|  |         server_settings: dict = None, | ||||||
|  |     ): | ||||||
|         ConfigurationModelABC.__init__(self) |         ConfigurationModelABC.__init__(self) | ||||||
|  |  | ||||||
|  |         self._technicians: List[int] = List(int) if technicians is None else technicians | ||||||
|  |         self._wait_for_restart = wait_for_restart | ||||||
|  |         self._wait_for_shutdown = wait_for_shutdown | ||||||
|  |         self._cache_max_messages = cache_max_messages | ||||||
|  |  | ||||||
|         self._servers: List[ServerSettings] = List(ServerSettings) |         self._servers: List[ServerSettings] = List(ServerSettings) | ||||||
|         self._technicians: List[int] = List(int) |  | ||||||
|         self._wait_for_restart = 2 |         if server_settings is not None: | ||||||
|         self._wait_for_shutdown = 2 |             self._servers_from_dict(server_settings) | ||||||
|         self._cache_max_messages = 1000 |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def servers(self) -> List[ServerSettings]: |     def servers(self) -> List[ServerSettings]: | ||||||
| @@ -37,26 +46,34 @@ class BotSettings(ConfigurationModelABC): | |||||||
|     def cache_max_messages(self) -> int: |     def cache_max_messages(self) -> int: | ||||||
|         return self._cache_max_messages |         return self._cache_max_messages | ||||||
|  |  | ||||||
|     def from_dict(self, settings: dict): |     def _servers_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) |         servers = List(ServerSettings) | ||||||
|         for s in settings: |         for s in settings: | ||||||
|                 st = ServerSettings() |             settings[s]["id"] = int(s) | ||||||
|                 settings[s]["Id"] = s |             st = JSONProcessor.process(ServerSettings, settings[s]) | ||||||
|                 st.from_dict(settings[s]) |  | ||||||
|             servers.append(st) |             servers.append(st) | ||||||
|         self._servers = servers |         self._servers = servers | ||||||
|         except Exception as e: |  | ||||||
|             Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") |     # def from_dict(self, settings: dict): | ||||||
|             Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") |     #     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()}") | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ from enum import Enum | |||||||
|  |  | ||||||
| class FeatureFlagsEnum(Enum): | class FeatureFlagsEnum(Enum): | ||||||
|     # modules |     # modules | ||||||
|  |     achievements_module = "AchievementsModule" | ||||||
|     api_module = "ApiModule" |     api_module = "ApiModule" | ||||||
|     admin_module = "AdminModule" |     admin_module = "AdminModule" | ||||||
|     auto_role_module = "AutoRoleModule" |     auto_role_module = "AutoRoleModule" | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): | |||||||
|  |  | ||||||
|         self._flags = { |         self._flags = { | ||||||
|             # modules |             # modules | ||||||
|  |             FeatureFlagsEnum.achievements_module.value: False,  # 14.06.2023 #268 | ||||||
|             FeatureFlagsEnum.api_module.value: False,  # 13.10.2022 #70 |             FeatureFlagsEnum.api_module.value: False,  # 13.10.2022 #70 | ||||||
|             FeatureFlagsEnum.admin_module.value: False,  # 02.10.2022 #48 |             FeatureFlagsEnum.admin_module.value: False,  # 02.10.2022 #48 | ||||||
|             FeatureFlagsEnum.auto_role_module.value: True,  # 03.10.2022 #54 |             FeatureFlagsEnum.auto_role_module.value: True,  # 03.10.2022 #54 | ||||||
|   | |||||||
| @@ -1,15 +1,18 @@ | |||||||
| import traceback |  | ||||||
|  |  | ||||||
| from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC | from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC | ||||||
| from cpl_core.console import Console |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ServerSettings(ConfigurationModelABC): | class ServerSettings(ConfigurationModelABC): | ||||||
|     def __init__(self): |     def __init__( | ||||||
|  |         self, | ||||||
|  |         id: int = None, | ||||||
|  |         message_delete_timer: int = None, | ||||||
|  |         notification_chat_id: int = None, | ||||||
|  |     ): | ||||||
|         ConfigurationModelABC.__init__(self) |         ConfigurationModelABC.__init__(self) | ||||||
|  |  | ||||||
|         self._id: int = 0 |         self._id: int = id | ||||||
|         self._message_delete_timer: int = 0 |         self._message_delete_timer: int = message_delete_timer | ||||||
|  |         self._notification_chat_id: int = notification_chat_id | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def id(self) -> int: |     def id(self) -> int: | ||||||
| @@ -19,10 +22,15 @@ class ServerSettings(ConfigurationModelABC): | |||||||
|     def message_delete_timer(self) -> int: |     def message_delete_timer(self) -> int: | ||||||
|         return self._message_delete_timer |         return self._message_delete_timer | ||||||
|  |  | ||||||
|     def from_dict(self, settings: dict): |     @property | ||||||
|         try: |     def notification_chat_id(self) -> int: | ||||||
|             self._id = int(settings["Id"]) |         return self._notification_chat_id | ||||||
|             self._message_delete_timer = int(settings["MessageDeleteTimer"]) |  | ||||||
|         except Exception as e: |     # def from_dict(self, settings: dict): | ||||||
|             Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in settings") |     #     try: | ||||||
|             Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") |     #         self._id = int(settings["Id"]) | ||||||
|  |     #         self._message_delete_timer = int(settings["MessageDeleteTimer"]) | ||||||
|  |     #         self._notification_chat_id = int(settings["NotificationChatId"]) | ||||||
|  |     #     except Exception as e: | ||||||
|  |     #         Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in settings") | ||||||
|  |     #         Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ from bot_data.model.user_joined_server import UserJoinedServer | |||||||
| from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel | from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel | ||||||
| from bot_data.service.seeder_service import SeederService | from bot_data.service.seeder_service import SeederService | ||||||
| from bot_data.service.user_repository_service import ServerRepositoryABC | from bot_data.service.user_repository_service import ServerRepositoryABC | ||||||
|  | from modules.achievements.achievement_service import AchievementService | ||||||
| from modules.base.configuration.base_server_settings import BaseServerSettings | from modules.base.configuration.base_server_settings import BaseServerSettings | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -42,6 +43,7 @@ class DataIntegrityService: | |||||||
|         user_joins: UserJoinedServerRepositoryABC, |         user_joins: UserJoinedServerRepositoryABC, | ||||||
|         user_joins_vc: UserJoinedVoiceChannelRepositoryABC, |         user_joins_vc: UserJoinedVoiceChannelRepositoryABC, | ||||||
|         user_joined_gs: UserJoinedGameServerRepositoryABC, |         user_joined_gs: UserJoinedGameServerRepositoryABC, | ||||||
|  |         achievement_service: AchievementService, | ||||||
|         dtp: DateTimeOffsetPipe, |         dtp: DateTimeOffsetPipe, | ||||||
|     ): |     ): | ||||||
|         self._config = config |         self._config = config | ||||||
| @@ -57,6 +59,7 @@ class DataIntegrityService: | |||||||
|         self._user_joins = user_joins |         self._user_joins = user_joins | ||||||
|         self._user_joins_vc = user_joins_vc |         self._user_joins_vc = user_joins_vc | ||||||
|         self._user_joined_gs = user_joined_gs |         self._user_joined_gs = user_joined_gs | ||||||
|  |         self._achievements = achievement_service | ||||||
|         self._dtp = dtp |         self._dtp = dtp | ||||||
|  |  | ||||||
|         self._is_for_shutdown = False |         self._is_for_shutdown = False | ||||||
| @@ -360,6 +363,25 @@ class DataIntegrityService: | |||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 self._logger.error(__name__, f"Cannot get UserJoinedGameServer", e) |                 self._logger.error(__name__, f"Cannot get UserJoinedGameServer", e) | ||||||
|  |  | ||||||
|  |     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}") | ||||||
|  |  | ||||||
|  |                 self._bot.loop.create_task(self._achievements.validate_achievements_for_user(user)) | ||||||
|  |  | ||||||
|     def check_data_integrity(self, is_for_shutdown=False): |     def check_data_integrity(self, is_for_shutdown=False): | ||||||
|         if is_for_shutdown != self._is_for_shutdown: |         if is_for_shutdown != self._is_for_shutdown: | ||||||
|             self._is_for_shutdown = is_for_shutdown |             self._is_for_shutdown = is_for_shutdown | ||||||
| @@ -371,3 +393,4 @@ class DataIntegrityService: | |||||||
|         self._check_user_joins() |         self._check_user_joins() | ||||||
|         self._check_user_joins_vc() |         self._check_user_joins_vc() | ||||||
|         self._check_user_joined_gs() |         self._check_user_joined_gs() | ||||||
|  |         self._check_for_user_achievements() | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								kdb-bot/src/bot_data/abc/achievement_repository_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								kdb-bot/src/bot_data/abc/achievement_repository_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -5,6 +5,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC | |||||||
|  |  | ||||||
| from bot_core.abc.module_abc import ModuleABC | from bot_core.abc.module_abc import ModuleABC | ||||||
| from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum | 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.api_key_repository_abc import ApiKeyRepositoryABC | ||||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||||
| from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC | from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC | ||||||
| @@ -24,6 +25,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_repository_abc import UserRepositoryABC | ||||||
| from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC | 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.api_key_repository_service import ApiKeyRepositoryService | ||||||
| from bot_data.service.auth_user_repository_service import AuthUserRepositoryService | from bot_data.service.auth_user_repository_service import AuthUserRepositoryService | ||||||
| from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService | from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService | ||||||
| @@ -77,5 +79,6 @@ class DataModule(ModuleABC): | |||||||
|         ) |         ) | ||||||
|         services.add_transient(GameServerRepositoryABC, GameServerRepositoryService) |         services.add_transient(GameServerRepositoryABC, GameServerRepositoryService) | ||||||
|         services.add_transient(UserGameIdentRepositoryABC, UserGameIdentRepositoryService) |         services.add_transient(UserGameIdentRepositoryABC, UserGameIdentRepositoryService) | ||||||
|  |         services.add_transient(AchievementRepositoryABC, AchievementRepositoryService) | ||||||
|  |  | ||||||
|         services.add_transient(SeederService) |         services.add_transient(SeederService) | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ class DBContext(DatabaseContext): | |||||||
|         self._logger = logger |         self._logger = logger | ||||||
|  |  | ||||||
|         DatabaseContext.__init__(self) |         DatabaseContext.__init__(self) | ||||||
|  |         self._fails = 0 | ||||||
|  |  | ||||||
|     def connect(self, database_settings: DatabaseSettings): |     def connect(self, database_settings: DatabaseSettings): | ||||||
|         try: |         try: | ||||||
| @@ -32,7 +33,11 @@ class DBContext(DatabaseContext): | |||||||
|         try: |         try: | ||||||
|             return super(DBContext, self).select(statement) |             return super(DBContext, self).select(statement) | ||||||
|         except Exception as e: |         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._logger.error(__name__, f"Database error caused by {statement}", e) | ||||||
|  |             self._fails += 1 | ||||||
|             try: |             try: | ||||||
|                 time.sleep(0.5) |                 time.sleep(0.5) | ||||||
|                 return self.select(statement) |                 return self.select(statement) | ||||||
|   | |||||||
							
								
								
									
										127
									
								
								kdb-bot/src/bot_data/migration/achievements_migration.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								kdb-bot/src/bot_data/migration/achievements_migration.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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;""")) | ||||||
							
								
								
									
										149
									
								
								kdb-bot/src/bot_data/model/achievement.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								kdb-bot/src/bot_data/model/achievement.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | 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}; | ||||||
|  |         """ | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @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}; | ||||||
|  |         """ | ||||||
|  |         ) | ||||||
							
								
								
									
										58
									
								
								kdb-bot/src/bot_data/model/achievement_history.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								kdb-bot/src/bot_data/model/achievement_history.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -15,6 +15,8 @@ class User(TableABC): | |||||||
|         self, |         self, | ||||||
|         dc_id: int, |         dc_id: int, | ||||||
|         xp: int, |         xp: int, | ||||||
|  |         message_count: int, | ||||||
|  |         reaction_count: int, | ||||||
|         server: Optional[Server], |         server: Optional[Server], | ||||||
|         created_at: datetime = None, |         created_at: datetime = None, | ||||||
|         modified_at: datetime = None, |         modified_at: datetime = None, | ||||||
| @@ -23,6 +25,8 @@ class User(TableABC): | |||||||
|         self._user_id = id |         self._user_id = id | ||||||
|         self._discord_id = dc_id |         self._discord_id = dc_id | ||||||
|         self._xp = xp |         self._xp = xp | ||||||
|  |         self._message_count = message_count | ||||||
|  |         self._reaction_count = reaction_count | ||||||
|         self._server = server |         self._server = server | ||||||
|  |  | ||||||
|         TableABC.__init__(self) |         TableABC.__init__(self) | ||||||
| @@ -59,6 +63,22 @@ class User(TableABC): | |||||||
|     def xp(self, value: int): |     def xp(self, value: int): | ||||||
|         self._xp = value |         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 |     @property | ||||||
|     @ServiceProviderABC.inject |     @ServiceProviderABC.inject | ||||||
|     def ontime(self, services: ServiceProviderABC) -> float: |     def ontime(self, services: ServiceProviderABC) -> float: | ||||||
| @@ -151,10 +171,12 @@ class User(TableABC): | |||||||
|         return str( |         return str( | ||||||
|             f""" |             f""" | ||||||
|             INSERT INTO `Users` ( |             INSERT INTO `Users` ( | ||||||
|                 `DiscordId`, `XP`, `ServerId` |                 `DiscordId`, `XP`, `MessageCount`, `ReactionCount`, `ServerId` | ||||||
|             ) VALUES ( |             ) VALUES ( | ||||||
|                 {self._discord_id}, |                 {self._discord_id}, | ||||||
|                 {self._xp}, |                 {self._xp}, | ||||||
|  |                 {self._message_count}, | ||||||
|  |                 {self._reaction_count}, | ||||||
|                 {self._server.id} |                 {self._server.id} | ||||||
|             ); |             ); | ||||||
|         """ |         """ | ||||||
| @@ -165,7 +187,9 @@ class User(TableABC): | |||||||
|         return str( |         return str( | ||||||
|             f""" |             f""" | ||||||
|             UPDATE `Users` |             UPDATE `Users` | ||||||
|             SET `XP` = {self._xp} |             SET `XP` = {self._xp}, | ||||||
|  |             `MessageCount` = {self._message_count}, | ||||||
|  |             `ReactionCount` = {self._reaction_count} | ||||||
|             WHERE `UserId` = {self._user_id}; |             WHERE `UserId` = {self._user_id}; | ||||||
|         """ |         """ | ||||||
|         ) |         ) | ||||||
|   | |||||||
							
								
								
									
										105
									
								
								kdb-bot/src/bot_data/model/user_got_achievement.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								kdb-bot/src/bot_data/model/user_got_achievement.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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}; | ||||||
|  |             """ | ||||||
|  |         ) | ||||||
| @@ -9,6 +9,8 @@ class UserHistory(HistoryTableABC): | |||||||
|         self, |         self, | ||||||
|         dc_id: int, |         dc_id: int, | ||||||
|         xp: int, |         xp: int, | ||||||
|  |         message_count: int, | ||||||
|  |         reaction_count: int, | ||||||
|         server: int, |         server: int, | ||||||
|         deleted: bool, |         deleted: bool, | ||||||
|         date_from: str, |         date_from: str, | ||||||
| @@ -20,6 +22,8 @@ class UserHistory(HistoryTableABC): | |||||||
|         self._user_id = id |         self._user_id = id | ||||||
|         self._discord_id = dc_id |         self._discord_id = dc_id | ||||||
|         self._xp = xp |         self._xp = xp | ||||||
|  |         self._message_count = message_count | ||||||
|  |         self._reaction_count = reaction_count | ||||||
|         self._server = server |         self._server = server | ||||||
|  |  | ||||||
|         self._deleted = deleted |         self._deleted = deleted | ||||||
| @@ -38,6 +42,14 @@ class UserHistory(HistoryTableABC): | |||||||
|     def xp(self) -> int: |     def xp(self) -> int: | ||||||
|         return self._xp |         return self._xp | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def message_count(self) -> int: | ||||||
|  |         return self._message_count | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def reaction_count(self) -> int: | ||||||
|  |         return self._reaction_count | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def server(self) -> int: |     def server(self) -> int: | ||||||
|         return self._server |         return self._server | ||||||
|   | |||||||
							
								
								
									
										123
									
								
								kdb-bot/src/bot_data/service/achievements_repository_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								kdb-bot/src/bot_data/service/achievements_repository_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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_id_string(server_id)}") | ||||||
|  |         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_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) | ||||||
| @@ -5,12 +5,21 @@ from bot_data.model.server import Server | |||||||
|  |  | ||||||
| class CacheService: | class CacheService: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._cached_server = List(Server) |         self._cached_servers = List(Server) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def cached_server(self) -> List[Server]: |     def cached_server(self) -> List[Server]: | ||||||
|         return self._cached_server |         return self._cached_servers | ||||||
|  |  | ||||||
|     @cached_server.setter |     def add_server(self, server: Server): | ||||||
|     def cached_server(self, value: List[Server]): |         if self._cached_servers.where(lambda x: x.id == server.id).count() > 0: | ||||||
|         self._cached_server = value |             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) | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ class ServerRepositoryService(ServerRepositoryABC): | |||||||
|         for result in results: |         for result in results: | ||||||
|             servers.append(Server(result[1], result[2], result[3], id=result[0])) |             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 |         return servers | ||||||
|  |  | ||||||
|     def get_filtered_servers(self, criteria: ServerSelectCriteria) -> FilteredResult: |     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: |     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() |         cs = self._cache.cached_server.where(lambda x: x.id == server_id).single_or_default() | ||||||
|  |  | ||||||
|         if cs is not None: |         if cs is not None: | ||||||
|             return cs |             return cs | ||||||
|  |  | ||||||
|         self._logger.trace(__name__, f"Send SQL command: {Server.get_select_by_id_string(server_id)}") |         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] |         result = self._context.select(Server.get_select_by_id_string(server_id))[0] | ||||||
|         server = Server(result[1], result[2], result[3], id=result[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 |         return server | ||||||
|  |  | ||||||
|     def get_server_by_discord_id(self, discord_id: int) -> Server: |     def get_server_by_discord_id(self, discord_id: int) -> Server: | ||||||
|   | |||||||
| @@ -27,9 +27,11 @@ class UserRepositoryService(UserRepositoryABC): | |||||||
|         return User( |         return User( | ||||||
|             result[1], |             result[1], | ||||||
|             result[2], |             result[2], | ||||||
|             self._servers.get_server_by_id(result[3]), |             result[3], | ||||||
|             result[4], |             result[4], | ||||||
|             result[5], |             self._servers.get_server_by_id(result[5]), | ||||||
|  |             result[6], | ||||||
|  |             result[7], | ||||||
|             id=result[0], |             id=result[0], | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								kdb-bot/src/bot_graphql/filter/achievement_filter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								kdb-bot/src/bot_graphql/filter/achievement_filter.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -8,6 +8,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum | |||||||
| from bot_data.service.seeder_service import SeederService | from bot_data.service.seeder_service import SeederService | ||||||
| from bot_graphql.abc.filter_abc import FilterABC | from bot_graphql.abc.filter_abc import FilterABC | ||||||
| from bot_graphql.abc.query_abc import QueryABC | 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_filter import AutoRoleFilter | ||||||
| from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter | from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter | ||||||
| from bot_graphql.filter.client_filter import ClientFilter | from bot_graphql.filter.client_filter import ClientFilter | ||||||
| @@ -19,17 +20,22 @@ from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter | |||||||
| from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | ||||||
| from bot_graphql.graphql_service import GraphQLService | from bot_graphql.graphql_service import GraphQLService | ||||||
| from bot_graphql.mutation import Mutation | 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_mutation import AutoRoleMutation | ||||||
| from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation | from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation | ||||||
| from bot_graphql.mutations.level_mutation import LevelMutation | from bot_graphql.mutations.level_mutation import LevelMutation | ||||||
| from bot_graphql.mutations.user_joined_game_server_mutation import UserJoinedGameServerMutation | from bot_graphql.mutations.user_joined_game_server_mutation import UserJoinedGameServerMutation | ||||||
| from bot_graphql.mutations.user_mutation import UserMutation | 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_history_query import AutoRoleHistoryQuery | ||||||
| from bot_graphql.queries.auto_role_query import AutoRoleQuery | 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_history_query import AutoRoleRuleHistoryQuery | ||||||
| from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery | from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery | ||||||
| from bot_graphql.queries.client_history_query import ClientHistoryQuery | from bot_graphql.queries.client_history_query import ClientHistoryQuery | ||||||
| from bot_graphql.queries.client_query import ClientQuery | from bot_graphql.queries.client_query import ClientQuery | ||||||
|  | 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_history_query import KnownUserHistoryQuery | ||||||
| from bot_graphql.queries.known_user_query import KnownUserQuery | from bot_graphql.queries.known_user_query import KnownUserQuery | ||||||
| from bot_graphql.queries.level_history_query import LevelHistoryQuery | from bot_graphql.queries.level_history_query import LevelHistoryQuery | ||||||
| @@ -62,6 +68,9 @@ class GraphQLModule(ModuleABC): | |||||||
|         services.add_singleton(Mutation) |         services.add_singleton(Mutation) | ||||||
|  |  | ||||||
|         # queries |         # 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, AutoRoleHistoryQuery) | ||||||
|         services.add_transient(QueryABC, AutoRoleQuery) |         services.add_transient(QueryABC, AutoRoleQuery) | ||||||
|         services.add_transient(QueryABC, AutoRoleRuleHistoryQuery) |         services.add_transient(QueryABC, AutoRoleRuleHistoryQuery) | ||||||
| @@ -74,6 +83,7 @@ class GraphQLModule(ModuleABC): | |||||||
|         services.add_transient(QueryABC, LevelQuery) |         services.add_transient(QueryABC, LevelQuery) | ||||||
|         services.add_transient(QueryABC, ServerHistoryQuery) |         services.add_transient(QueryABC, ServerHistoryQuery) | ||||||
|         services.add_transient(QueryABC, ServerQuery) |         services.add_transient(QueryABC, ServerQuery) | ||||||
|  |         services.add_transient(QueryABC, GameServerQuery) | ||||||
|         services.add_transient(QueryABC, UserHistoryQuery) |         services.add_transient(QueryABC, UserHistoryQuery) | ||||||
|         services.add_transient(QueryABC, UserQuery) |         services.add_transient(QueryABC, UserQuery) | ||||||
|         services.add_transient(QueryABC, UserJoinedServerHistoryQuery) |         services.add_transient(QueryABC, UserJoinedServerHistoryQuery) | ||||||
| @@ -90,6 +100,7 @@ class GraphQLModule(ModuleABC): | |||||||
|         services.add_transient(FilterABC, LevelFilter) |         services.add_transient(FilterABC, LevelFilter) | ||||||
|         services.add_transient(FilterABC, ServerFilter) |         services.add_transient(FilterABC, ServerFilter) | ||||||
|         services.add_transient(FilterABC, UserFilter) |         services.add_transient(FilterABC, UserFilter) | ||||||
|  |         services.add_transient(FilterABC, AchievementFilter) | ||||||
|         services.add_transient(FilterABC, UserJoinedServerFilter) |         services.add_transient(FilterABC, UserJoinedServerFilter) | ||||||
|         services.add_transient(FilterABC, UserJoinedVoiceChannelFilter) |         services.add_transient(FilterABC, UserJoinedVoiceChannelFilter) | ||||||
|         services.add_transient(FilterABC, UserJoinedGameServerFilter) |         services.add_transient(FilterABC, UserJoinedGameServerFilter) | ||||||
| @@ -99,6 +110,7 @@ class GraphQLModule(ModuleABC): | |||||||
|         services.add_transient(QueryABC, AutoRoleRuleMutation) |         services.add_transient(QueryABC, AutoRoleRuleMutation) | ||||||
|         services.add_transient(QueryABC, LevelMutation) |         services.add_transient(QueryABC, LevelMutation) | ||||||
|         services.add_transient(QueryABC, UserMutation) |         services.add_transient(QueryABC, UserMutation) | ||||||
|  |         services.add_transient(QueryABC, AchievementMutation) | ||||||
|         services.add_transient(QueryABC, UserJoinedGameServerMutation) |         services.add_transient(QueryABC, UserJoinedGameServerMutation) | ||||||
|  |  | ||||||
|         services.add_transient(SeederService) |         services.add_transient(SeederService) | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								kdb-bot/src/bot_graphql/model/achievement.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								kdb-bot/src/bot_graphql/model/achievement.gql
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
| @@ -4,4 +4,5 @@ type Mutation { | |||||||
|     level: LevelMutation |     level: LevelMutation | ||||||
|     user: UserMutation |     user: UserMutation | ||||||
|     userJoinedGameServer: UserJoinedGameServerMutation |     userJoinedGameServer: UserJoinedGameServerMutation | ||||||
|  |     achievement: AchievementMutation | ||||||
| } | } | ||||||
| @@ -17,6 +17,9 @@ type Query { | |||||||
|     serverCount: Int |     serverCount: Int | ||||||
|     servers(filter: ServerFilter, page: Page, sort: Sort): [Server] |     servers(filter: ServerFilter, page: Page, sort: Sort): [Server] | ||||||
|  |  | ||||||
|  |     gameServerCount: Int | ||||||
|  |     gameServers: [GameServer] | ||||||
|  |  | ||||||
|     userJoinedServerCount: Int |     userJoinedServerCount: Int | ||||||
|     userJoinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [UserJoinedServer] |     userJoinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [UserJoinedServer] | ||||||
|  |  | ||||||
| @@ -29,5 +32,10 @@ type Query { | |||||||
|     userCount: Int |     userCount: Int | ||||||
|     users(filter: UserFilter, page: Page, sort: Sort): [User] |     users(filter: UserFilter, page: Page, sort: Sort): [User] | ||||||
|  |  | ||||||
|  |     achievementCount: Int | ||||||
|  |     achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] | ||||||
|  |     achievementAttributes: [AchievementAttribute] | ||||||
|  |     achievementOperators: [String] | ||||||
|  |  | ||||||
|     guilds(filter: GuildFilter): [Guild] |     guilds(filter: GuildFilter): [Guild] | ||||||
| } | } | ||||||
| @@ -1,3 +1,12 @@ | |||||||
|  | type GameServer { | ||||||
|  |     id: ID | ||||||
|  |     name: String | ||||||
|  |     server: Server | ||||||
|  |  | ||||||
|  |     createdAt: String | ||||||
|  |     modifiedAt: String | ||||||
|  | } | ||||||
|  |  | ||||||
| type Server implements TableWithHistoryQuery { | type Server implements TableWithHistoryQuery { | ||||||
|     id: ID |     id: ID | ||||||
|     discordId: String |     discordId: String | ||||||
| @@ -13,9 +22,15 @@ type Server implements TableWithHistoryQuery { | |||||||
|     levelCount: Int |     levelCount: Int | ||||||
|     levels(filter: LevelFilter, page: Page, sort: Sort): [Level] |     levels(filter: LevelFilter, page: Page, sort: Sort): [Level] | ||||||
|  |  | ||||||
|  |     gameServerCount: Int | ||||||
|  |     gameServers: [GameServer] | ||||||
|  |  | ||||||
|     userCount: Int |     userCount: Int | ||||||
|     users(filter: UserFilter, page: Page, sort: Sort): [User] |     users(filter: UserFilter, page: Page, sort: Sort): [User] | ||||||
|  |  | ||||||
|  |     achievementCount: Int | ||||||
|  |     achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] | ||||||
|  |  | ||||||
|     createdAt: String |     createdAt: String | ||||||
|     modifiedAt: String |     modifiedAt: String | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ type User implements TableWithHistoryQuery { | |||||||
|     discordId: String |     discordId: String | ||||||
|     name: String |     name: String | ||||||
|     xp: Int |     xp: Int | ||||||
|  |     messageCount: Int | ||||||
|  |     reactionCount: Int | ||||||
|     ontime: Float |     ontime: Float | ||||||
|     level: Level |     level: Level | ||||||
|  |  | ||||||
| @@ -15,6 +17,9 @@ type User implements TableWithHistoryQuery { | |||||||
|     userJoinedGameServerCount: Int |     userJoinedGameServerCount: Int | ||||||
|     userJoinedGameServers(filter: UserJoinedGameServerFilter, page: Page, sort: Sort): [UserJoinedGameServer] |     userJoinedGameServers(filter: UserJoinedGameServerFilter, page: Page, sort: Sort): [UserJoinedGameServer] | ||||||
|  |  | ||||||
|  |     achievementCount: Int | ||||||
|  |     achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] | ||||||
|  |  | ||||||
|     server: Server |     server: Server | ||||||
|     leftServer: Boolean |     leftServer: Boolean | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| from ariadne import MutationType | 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_mutation import AutoRoleMutation | ||||||
| from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation | from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation | ||||||
| from bot_graphql.mutations.level_mutation import LevelMutation | from bot_graphql.mutations.level_mutation import LevelMutation | ||||||
| @@ -15,6 +15,7 @@ class Mutation(MutationType): | |||||||
|         auto_role_rule_mutation: AutoRoleRuleMutation, |         auto_role_rule_mutation: AutoRoleRuleMutation, | ||||||
|         level_mutation: LevelMutation, |         level_mutation: LevelMutation, | ||||||
|         user_mutation: UserMutation, |         user_mutation: UserMutation, | ||||||
|  |         achievement_mutation: AchievementMutation, | ||||||
|         user_joined_game_server: UserJoinedGameServerMutation, |         user_joined_game_server: UserJoinedGameServerMutation, | ||||||
|     ): |     ): | ||||||
|         MutationType.__init__(self) |         MutationType.__init__(self) | ||||||
| @@ -23,4 +24,5 @@ class Mutation(MutationType): | |||||||
|         self.set_field("autoRoleRule", lambda *_: auto_role_rule_mutation) |         self.set_field("autoRoleRule", lambda *_: auto_role_rule_mutation) | ||||||
|         self.set_field("level", lambda *_: level_mutation) |         self.set_field("level", lambda *_: level_mutation) | ||||||
|         self.set_field("user", lambda *_: user_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("userJoinedGameServer", lambda *_: user_joined_game_server) | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								kdb-bot/src/bot_graphql/mutations/achievement_mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								kdb-bot/src/bot_graphql/mutations/achievement_mutation.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -50,6 +50,7 @@ class LevelMutation(QueryABC): | |||||||
|                 and l.color == level.color |                 and l.color == level.color | ||||||
|                 and l.min_xp == level.min_xp |                 and l.min_xp == level.min_xp | ||||||
|                 and l.permissions == level.permissions |                 and l.permissions == level.permissions | ||||||
|  |                 and l.server.id == server.id | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|         self._bot.loop.create_task(self._level_seeder.seed()) |         self._bot.loop.create_task(self._level_seeder.seed()) | ||||||
|   | |||||||
| @@ -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) | ||||||
							
								
								
									
										14
									
								
								kdb-bot/src/bot_graphql/queries/achievement_history_query.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								kdb-bot/src/bot_graphql/queries/achievement_history_query.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||||
							
								
								
									
										20
									
								
								kdb-bot/src/bot_graphql/queries/achievement_query.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								kdb-bot/src/bot_graphql/queries/achievement_query.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||||
							
								
								
									
										10
									
								
								kdb-bot/src/bot_graphql/queries/game_server_query.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								kdb-bot/src/bot_graphql/queries/game_server_query.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||||
| @@ -1,16 +1,18 @@ | |||||||
| from cpl_core.database.context import DatabaseContextABC | from cpl_core.database.context import DatabaseContextABC | ||||||
| from cpl_discord.service import DiscordBotServiceABC | 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.auto_role_repository_abc import AutoRoleRepositoryABC | ||||||
| from bot_data.abc.client_repository_abc import ClientRepositoryABC | 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.level_repository_abc import LevelRepositoryABC | ||||||
| from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC | 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_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC | ||||||
| from bot_data.abc.user_repository_abc import UserRepositoryABC | from bot_data.abc.user_repository_abc import UserRepositoryABC | ||||||
| from bot_data.model.server import Server | from bot_data.model.server import Server | ||||||
| from bot_data.model.server_history import ServerHistory | 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.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.auto_role_filter import AutoRoleFilter | ||||||
| from bot_graphql.filter.client_filter import ClientFilter | from bot_graphql.filter.client_filter import ClientFilter | ||||||
| from bot_graphql.filter.level_filter import LevelFilter | from bot_graphql.filter.level_filter import LevelFilter | ||||||
| @@ -25,9 +27,11 @@ class ServerQuery(DataQueryWithHistoryABC): | |||||||
|         auto_roles: AutoRoleRepositoryABC, |         auto_roles: AutoRoleRepositoryABC, | ||||||
|         clients: ClientRepositoryABC, |         clients: ClientRepositoryABC, | ||||||
|         levels: LevelRepositoryABC, |         levels: LevelRepositoryABC, | ||||||
|  |         game_servers: GameServerRepositoryABC, | ||||||
|         users: UserRepositoryABC, |         users: UserRepositoryABC, | ||||||
|         ujs: UserJoinedServerRepositoryABC, |         ujs: UserJoinedServerRepositoryABC, | ||||||
|         ujvs: UserJoinedVoiceChannelRepositoryABC, |         ujvs: UserJoinedVoiceChannelRepositoryABC, | ||||||
|  |         achievements: AchievementRepositoryABC, | ||||||
|     ): |     ): | ||||||
|         DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db) |         DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db) | ||||||
|  |  | ||||||
| @@ -54,6 +58,10 @@ class ServerQuery(DataQueryWithHistoryABC): | |||||||
|         ) |         ) | ||||||
|         self.add_collection("level", lambda server, *_: self._levels.get_levels_by_server_id(server.id), LevelFilter) |         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("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 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def resolve_id(server: Server, *_): |     def resolve_id(server: Server, *_): | ||||||
|   | |||||||
| @@ -2,12 +2,14 @@ from cpl_core.database.context import DatabaseContextABC | |||||||
| from cpl_discord.service import DiscordBotServiceABC | from cpl_discord.service import DiscordBotServiceABC | ||||||
|  |  | ||||||
| from bot_core.abc.client_utils_abc import ClientUtilsABC | 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_game_server_repository_abc import UserJoinedGameServerRepositoryABC | ||||||
| from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC | 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_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC | ||||||
| from bot_data.model.user import User | from bot_data.model.user import User | ||||||
| from bot_data.model.user_history import UserHistory | from bot_data.model.user_history import UserHistory | ||||||
| from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC | 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_game_server_filter import UserJoinedGameServerFilter | ||||||
| from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter | from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter | ||||||
| from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | ||||||
| @@ -26,6 +28,7 @@ class UserQuery(DataQueryWithHistoryABC): | |||||||
|         ujvs: UserJoinedVoiceChannelRepositoryABC, |         ujvs: UserJoinedVoiceChannelRepositoryABC, | ||||||
|         user_joined_game_server: UserJoinedGameServerRepositoryABC, |         user_joined_game_server: UserJoinedGameServerRepositoryABC, | ||||||
|         permissions: PermissionServiceABC, |         permissions: PermissionServiceABC, | ||||||
|  |         achievements: AchievementRepositoryABC, | ||||||
|     ): |     ): | ||||||
|         DataQueryWithHistoryABC.__init__(self, "User", "UsersHistory", UserHistory, db) |         DataQueryWithHistoryABC.__init__(self, "User", "UsersHistory", UserHistory, db) | ||||||
|  |  | ||||||
| @@ -36,11 +39,14 @@ class UserQuery(DataQueryWithHistoryABC): | |||||||
|         self._ujs = ujs |         self._ujs = ujs | ||||||
|         self._ujvs = ujvs |         self._ujvs = ujvs | ||||||
|         self._permissions = permissions |         self._permissions = permissions | ||||||
|  |         self._achievements = achievements | ||||||
|  |  | ||||||
|         self.set_field("id", self.resolve_id) |         self.set_field("id", self.resolve_id) | ||||||
|         self.set_field("discordId", self.resolve_discord_id) |         self.set_field("discordId", self.resolve_discord_id) | ||||||
|         self.set_field("name", self.resolve_name) |         self.set_field("name", self.resolve_name) | ||||||
|         self.set_field("xp", self.resolve_xp) |         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("ontime", self.resolve_ontime) | ||||||
|         self.set_field("level", self.resolve_level) |         self.set_field("level", self.resolve_level) | ||||||
|         self.add_collection( |         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), |             lambda user, *_: self._user_joined_game_server.get_user_joined_game_servers_by_user_id(user.id), | ||||||
|             UserJoinedGameServerFilter, |             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("server", self.resolve_server) | ||||||
|         self.set_field("leftServer", self.resolve_left_server) |         self.set_field("leftServer", self.resolve_left_server) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,9 @@ | |||||||
| from cpl_discord.service import DiscordBotServiceABC | 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.auto_role_repository_abc import AutoRoleRepositoryABC | ||||||
| from bot_data.abc.client_repository_abc import ClientRepositoryABC | 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.known_user_repository_abc import KnownUserRepositoryABC | ||||||
| from bot_data.abc.level_repository_abc import LevelRepositoryABC | from bot_data.abc.level_repository_abc import LevelRepositoryABC | ||||||
| from bot_data.abc.server_repository_abc import ServerRepositoryABC | from bot_data.abc.server_repository_abc import ServerRepositoryABC | ||||||
| @@ -10,6 +12,7 @@ from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepos | |||||||
| from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC | from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC | ||||||
| from bot_data.abc.user_repository_abc import UserRepositoryABC | from bot_data.abc.user_repository_abc import UserRepositoryABC | ||||||
| from bot_graphql.abc.query_abc import QueryABC | 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_filter import AutoRoleFilter | ||||||
| from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter | from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter | ||||||
| from bot_graphql.filter.client_filter import ClientFilter | from bot_graphql.filter.client_filter import ClientFilter | ||||||
| @@ -19,6 +22,7 @@ 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_game_server_filter import UserJoinedGameServerFilter | ||||||
| from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter | from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter | ||||||
| from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter | ||||||
|  | from modules.achievements.achievement_service import AchievementService | ||||||
|  |  | ||||||
|  |  | ||||||
| class Query(QueryABC): | class Query(QueryABC): | ||||||
| @@ -30,10 +34,13 @@ class Query(QueryABC): | |||||||
|         known_users: KnownUserRepositoryABC, |         known_users: KnownUserRepositoryABC, | ||||||
|         levels: LevelRepositoryABC, |         levels: LevelRepositoryABC, | ||||||
|         servers: ServerRepositoryABC, |         servers: ServerRepositoryABC, | ||||||
|  |         game_servers: GameServerRepositoryABC, | ||||||
|         user_joined_servers: UserJoinedServerRepositoryABC, |         user_joined_servers: UserJoinedServerRepositoryABC, | ||||||
|         user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC, |         user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC, | ||||||
|         user_joined_game_server: UserJoinedGameServerRepositoryABC, |         user_joined_game_server: UserJoinedGameServerRepositoryABC, | ||||||
|         users: UserRepositoryABC, |         users: UserRepositoryABC, | ||||||
|  |         achievements: AchievementRepositoryABC, | ||||||
|  |         achievement_service: AchievementService, | ||||||
|     ): |     ): | ||||||
|         QueryABC.__init__(self, "Query") |         QueryABC.__init__(self, "Query") | ||||||
|  |  | ||||||
| @@ -45,6 +52,7 @@ class Query(QueryABC): | |||||||
|         self.add_collection("knownUser", lambda *_: known_users.get_users()) |         self.add_collection("knownUser", lambda *_: known_users.get_users()) | ||||||
|         self.add_collection("level", lambda *_: levels.get_levels(), LevelFilter) |         self.add_collection("level", lambda *_: levels.get_levels(), LevelFilter) | ||||||
|         self.add_collection("server", lambda *_: servers.get_servers(), ServerFilter) |         self.add_collection("server", lambda *_: servers.get_servers(), ServerFilter) | ||||||
|  |         self.add_collection("gameServer", lambda *_: game_servers.get_game_servers()) | ||||||
|         self.add_collection( |         self.add_collection( | ||||||
|             "userJoinedServer", lambda *_: user_joined_servers.get_user_joined_servers(), UserJoinedServerFilter |             "userJoinedServer", lambda *_: user_joined_servers.get_user_joined_servers(), UserJoinedServerFilter | ||||||
|         ) |         ) | ||||||
| @@ -59,8 +67,11 @@ class Query(QueryABC): | |||||||
|             UserJoinedGameServerFilter, |             UserJoinedGameServerFilter, | ||||||
|         ) |         ) | ||||||
|         self.add_collection("user", lambda *_: users.get_users(), UserFilter) |         self.add_collection("user", lambda *_: users.get_users(), UserFilter) | ||||||
|  |         self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter) | ||||||
|  |  | ||||||
|         self.set_field("guilds", self._resolve_guilds) |         self.set_field("guilds", self._resolve_guilds) | ||||||
|  |         self.set_field("achievementAttributes", lambda x, *_: achievement_service.get_attributes()) | ||||||
|  |         self.set_field("achievementOperators", lambda x, *_: achievement_service.get_operators()) | ||||||
|  |  | ||||||
|     def _resolve_guilds(self, *_, filter=None): |     def _resolve_guilds(self, *_, filter=None): | ||||||
|         if filter is None or "id" not in filter: |         if filter is None or "id" not in filter: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								kdb-bot/src/modules/achievements/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/modules/achievements/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # imports | ||||||
| @@ -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))) | ||||||
							
								
								
									
										116
									
								
								kdb-bot/src/modules/achievements/achievement_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								kdb-bot/src/modules/achievements/achievement_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | |||||||
|  | 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.configuration.server_settings import ServerSettings | ||||||
|  | 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.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 | ||||||
|  | from modules.base.configuration.base_server_settings import BaseServerSettings | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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: value <= expected_value, | ||||||
|  |             ">=": lambda value, expected_value: value >= expected_value, | ||||||
|  |             "<": lambda value, expected_value: value < expected_value, | ||||||
|  |             ">": lambda value, expected_value: value > expected_value, | ||||||
|  |             "contains": lambda value, expected_value: expected_value in value, | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     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: BaseServerSettings = self._config.get_configuration(f"BaseServerSettings_{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: ServerSettings = self._config.get_configuration(f"ServerSettings_{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, | ||||||
|  |         ) | ||||||
							
								
								
									
										44
									
								
								kdb-bot/src/modules/achievements/achievements.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								kdb-bot/src/modules/achievements/achievements.json
									
									
									
									
									
										Normal file
									
								
							| @@ -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>=2023.4.0.post2" | ||||||
|  |     ], | ||||||
|  |     "DevDependencies": [ | ||||||
|  |       "cpl-cli>=2023.4.0.post3" | ||||||
|  |     ], | ||||||
|  |     "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": [] | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								kdb-bot/src/modules/achievements/achievements_module.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								kdb-bot/src/modules/achievements/achievements_module.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | 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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AchievementsModule(ModuleABC): | ||||||
|  |     def __init__(self, dc: DiscordCollectionABC): | ||||||
|  |         ModuleABC.__init__(self, dc, FeatureFlagsEnum.auto_role_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) | ||||||
							
								
								
									
										1
									
								
								kdb-bot/src/modules/achievements/commands/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/modules/achievements/commands/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # imports | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | import discord | ||||||
|  | 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.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 modules.achievements.achievement_service import AchievementService | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AchievementGroup(DiscordCommandABC): | ||||||
|  |     def __init__( | ||||||
|  |         self, | ||||||
|  |         logger: CommandLogger, | ||||||
|  |         message_service: MessageServiceABC, | ||||||
|  |         bot: DiscordBotServiceABC, | ||||||
|  |         servers: ServerRepositoryABC, | ||||||
|  |         users: UserRepositoryABC, | ||||||
|  |         achievement_service: AchievementService, | ||||||
|  |         translate: TranslatePipe, | ||||||
|  |     ): | ||||||
|  |         DiscordCommandABC.__init__(self) | ||||||
|  |  | ||||||
|  |         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}") | ||||||
|  |  | ||||||
|  |         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") | ||||||
							
								
								
									
										1
									
								
								kdb-bot/src/modules/achievements/events/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/modules/achievements/events/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # imports | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | import discord | ||||||
|  | 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.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.achievements.achievement_service import AchievementService | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AchievementOnMessageEvent(OnMessageABC): | ||||||
|  |     def __init__( | ||||||
|  |         self, | ||||||
|  |         logger: LoggerABC, | ||||||
|  |         bot: DiscordBotServiceABC, | ||||||
|  |         achievements: AchievementService, | ||||||
|  |         db: DatabaseContextABC, | ||||||
|  |         servers: ServerRepositoryABC, | ||||||
|  |         users: UserRepositoryABC, | ||||||
|  |     ): | ||||||
|  |         OnMessageABC.__init__(self) | ||||||
|  |  | ||||||
|  |         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.author.bot: | ||||||
|  |             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._db.save_changes() | ||||||
|  |  | ||||||
|  |         await self._achievements.validate_achievements_for_user(user) | ||||||
							
								
								
									
										1
									
								
								kdb-bot/src/modules/achievements/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/modules/achievements/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # imports | ||||||
| @@ -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) | ||||||
| @@ -16,6 +16,7 @@ class BaseServerSettings(ConfigurationModelABC): | |||||||
|         self._max_message_xp_per_hour: int = 0 |         self._max_message_xp_per_hour: int = 0 | ||||||
|         self._xp_per_ontime_hour: int = 0 |         self._xp_per_ontime_hour: int = 0 | ||||||
|         self._xp_per_event_participation: int = 0 |         self._xp_per_event_participation: int = 0 | ||||||
|  |         self._xp_per_achievement: int = 0 | ||||||
|         self._afk_channel_ids: List[int] = List(int) |         self._afk_channel_ids: List[int] = List(int) | ||||||
|         self._afk_command_channel_id: int = 0 |         self._afk_command_channel_id: int = 0 | ||||||
|         self._help_command_reference_url: str = "" |         self._help_command_reference_url: str = "" | ||||||
| @@ -51,6 +52,10 @@ class BaseServerSettings(ConfigurationModelABC): | |||||||
|     def xp_per_event_participation(self) -> int: |     def xp_per_event_participation(self) -> int: | ||||||
|         return self._xp_per_event_participation |         return self._xp_per_event_participation | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def xp_per_achievement(self) -> int: | ||||||
|  |         return self._xp_per_achievement | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def afk_channel_ids(self) -> List[int]: |     def afk_channel_ids(self) -> List[int]: | ||||||
|         return self._afk_channel_ids |         return self._afk_channel_ids | ||||||
|   | |||||||
| @@ -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()}") |  | ||||||
| @@ -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()}") |  | ||||||
| @@ -6,13 +6,13 @@ from cpl_discord.container import Guild, Role, Member | |||||||
| from cpl_discord.service import DiscordBotServiceABC | from cpl_discord.service import DiscordBotServiceABC | ||||||
| from cpl_translation import TranslatePipe | from cpl_translation import TranslatePipe | ||||||
|  |  | ||||||
|  | from bot_core.configuration.server_settings import ServerSettings | ||||||
| from bot_core.service.message_service import MessageService | from bot_core.service.message_service import MessageService | ||||||
| from bot_data.model.level import Level | from bot_data.model.level import Level | ||||||
| from bot_data.model.user import User | from bot_data.model.user import User | ||||||
| from bot_data.service.level_repository_service import LevelRepositoryService | from bot_data.service.level_repository_service import LevelRepositoryService | ||||||
| from bot_data.service.server_repository_service import ServerRepositoryService | from bot_data.service.server_repository_service import ServerRepositoryService | ||||||
| from bot_data.service.user_repository_service import UserRepositoryService | from bot_data.service.user_repository_service import UserRepositoryService | ||||||
| from modules.level.configuration.level_server_settings import LevelServerSettings |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class LevelService: | class LevelService: | ||||||
| @@ -75,9 +75,9 @@ class LevelService: | |||||||
|             self._logger.error(__name__, f"Adding role {level_role.name} to {member.name} failed!", e) |             self._logger.error(__name__, f"Adding role {level_role.name} to {member.name} failed!", e) | ||||||
|  |  | ||||||
|         if notification_needed: |         if notification_needed: | ||||||
|             level_settings: LevelServerSettings = self._config.get_configuration(f"LevelServerSettings_{guild.id}") |             settings: ServerSettings = self._config.get_configuration(f"ServerSettings_{guild.id}") | ||||||
|             await self._message_service.send_channel_message( |             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), |                 self._t.transform("modules.level.new_level_message").format(member.mention, level.name), | ||||||
|                 is_persistent=True, |                 is_persistent=True, | ||||||
|             ) |             ) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|     "name": "kdb-web", |     "name": "kdb-web", | ||||||
|     "version": "1.0.7", |     "version": "1.0.dev268_achievements", | ||||||
|     "scripts": { |     "scripts": { | ||||||
|         "ng": "ng", |         "ng": "ng", | ||||||
|         "update-version": "ts-node-esm update-version.ts", |         "update-version": "ts-node-esm update-version.ts", | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								kdb-web/src/app/models/data/achievement.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								kdb-web/src/app/models/data/achievement.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||||
|  | } | ||||||
| @@ -4,6 +4,11 @@ import {Level} from "./level.model"; | |||||||
| import {Client} from "./client.model"; | import {Client} from "./client.model"; | ||||||
| import { AutoRole } from "./auto_role.model"; | import { AutoRole } from "./auto_role.model"; | ||||||
|  |  | ||||||
|  | export interface GameServer { | ||||||
|  |   id?: number; | ||||||
|  |   name?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface Server extends Data { | export interface Server extends Data { | ||||||
|   id?: number; |   id?: number; | ||||||
|   discordId?: String; |   discordId?: String; | ||||||
|   | |||||||
| @@ -4,12 +4,15 @@ import { Server, ServerFilter } from "./server.model"; | |||||||
| import { UserJoinedServer } from "./user_joined_server.model"; | import { UserJoinedServer } from "./user_joined_server.model"; | ||||||
| import { UserJoinedVoiceChannel } from "./user_joined_voice_channel.model"; | import { UserJoinedVoiceChannel } from "./user_joined_voice_channel.model"; | ||||||
| import { UserJoinedGameServer } from "./user_joined_game_server.model"; | import { UserJoinedGameServer } from "./user_joined_game_server.model"; | ||||||
|  | import { Achievement } from "./achievement.model"; | ||||||
|  |  | ||||||
| export interface User extends DataWithHistory { | export interface User extends DataWithHistory { | ||||||
|   id?: number; |   id?: number; | ||||||
|   discordId?: number; |   discordId?: number; | ||||||
|   name?: string; |   name?: string; | ||||||
|   xp?: number; |   xp?: number; | ||||||
|  |   message_count?: number; | ||||||
|  |   reaction_count?: number; | ||||||
|   ontime?: number; |   ontime?: number; | ||||||
|   level?: Level; |   level?: Level; | ||||||
|   server?: Server; |   server?: Server; | ||||||
| @@ -23,6 +26,9 @@ export interface User extends DataWithHistory { | |||||||
|  |  | ||||||
|   userJoinedGameServerCount?: number; |   userJoinedGameServerCount?: number; | ||||||
|   userJoinedGameServers?: UserJoinedGameServer[]; |   userJoinedGameServers?: UserJoinedGameServer[]; | ||||||
|  |  | ||||||
|  |   achievementCount?: number; | ||||||
|  |   achievements?: Achievement[]; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface UserFilter { | export interface UserFilter { | ||||||
|   | |||||||
| @@ -121,4 +121,48 @@ 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 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   `; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -46,6 +46,16 @@ export class Queries { | |||||||
|     } |     } | ||||||
|   `; |   `; | ||||||
|  |  | ||||||
|  |   static gameServerQuery = ` | ||||||
|  |       query GameServersList($serverId: ID) { | ||||||
|  |       servers(filter: {id: $serverId}) { | ||||||
|  |         gameServers { | ||||||
|  |           name | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ` | ||||||
|  |  | ||||||
|   static levelQuery = ` |   static levelQuery = ` | ||||||
|     query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) { |     query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) { | ||||||
|       servers(filter: {id: $serverId}) { |       servers(filter: {id: $serverId}) { | ||||||
| @@ -90,6 +100,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 = ` |   static usersQuery = ` | ||||||
|     query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) { |     query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) { | ||||||
|       servers(filter: {id: $serverId}) { |       servers(filter: {id: $serverId}) { | ||||||
| @@ -158,6 +224,12 @@ export class Queries { | |||||||
|             joinedOn |             joinedOn | ||||||
|             leavedOn |             leavedOn | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           achievements { | ||||||
|  |             id | ||||||
|  |             name | ||||||
|  |             createdAt | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| import { Server } from "../data/server.model"; | import { GameServer, Server } from "../data/server.model"; | ||||||
| import { User } from "../data/user.model"; | import { User } from "../data/user.model"; | ||||||
| import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; | import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; | ||||||
| import { Guild } from "../data/discord.model"; | import { Guild } from "../data/discord.model"; | ||||||
| import { Level } from "../data/level.model"; | import { Level } from "../data/level.model"; | ||||||
|  | import { Achievement, AchievementAttribute } from "../data/achievement.model"; | ||||||
|  |  | ||||||
| export interface Query { | export interface Query { | ||||||
|   serverCount: number; |   serverCount: number; | ||||||
| @@ -18,11 +19,26 @@ export interface UserListQuery { | |||||||
|   users: User[]; |   users: User[]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface GameServerListQuery { | ||||||
|  |   gameServerCount: number; | ||||||
|  |   gameServers: GameServer[]; | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface LevelListQuery { | export interface LevelListQuery { | ||||||
|   levelCount: number; |   levelCount: number; | ||||||
|   levels: Level[]; |   levels: Level[]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface AchievementTypeQuery { | ||||||
|  |   achievementAttributes: AchievementAttribute[]; | ||||||
|  |   achievementOperators: string[]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface AchievementListQuery { | ||||||
|  |   achievementCount: number; | ||||||
|  |   achievements: Achievement[]; | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface AutoRoleQuery { | export interface AutoRoleQuery { | ||||||
|   autoRoleCount: number; |   autoRoleCount: number; | ||||||
|   autoRoles: AutoRole[]; |   autoRoles: AutoRole[]; | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import { User } from "../data/user.model"; | |||||||
| import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; | import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; | ||||||
| import { Level } from "../data/level.model"; | import { Level } from "../data/level.model"; | ||||||
| import { Server } from "../data/server.model"; | import { Server } from "../data/server.model"; | ||||||
|  | import { Achievement } from "../data/achievement.model"; | ||||||
|  |  | ||||||
| export interface GraphQLResult { | export interface GraphQLResult { | ||||||
|   data: { |   data: { | ||||||
| @@ -45,3 +46,11 @@ export interface LevelMutationResult { | |||||||
|     deleteLevel?: Level |     deleteLevel?: Level | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface AchievementMutationResult { | ||||||
|  |   achievement: { | ||||||
|  |     createAchievement?: Achievement | ||||||
|  |     updateAchievement?: Achievement | ||||||
|  |     deleteAchievement?: Achievement | ||||||
|  |   }; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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 { | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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 { } | ||||||
| @@ -0,0 +1,258 @@ | |||||||
|  | <h1> | ||||||
|  |   {{'view.server.achievements.header' | translate}} | ||||||
|  | </h1> | ||||||
|  | <div class="content-wrapper"> | ||||||
|  |   <div class="content"> | ||||||
|  |     <p-table #dt [value]="achievements" dataKey="id" editMode="row" [rowHover]="true" [rows]="10" | ||||||
|  |              [rowsPerPageOptions]="[10,25,50]" [paginator]="true" [loading]="loading" [totalRecords]="totalRecords" | ||||||
|  |              [lazy]="true" (onLazyLoad)="nextPage($event)"> | ||||||
|  |  | ||||||
|  |       <ng-template pTemplate="caption"> | ||||||
|  |         <div class="table-caption"> | ||||||
|  |           <div class="table-caption-text"> | ||||||
|  |             <ng-container *ngIf="!loading">{{achievements.length}} {{'common.of' | translate}} | ||||||
|  |               {{dt.totalRecords}} | ||||||
|  |             </ng-container> | ||||||
|  |             {{'view.server.achievements.achievements' | translate}} | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <div class="table-caption-btn-wrapper btn-wrapper"> | ||||||
|  |             <button pButton label="{{'common.add' | translate}}" class="icon-btn btn" | ||||||
|  |                     icon="pi pi-user-plus" (click)="addAchievement(dt)" [disabled]="isEditingNew || !user?.isModerator && !user?.isAdmin"> | ||||||
|  |             </button> | ||||||
|  |             <button pButton label="{{'common.reset_filters' | translate}}" icon="pi pi-undo" | ||||||
|  |                     class="icon-btn btn" (click)="resetFilters()"> | ||||||
|  |             </button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </ng-template> | ||||||
|  |  | ||||||
|  |       <ng-template pTemplate="header"> | ||||||
|  |         <tr> | ||||||
|  |           <th pSortableColumn="id"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'common.id' | translate}}</div> | ||||||
|  |               <p-sortIcon field="id" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th pSortableColumn="name"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'view.server.achievements.headers.name' | translate}}</div> | ||||||
|  |               <p-sortIcon field="name" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th pSortableColumn="description"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'view.server.achievements.headers.description' | translate}}</div> | ||||||
|  |               <p-sortIcon field="name" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th pSortableColumn="attribute"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'view.server.achievements.headers.attribute' | translate}}</div> | ||||||
|  |               <p-sortIcon field="attribute" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th pSortableColumn="operator"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'view.server.achievements.headers.operator' | translate}}</div> | ||||||
|  |               <p-sortIcon field="operator" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th pSortableColumn="value"> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'view.server.achievements.headers.value' | translate}}</div> | ||||||
|  |               <p-sortIcon field="value" class="table-header-icon"></p-sortIcon> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'common.created_at' | translate}}</div> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'common.modified_at' | translate}}</div> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |  | ||||||
|  |           <th> | ||||||
|  |             <div class="table-header-label"> | ||||||
|  |               <div class="table-header-text">{{'common.actions' | translate}}</div> | ||||||
|  |             </div> | ||||||
|  |           </th> | ||||||
|  |         </tr> | ||||||
|  |  | ||||||
|  |         <tr> | ||||||
|  |           <th class="table-header-small"> | ||||||
|  |             <form [formGroup]="filterForm"> | ||||||
|  |               <input type="text" pInputText formControlName="id" | ||||||
|  |                      placeholder="{{'common.id' | translate}}"> | ||||||
|  |             </form> | ||||||
|  |           </th> | ||||||
|  |           <th> | ||||||
|  |             <form [formGroup]="filterForm"> | ||||||
|  |               <input type="text" pInputText formControlName="name" | ||||||
|  |                      placeholder="{{'view.server.achievements.headers.name' | translate}}"> | ||||||
|  |             </form> | ||||||
|  |           </th> | ||||||
|  |           <th> | ||||||
|  |             <form [formGroup]="filterForm"> | ||||||
|  |               <input type="text" pInputText formControlName="name" | ||||||
|  |                      placeholder="{{'view.server.achievements.headers.description' | translate}}"> | ||||||
|  |             </form> | ||||||
|  |           </th> | ||||||
|  |           <th></th> | ||||||
|  |           <th></th> | ||||||
|  |           <th></th> | ||||||
|  |           <th class="table-header-small-dropdown"></th> | ||||||
|  |           <th class="table-header-small-dropdown"></th> | ||||||
|  |           <th class="table-header-actions"></th> | ||||||
|  |         </tr> | ||||||
|  |       </ng-template> | ||||||
|  |  | ||||||
|  |       <ng-template pTemplate="body" let-achievement let-editing="editing" let-ri="rowIndex"> | ||||||
|  |         <tr [pEditableRow]="achievement"> | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 {{achievement.id}} | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.id}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <input class="table-edit-input" pInputText type="text" [(ngModel)]="achievement.name"> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.name}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <input class="table-edit-input" pInputText type="text" [(ngModel)]="achievement.description"> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.description}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <p-dropdown [options]="attributes" [(ngModel)]="achievement.attribute" | ||||||
|  |                             placeholder="{{'view.server.achievements.headers.attribute' | translate}}"></p-dropdown> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.attribute}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <p-dropdown [options]="operators" [(ngModel)]="achievement.operator" placeholder="{{'view.server.achievements.headers.operator' | translate}}"></p-dropdown> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.operator}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor *ngIf="getAchievementAttributeByName(achievement.attribute)?.type === 'number'"> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <input class="table-edit-input" pInputText min="0" type="number" [(ngModel)]="achievement.value"> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.value}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |  | ||||||
|  |             <p-cellEditor *ngIf="getAchievementAttributeByName(achievement.attribute)?.type === 'Level'"> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <p-dropdown [options]="levels" [(ngModel)]="achievement.value" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.value}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |  | ||||||
|  |             <p-cellEditor *ngIf="getAchievementAttributeByName(achievement.attribute)?.type === 'GameServer'"> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 <p-dropdown [options]="gameServers" [(ngModel)]="achievement.value" placeholder="{{'view.server.profile.joined_game_server.name' | translate}}"></p-dropdown> | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.value}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |  | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 {{achievement.createdAt | date:'dd.MM.yy HH:mm'}} | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.createdAt | date:'dd.MM.yy HH:mm'}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |           <td> | ||||||
|  |             <p-cellEditor> | ||||||
|  |               <ng-template pTemplate="input"> | ||||||
|  |                 {{achievement.modifiedAt | date:'dd.MM.yy HH:mm'}} | ||||||
|  |               </ng-template> | ||||||
|  |               <ng-template pTemplate="output"> | ||||||
|  |                 {{achievement.modifiedAt | date:'dd.MM.yy HH:mm'}} | ||||||
|  |               </ng-template> | ||||||
|  |             </p-cellEditor> | ||||||
|  |           </td> | ||||||
|  |           <td> | ||||||
|  |             <div class="btn-wrapper"> | ||||||
|  |               <app-history-btn *ngIf="!isEditingNew" [id]="achievement.id" [query]="query" translationKey="view.server.achievements.header"></app-history-btn> | ||||||
|  |               <button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" | ||||||
|  |                       (click)="onRowEditInit(dt, achievement, ri)" [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||||
|  |               <button *ngIf="!editing" pButton class="btn icon-btn danger-icon-btn" icon="pi pi-trash" | ||||||
|  |                       (click)="deleteAchievement(achievement)" [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||||
|  |  | ||||||
|  |               <button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn" | ||||||
|  |                       icon="pi pi-check-circle" (click)="onRowEditSave(dt, achievement, ri)" [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||||
|  |               <button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn" | ||||||
|  |                       icon="pi pi-times-circle" (click)="onRowEditCancel(ri)" [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||||
|  |             </div> | ||||||
|  |           </td> | ||||||
|  |         </tr> | ||||||
|  |       </ng-template> | ||||||
|  |  | ||||||
|  |       <ng-template pTemplate="emptymessage"> | ||||||
|  |         <tr></tr> | ||||||
|  |         <tr> | ||||||
|  |           <td colspan="10">{{'common.no_entries_found' | translate}}</td> | ||||||
|  |         </tr> | ||||||
|  |         <tr></tr> | ||||||
|  |       </ng-template> | ||||||
|  |  | ||||||
|  |       <ng-template pTemplate="paginatorleft"> | ||||||
|  |       </ng-template> | ||||||
|  |     </p-table> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  |  | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||||
|  |  | ||||||
|  | import { AchievementComponent } from './achievement.component'; | ||||||
|  |  | ||||||
|  | describe('AchievementComponent', () => { | ||||||
|  |   let component: AchievementComponent; | ||||||
|  |   let fixture: ComponentFixture<AchievementComponent>; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await TestBed.configureTestingModule({ | ||||||
|  |       declarations: [ AchievementComponent ] | ||||||
|  |     }) | ||||||
|  |     .compileComponents(); | ||||||
|  |  | ||||||
|  |     fixture = TestBed.createComponent(AchievementComponent); | ||||||
|  |     component = fixture.componentInstance; | ||||||
|  |     fixture.detectChanges(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should create', () => { | ||||||
|  |     expect(component).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,323 @@ | |||||||
|  | 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"; | ||||||
|  |  | ||||||
|  | @Component({ | ||||||
|  |   selector: "app-achievement", | ||||||
|  |   templateUrl: "./achievement.component.html", | ||||||
|  |   styleUrls: ["./achievement.component.scss"] | ||||||
|  | }) | ||||||
|  | export class AchievementComponent implements OnInit, OnDestroy { | ||||||
|  |   public achievements: Achievement[] = []; | ||||||
|  |   public loading = true; | ||||||
|  |  | ||||||
|  |   public isEditingNew: boolean = false; | ||||||
|  |  | ||||||
|  |   public filterForm!: FormGroup<{ | ||||||
|  |     id: FormControl<number | null>, | ||||||
|  |     name: FormControl<string | null>, | ||||||
|  |     color: FormControl<string | null>, | ||||||
|  |     min_xp: FormControl<number | null>, | ||||||
|  |     permissions: FormControl<string | null>, | ||||||
|  |   }>; | ||||||
|  |  | ||||||
|  |   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<void>(); | ||||||
|  |   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) { | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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<LevelListQuery>(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<GameServerListQuery>(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<AchievementListQuery>(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<AchievementTypeQuery>(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<number | null>(null), | ||||||
|  |       name: new FormControl<string | null>(null), | ||||||
|  |       color: new FormControl<string | null>(null), | ||||||
|  |       min_xp: new FormControl<number | null>(null), | ||||||
|  |       permissions: new FormControl<string | null>(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<AchievementMutationResult>(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(); | ||||||
|  |         this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_create_failed"), this.translate.instant("view.server.achievements.message.achievement_create_failed_d")); | ||||||
|  |         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<AchievementMutationResult>(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(); | ||||||
|  |       this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_update_failed"), this.translate.instant("view.server.achievements.message.achievement_update_failed_d", { name: newAchievement.name })); | ||||||
|  |       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<AchievementMutationResult>(Mutations.deleteAchievement, { | ||||||
|  |             id: achievement.id | ||||||
|  |           } | ||||||
|  |         ).pipe(catchError(err => { | ||||||
|  |           this.spinner.hideSpinner(); | ||||||
|  |           this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_delete_failed"), this.translate.instant("view.server.achievements.message.achievement_delete_failed_d", { name: achievement.name })); | ||||||
|  |           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 = 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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -78,6 +78,22 @@ | |||||||
|  |  | ||||||
|     <div class="content-divider"></div> |     <div class="content-divider"></div> | ||||||
|  |  | ||||||
|  |     <p-panel header="{{'view.server.profile.achievements.header' | translate}}" [toggleable]="true"> | ||||||
|  |       <div *ngFor="let achievement of user.achievements;"> | ||||||
|  |         <div class="content-row"> | ||||||
|  |           <div class="content-column"> | ||||||
|  |             <div class="content-data-name">{{'common.name' | translate}}:</div> | ||||||
|  |             <div class="content-data-value">{{achievement.name}}</div> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <div class="content-column"> | ||||||
|  |             <div class="content-data-name">{{'view.server.profile.achievements.time' | translate}}:</div> | ||||||
|  |             <div class="content-data-value">{{achievement.createdAt | date:'dd.MM.yyyy HH:mm:ss'}}</div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </p-panel> | ||||||
|  |  | ||||||
|     <p-panel header="{{'view.server.profile.joined_voice_channel.header' | translate}}" [toggleable]="true"> |     <p-panel header="{{'view.server.profile.joined_voice_channel.header' | translate}}" [toggleable]="true"> | ||||||
|       <div *ngFor="let join of user.joinedVoiceChannels;"> |       <div *ngFor="let join of user.joinedVoiceChannels;"> | ||||||
|         <div class="content-row"> |         <div class="content-row"> | ||||||
| @@ -102,8 +118,8 @@ | |||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|     </p-panel> |     </p-panel> | ||||||
|  |  | ||||||
|     <p-panel header="{{'view.server.profile.joined_game_server.header' | translate}}" [toggleable]="true"> |     <p-panel header="{{'view.server.profile.joined_game_server.header' | translate}}" [toggleable]="true"> | ||||||
|       <div *ngFor="let join of user.userJoinedGameServers;"> |       <div *ngFor="let join of user.userJoinedGameServers;"> | ||||||
|         <div class="content-row"> |         <div class="content-row"> | ||||||
|   | |||||||
| @@ -9,7 +9,8 @@ const routes: Routes = [ | |||||||
|   { path: "members", component: MembersComponent }, |   { path: "members", component: MembersComponent }, | ||||||
|   { path: "members/:memberId", component: ProfileComponent }, |   { path: "members/:memberId", component: ProfileComponent }, | ||||||
|   { path: "auto-roles", loadChildren: () => import("./auto-role/auto-role.module").then(m => m.AutoRoleModule) }, |   { path: "auto-roles", loadChildren: () => import("./auto-role/auto-role.module").then(m => m.AutoRoleModule) }, | ||||||
|   { path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule) } |   { path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule) }, | ||||||
|  |   { path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule) } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ export class SidebarService { | |||||||
|   serverMembers: MenuItem = {}; |   serverMembers: MenuItem = {}; | ||||||
|   serverAutoRoles: MenuItem = {}; |   serverAutoRoles: MenuItem = {}; | ||||||
|   serverLevels: MenuItem = {}; |   serverLevels: MenuItem = {}; | ||||||
|  |   serverAchievements: MenuItem = {}; | ||||||
|   serverMenu: MenuItem = {}; |   serverMenu: MenuItem = {}; | ||||||
|   adminConfig: MenuItem = {}; |   adminConfig: MenuItem = {}; | ||||||
|   adminUsers: MenuItem = {}; |   adminUsers: MenuItem = {}; | ||||||
| @@ -102,12 +103,19 @@ export class SidebarService { | |||||||
|       routerLink: `server/${this.server$.value?.id}/levels` |       routerLink: `server/${this.server$.value?.id}/levels` | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     this.serverAchievements = { | ||||||
|  |       label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.achievements") : "", | ||||||
|  |       icon: "pi pi-angle-double-up", | ||||||
|  |       visible: true, | ||||||
|  |       routerLink: `server/${this.server$.value?.id}/achievements` | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     this.serverMenu = { |     this.serverMenu = { | ||||||
|       label: this.isSidebarOpen ? this.server$.value?.name : "", |       label: this.isSidebarOpen ? this.server$.value?.name : "", | ||||||
|       icon: "pi pi-server", |       icon: "pi pi-server", | ||||||
|       visible: false, |       visible: false, | ||||||
|       expanded: true, |       expanded: true, | ||||||
|       items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels] |       items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements] | ||||||
|     }; |     }; | ||||||
|     this.adminConfig = { |     this.adminConfig = { | ||||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", |       label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", | ||||||
| @@ -142,6 +150,7 @@ export class SidebarService { | |||||||
|         this.serverMembers.visible = !!user?.isModerator; |         this.serverMembers.visible = !!user?.isModerator; | ||||||
|         this.serverAutoRoles.visible = !!user?.isModerator; |         this.serverAutoRoles.visible = !!user?.isModerator; | ||||||
|         this.serverLevels.visible = !!user?.isModerator; |         this.serverLevels.visible = !!user?.isModerator; | ||||||
|  |         this.serverAchievements.visible = !!user?.isModerator; | ||||||
|       } else { |       } else { | ||||||
|         this.serverMenu.visible = false; |         this.serverMenu.visible = false; | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|     "WebVersion": { |     "WebVersion": { | ||||||
|         "Major": "1", |         "Major": "1", | ||||||
|         "Minor": "0", |         "Minor": "0", | ||||||
|         "Micro": "7" |         "Micro": "dev268_achievements" | ||||||
|     }, |     }, | ||||||
|     "Themes": [ |     "Themes": [ | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -97,6 +97,7 @@ | |||||||
|       "wrong_password": "Falsches Passwort" |       "wrong_password": "Falsches Passwort" | ||||||
|     }, |     }, | ||||||
|     "register": { |     "register": { | ||||||
|  |       "confirm_privacy": "Ich erkläre mich mit der <a href=\"{{url}}\">Datenschutzerklärung</a> einverstanden.", | ||||||
|       "email_required": "E-Mail benötigt", |       "email_required": "E-Mail benötigt", | ||||||
|       "emails_not_match": "E-Mails stimmen nicht überein", |       "emails_not_match": "E-Mails stimmen nicht überein", | ||||||
|       "first_name": "Vorname", |       "first_name": "Vorname", | ||||||
| @@ -113,8 +114,7 @@ | |||||||
|       "register_with_discord": "Mit Discord Registrieren", |       "register_with_discord": "Mit Discord Registrieren", | ||||||
|       "repeat_email": "E-Mail wiederholen", |       "repeat_email": "E-Mail wiederholen", | ||||||
|       "repeat_password": "Passwort wiederholen", |       "repeat_password": "Passwort wiederholen", | ||||||
|       "user_already_exists": "Benutzer existiert bereits", |       "user_already_exists": "Benutzer existiert bereits" | ||||||
|       "confirm_privacy": "Ich erkläre mich mit der <a href=\"{{url}}\">Datenschutzerklärung</a> einverstanden." |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "common": { |   "common": { | ||||||
| @@ -147,12 +147,16 @@ | |||||||
|       "permissions": "Berechtigung", |       "permissions": "Berechtigung", | ||||||
|       "roleId": "Rolle", |       "roleId": "Rolle", | ||||||
|       "server": "Server", |       "server": "Server", | ||||||
|       "xp": "XP" |       "xp": "XP", | ||||||
|  |       "attribute": "Attribut", | ||||||
|  |       "operator": "Operator", | ||||||
|  |       "value": "Wert" | ||||||
|     }, |     }, | ||||||
|     "id": "Id", |     "id": "Id", | ||||||
|     "joined_at": "Beigetreten am", |     "joined_at": "Beigetreten am", | ||||||
|     "leaved_at": "Verlassen am", |     "leaved_at": "Verlassen am", | ||||||
|     "modified_at": "Bearbeitet am", |     "modified_at": "Bearbeitet am", | ||||||
|  |     "name": "Name", | ||||||
|     "no_entries_found": "Keine Einträge gefunden", |     "no_entries_found": "Keine Einträge gefunden", | ||||||
|     "of": "von", |     "of": "von", | ||||||
|     "reset_filters": "Filter zurücksetzen" |     "reset_filters": "Filter zurücksetzen" | ||||||
| @@ -276,6 +280,7 @@ | |||||||
|     "dashboard": "Dashboard", |     "dashboard": "Dashboard", | ||||||
|     "members": "Mitglieder", |     "members": "Mitglieder", | ||||||
|     "server": { |     "server": { | ||||||
|  |       "achievements": "Errungenschaften", | ||||||
|       "auto_roles": "Auto Rollen", |       "auto_roles": "Auto Rollen", | ||||||
|       "dashboard": "Dashboard", |       "dashboard": "Dashboard", | ||||||
|       "levels": "Level", |       "levels": "Level", | ||||||
| @@ -315,6 +320,33 @@ | |||||||
|       "servers": "Server" |       "servers": "Server" | ||||||
|     }, |     }, | ||||||
|     "server": { |     "server": { | ||||||
|  |       "achievements": { | ||||||
|  |         "achievements": "Errungenschaften", | ||||||
|  |         "header": "Errungenschaften", | ||||||
|  |         "headers": { | ||||||
|  |           "attribute": "Attribut", | ||||||
|  |           "name": "Name", | ||||||
|  |           "description": "Beschreibung", | ||||||
|  |           "operator": "Operator", | ||||||
|  |           "value": "Wert" | ||||||
|  |         }, | ||||||
|  |         "message": { | ||||||
|  |           "achievement_create": "Errungenschaft erstellt", | ||||||
|  |           "achievement_create_d": "Errungenschaft {{name}} erfolgreich erstellt", | ||||||
|  |           "achievement_create_failed": "Errungenschaft Erstellung fehlgeschlagen", | ||||||
|  |           "achievement_create_failed_d": "Die Erstellung der Errungenschaft ist fehlgeschlagen!", | ||||||
|  |           "achievement_delete": "Errungenschaft löschen", | ||||||
|  |           "achievement_delete_failed": "Errungenschaft Löschung fehlgeschlagen", | ||||||
|  |           "achievement_delete_failed_d": "Die Löschung der Errungenschaft {{name}} ist fehlgeschlagen!", | ||||||
|  |           "achievement_delete_q": "Sind Sie sich sicher, dass Sie das Errungenschaft {{name}} löschen möchten?", | ||||||
|  |           "achievement_deleted": "Errungenschaft gelöscht", | ||||||
|  |           "achievement_deleted_d": "Errungenschaft {{name}} erfolgreich gelöscht", | ||||||
|  |           "achievement_update": "Errungenschaft bearbeitet", | ||||||
|  |           "achievement_update_d": "Errungenschaft {{name}} erfolgreich bearbeitet", | ||||||
|  |           "achievement_update_failed": "Errungenschaft Bearbeitung fehlgeschlagen", | ||||||
|  |           "achievement_update_failed_d": "Die Bearbeitung der Errungenschaft ist fehlgeschlagen!" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|       "auto_roles": { |       "auto_roles": { | ||||||
|         "auto_roles": "Auto Rollen", |         "auto_roles": "Auto Rollen", | ||||||
|         "header": "Auto Rollen", |         "header": "Auto Rollen", | ||||||
| @@ -415,6 +447,10 @@ | |||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "profile": { |       "profile": { | ||||||
|  |         "achievements": { | ||||||
|  |           "header": "Errungeschaften", | ||||||
|  |           "time": "Erreicht am" | ||||||
|  |         }, | ||||||
|         "header": "Dein Profil", |         "header": "Dein Profil", | ||||||
|         "joined_game_server": { |         "joined_game_server": { | ||||||
|           "header": "Gameserver-beitritte", |           "header": "Gameserver-beitritte", | ||||||
|   | |||||||
| @@ -97,6 +97,7 @@ | |||||||
|       "wrong_password": "Wrong password" |       "wrong_password": "Wrong password" | ||||||
|     }, |     }, | ||||||
|     "register": { |     "register": { | ||||||
|  |       "confirm_privacy": "I agree to the <a href=\"{{url}}\">Privacy Policy</a>.", | ||||||
|       "email_required": "E-Mail required", |       "email_required": "E-Mail required", | ||||||
|       "emails_not_match": "E-Mails do not match", |       "emails_not_match": "E-Mails do not match", | ||||||
|       "first_name": "First name", |       "first_name": "First name", | ||||||
| @@ -113,8 +114,7 @@ | |||||||
|       "register_with_discord": "Register with discord", |       "register_with_discord": "Register with discord", | ||||||
|       "repeat_email": "Repeat E-mail", |       "repeat_email": "Repeat E-mail", | ||||||
|       "repeat_password": "Repeat password", |       "repeat_password": "Repeat password", | ||||||
|       "user_already_exists": "User already exists", |       "user_already_exists": "User already exists" | ||||||
|       "confirm_privacy": "I agree to the <a href=\"{{url}}\">Privacy Policy</a>." |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "common": { |   "common": { | ||||||
| @@ -147,12 +147,16 @@ | |||||||
|       "permissions": "Permissions", |       "permissions": "Permissions", | ||||||
|       "roleId": "Role", |       "roleId": "Role", | ||||||
|       "server": "Server", |       "server": "Server", | ||||||
|       "xp": "XP" |       "xp": "XP", | ||||||
|  |       "attribute": "Attribute", | ||||||
|  |       "operator": "Operator", | ||||||
|  |       "value": "Value" | ||||||
|     }, |     }, | ||||||
|     "id": "Id", |     "id": "Id", | ||||||
|     "joined_at": "Joined at", |     "joined_at": "Joined at", | ||||||
|     "leaved_at": "Leaved at", |     "leaved_at": "Leaved at", | ||||||
|     "modified_at": "Modified at", |     "modified_at": "Modified at", | ||||||
|  |     "name": "Name", | ||||||
|     "no_entries_found": "No entries found", |     "no_entries_found": "No entries found", | ||||||
|     "of": "of", |     "of": "of", | ||||||
|     "reset_filters": "Reset filters" |     "reset_filters": "Reset filters" | ||||||
| @@ -276,6 +280,7 @@ | |||||||
|     "dashboard": "Dashboard", |     "dashboard": "Dashboard", | ||||||
|     "members": "Members", |     "members": "Members", | ||||||
|     "server": { |     "server": { | ||||||
|  |       "achievements": "Achievements", | ||||||
|       "auto_roles": "Auto role", |       "auto_roles": "Auto role", | ||||||
|       "dashboard": "Dashboard", |       "dashboard": "Dashboard", | ||||||
|       "levels": "Level", |       "levels": "Level", | ||||||
| @@ -315,6 +320,32 @@ | |||||||
|       "servers": "Server" |       "servers": "Server" | ||||||
|     }, |     }, | ||||||
|     "server": { |     "server": { | ||||||
|  |       "achievements": { | ||||||
|  |         "achievements": "Achievements", | ||||||
|  |         "header": "Achievements", | ||||||
|  |         "headers": { | ||||||
|  |           "attribute": "Attribute", | ||||||
|  |           "name": "Namer", | ||||||
|  |           "operator": "Operator", | ||||||
|  |           "value": "Value" | ||||||
|  |         }, | ||||||
|  |         "message": { | ||||||
|  |           "achievement_create": "Achievement created", | ||||||
|  |           "achievement_create_d": "Achievement {{name}} successfully created", | ||||||
|  |           "achievement_create_failed": "Achievement creation failed", | ||||||
|  |           "achievement_create_failed_d": "Creation of achievement failed!", | ||||||
|  |           "achievement_delete": "Delete achievement", | ||||||
|  |           "achievement_delete_failed": "Achievement deletion failed", | ||||||
|  |           "achievement_delete_failed_d": "Deletion of achievement {{name}} failed!", | ||||||
|  |           "achievement_delete_q": "Are you sure you want to delete the {{name}} achievement?", | ||||||
|  |           "achievement_deleted": "Achievement deleted", | ||||||
|  |           "achievement_deleted_d": "Achievement {{name}} successfully deleted\t", | ||||||
|  |           "achievement_update": "Achievement edited", | ||||||
|  |           "achievement_update_d": "Achievement {{name}} edited successfully", | ||||||
|  |           "achievement_update_failed": "Achievement editing failed", | ||||||
|  |           "achievement_update_failed_d": "Achievement editing failed!" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|       "auto_roles": { |       "auto_roles": { | ||||||
|         "auto_roles": "Auto roles", |         "auto_roles": "Auto roles", | ||||||
|         "header": "Auto roles", |         "header": "Auto roles", | ||||||
| @@ -415,6 +446,10 @@ | |||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "profile": { |       "profile": { | ||||||
|  |         "achievements": { | ||||||
|  |           "header": "Achievements", | ||||||
|  |           "time": "Reached at" | ||||||
|  |         }, | ||||||
|         "header": "Profile", |         "header": "Profile", | ||||||
|         "joined_game_server": { |         "joined_game_server": { | ||||||
|           "header": "Game server accessions", |           "header": "Game server accessions", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user