diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 359f9c38..20adf415 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 359f9c38c3ec825a89f3bf289a65ec035cfcb693 +Subproject commit 20adf415f9f02792aa0a873fac77b308405e38a4 diff --git a/kdb-bot/src/bot/main.py b/kdb-bot/src/bot/main.py index 48b66eed..c004dd7d 100644 --- a/kdb-bot/src/bot/main.py +++ b/kdb-bot/src/bot/main.py @@ -31,10 +31,10 @@ class Program: .use_extension(StartupDiscordExtension) .use_extension(StartupModuleExtension) .use_extension(StartupMigrationExtension) + .use_extension(DatabaseExtension) .use_extension(ConfigExtension) .use_extension(InitBotExtension) .use_extension(BootLogExtension) - .use_extension(DatabaseExtension) .use_extension(AppApiExtension) .use_extension(CoreExtension) .use_startup(Startup) diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index 796b8257..89c5ae7d 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -9,6 +9,7 @@ from bot_data.migration.api_key_migration import ApiKeyMigration from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration from bot_data.migration.auto_role_migration import AutoRoleMigration +from bot_data.migration.config_feature_flags_migration import ConfigFeatureFlagsMigration from bot_data.migration.config_migration import ConfigMigration from bot_data.migration.db_history_migration import DBHistoryMigration from bot_data.migration.initial_migration import InitialMigration @@ -46,3 +47,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, DBHistoryMigration) # 06.03.2023 #246 - 1.0.0 services.add_transient(MigrationABC, AchievementsMigration) # 14.06.2023 #268 - 1.1.0 services.add_transient(MigrationABC, ConfigMigration) # 19.07.2023 #127 - 1.1.0 + services.add_transient(MigrationABC, ConfigFeatureFlagsMigration) # 15.08.2023 #334 - 1.1.0 diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py index 9bd72c1f..2ef3f70f 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py @@ -5,18 +5,17 @@ class FeatureFlagsEnum(Enum): # modules achievements_module = "AchievementsModule" api_module = "ApiModule" - admin_module = "AdminModule" auto_role_module = "AutoRoleModule" base_module = "BaseModule" boot_log_module = "BootLogModule" core_module = "CoreModule" core_extension_module = "CoreExtensionModule" + config_module = "ConfigModule" data_module = "DataModule" database_module = "DatabaseModule" level_module = "LevelModule" moderator_module = "ModeratorModule" permission_module = "PermissionModule" - config_module = "ConfigModule" # features api_only = "ApiOnly" presence = "Presence" diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py index 19aeb110..f132cc03 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py @@ -8,8 +8,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): # modules FeatureFlagsEnum.achievements_module.value: False, # 14.06.2023 #268 FeatureFlagsEnum.api_module.value: False, # 13.10.2022 #70 - FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48 - FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54 + FeatureFlagsEnum.auto_role_module.value: False, # 03.10.2022 #54 FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48 FeatureFlagsEnum.boot_log_module.value: True, # 02.10.2022 #48 FeatureFlagsEnum.core_module.value: True, # 03.10.2022 #56 @@ -34,6 +33,17 @@ class FeatureFlagsSettings(ConfigurationModelABC): for flag in [f.value for f in FeatureFlagsEnum]: self._load_flag(kwargs, FeatureFlagsEnum(flag)) + @classmethod + def get_flag_from_dict(cls, flags: dict, key: FeatureFlagsEnum) -> bool: + def get_flag(): + if key.value not in cls._flags: + return False + return cls._flags[key.value] + + if key.value not in flags: + return get_flag() + return flags[key.value] + def get_flag(self, key: FeatureFlagsEnum) -> bool: if key.value not in self._flags: return False diff --git a/kdb-bot/src/bot_core/core_module.py b/kdb-bot/src/bot_core/core_module.py index b7397b36..fcb06333 100644 --- a/kdb-bot/src/bot_core/core_module.py +++ b/kdb-bot/src/bot_core/core_module.py @@ -11,6 +11,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.events.core_on_ready_event import CoreOnReadyEvent from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_core.service.client_utils_service import ClientUtilsService +from bot_core.service.config_service import ConfigService from bot_core.service.data_integrity_service import DataIntegrityService from bot_core.service.message_service import MessageService @@ -23,6 +24,7 @@ class CoreModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(ConfigService) services.add_transient(MessageServiceABC, MessageService) services.add_transient(ClientUtilsABC, ClientUtilsService) services.add_transient(DataIntegrityService) diff --git a/kdb-bot/src/bot_core/service/config_service.py b/kdb-bot/src/bot_core/service/config_service.py new file mode 100644 index 00000000..28c94373 --- /dev/null +++ b/kdb-bot/src/bot_core/service/config_service.py @@ -0,0 +1,30 @@ +from cpl_core.configuration import ConfigurationABC + +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC +from bot_data.model.server import Server +from bot_data.model.technician_config import TechnicianConfig + + +class ConfigService: + def __init__( + self, + config: ConfigurationABC, + technician_config_repo: TechnicianConfigRepositoryABC, + server_config_repo: ServerConfigRepositoryABC, + ): + self._config = config + self._technician_config_repo = technician_config_repo + self._server_config_repo = server_config_repo + + def reload_technician_config(self): + technician_config = self._technician_config_repo.get_technician_config() + self._config.add_configuration(TechnicianConfig, technician_config) + self._config.add_configuration(FeatureFlagsSettings, FeatureFlagsSettings(**technician_config.feature_flags)) + + def reload_server_config(self, server: Server): + server_config = self._server_config_repo.get_server_config_by_server(server.id) + self._config.add_configuration( + f"{type(server_config).__name__}_{server_config.server.discord_id}", server_config + ) diff --git a/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py b/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py new file mode 100644 index 00000000..18a583e6 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/config_feature_flags_migration.py @@ -0,0 +1,33 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class ConfigFeatureFlagsMigration(MigrationABC): + name = "1.1.0_ConfigFeatureFlagsMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + self._cursor.execute( + str( + """ALTER TABLE CFG_Technician ADD FeatureFlags JSON NULL DEFAULT JSON_OBJECT() AFTER CacheMaxMessages;""" + ) + ) + + self._cursor.execute( + str( + """ALTER TABLE CFG_Server ADD FeatureFlags JSON NULL DEFAULT JSON_OBJECT() AFTER LoginMessageChannelId;""" + ) + ) + + def downgrade(self): + self._logger.debug(__name__, "Running downgrade") + self._cursor.execute("ALTER TABLE CFG_Technician DROP COLUMN FeatureFlags;") + self._cursor.execute("ALTER TABLE CFG_Server DROP COLUMN FeatureFlags;") diff --git a/kdb-bot/src/bot_data/migration/config_migration.py b/kdb-bot/src/bot_data/migration/config_migration.py index 0370cbb4..556b396f 100644 --- a/kdb-bot/src/bot_data/migration/config_migration.py +++ b/kdb-bot/src/bot_data/migration/config_migration.py @@ -143,6 +143,8 @@ class ConfigMigration(MigrationABC): def downgrade(self): self._logger.debug(__name__, "Running downgrade") + self._server_downgrade() + self._technician_downgrade() def _server_downgrade(self): self._cursor.execute("DROP TABLE `CFG_Server`;") diff --git a/kdb-bot/src/bot_data/model/server_config.py b/kdb-bot/src/bot_data/model/server_config.py index ba1db9f6..64c666d1 100644 --- a/kdb-bot/src/bot_data/model/server_config.py +++ b/kdb-bot/src/bot_data/model/server_config.py @@ -1,9 +1,11 @@ +import json from datetime import datetime from cpl_core.configuration import ConfigurationModelABC from cpl_core.database import TableABC from cpl_query.extension import List +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.model.server import Server from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig @@ -24,6 +26,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): help_voice_channel_id: int, team_channel_id: int, login_message_channel_id: int, + feature_flags: dict[FeatureFlagsEnum], server: Server, afk_channel_ids: List[int], team_role_ids: List[ServerTeamRoleIdsConfig], @@ -45,6 +48,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): self._help_voice_channel_id = help_voice_channel_id self._team_channel_id = team_channel_id self._login_message_channel_id = login_message_channel_id + self._feature_flags = feature_flags self._server = server self._afk_channel_ids = afk_channel_ids self._team_role_ids = team_role_ids @@ -161,6 +165,14 @@ class ServerConfig(TableABC, ConfigurationModelABC): def login_message_channel_id(self, value: int): self._login_message_channel_id = value + @property + def feature_flags(self) -> dict[FeatureFlagsEnum]: + return self._feature_flags + + @feature_flags.setter + def feature_flags(self, value: dict[FeatureFlagsEnum]): + self._feature_flags = value + @property def afk_channel_ids(self) -> List[int]: return self._afk_channel_ids @@ -225,6 +237,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): `HelpVoiceChannelId`, `TeamChannelId`, `LoginMessageChannelId`, + `FeatureFlags`, `ServerId` ) VALUES ( {self._message_delete_timer}, @@ -240,6 +253,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): {self._help_voice_channel_id}, {self._team_channel_id}, {self._login_message_channel_id}, + '{json.dumps(self._feature_flags)}', {self._server.id} ); """ @@ -263,6 +277,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): `HelpVoiceChannelId` = {self._help_voice_channel_id}, `TeamChannelId` = {self._team_channel_id}, `LoginMessageChannelId` = {self._login_message_channel_id}, + `FeatureFlags` = '{json.dumps(self._feature_flags)}', `ServerId` = {self._server.id} WHERE `Id` = {self._id}; """ diff --git a/kdb-bot/src/bot_data/model/server_config_history.py b/kdb-bot/src/bot_data/model/server_config_history.py index d96e8473..a816e069 100644 --- a/kdb-bot/src/bot_data/model/server_config_history.py +++ b/kdb-bot/src/bot_data/model/server_config_history.py @@ -17,6 +17,7 @@ class ServerConfigHistory(HistoryTableABC): help_voice_channel_id: int, team_channel_id: int, login_message_channel_id: int, + feature_flags: dict[str], server_id: int, deleted: bool, date_from: str, @@ -39,6 +40,7 @@ class ServerConfigHistory(HistoryTableABC): self._help_voice_channel_id = help_voice_channel_id self._team_channel_id = team_channel_id self._login_message_channel_id = login_message_channel_id + self._feature_flags = feature_flags self._server_id = server_id self._deleted = deleted @@ -97,6 +99,10 @@ class ServerConfigHistory(HistoryTableABC): def login_message_channel_id(self) -> int: return self._login_message_channel_id + @property + def feature_flags(self) -> dict[str]: + return self._feature_flags + @property def server_id(self) -> int: return self._server_id diff --git a/kdb-bot/src/bot_data/model/technician_config.py b/kdb-bot/src/bot_data/model/technician_config.py index db43a82c..1390f4b8 100644 --- a/kdb-bot/src/bot_data/model/technician_config.py +++ b/kdb-bot/src/bot_data/model/technician_config.py @@ -1,9 +1,12 @@ +import json from datetime import datetime from cpl_core.configuration import ConfigurationModelABC from cpl_core.database import TableABC from cpl_query.extension import List +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum + class TechnicianConfig(TableABC, ConfigurationModelABC): def __init__( @@ -12,6 +15,7 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): wait_for_restart: int, wait_for_shutdown: int, cache_max_messages: int, + feature_flags: dict[FeatureFlagsEnum], technician_ids: List[int], ping_urls: List[str], created_at: datetime = None, @@ -23,6 +27,7 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): self._wait_for_restart = wait_for_restart self._wait_for_shutdown = wait_for_shutdown self._cache_max_messages = cache_max_messages + self._feature_flags = feature_flags self._technician_ids = technician_ids self._ping_urls = ping_urls @@ -66,6 +71,14 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): def cache_max_messages(self, value: int): self._cache_max_messages = value + @property + def feature_flags(self) -> dict[FeatureFlagsEnum]: + return self._feature_flags + + @feature_flags.setter + def feature_flags(self, value: dict[FeatureFlagsEnum]): + self._feature_flags = value + @property def technician_ids(self) -> List[int]: return self._technician_ids @@ -104,12 +117,13 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): return str( f""" INSERT INTO `CFG_Technician` ( - `HelpCommandReferenceUrl`, `WaitForRestart`, `WaitForShutdown`, `CacheMaxMessages` + `HelpCommandReferenceUrl`, `WaitForRestart`, `WaitForShutdown`, `CacheMaxMessages`, `FeatureFlags` ) VALUES ( '{self._help_command_reference_url}', {self._wait_for_restart}, {self._wait_for_shutdown}, - {self._cache_max_messages} + {self._cache_max_messages}, + '{json.dumps(self._feature_flags)}' ); """ ) @@ -122,7 +136,8 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): SET `HelpCommandReferenceUrl` = '{self._help_command_reference_url}', `WaitForRestart` = {self._wait_for_restart}, `WaitForShutdown` = {self._wait_for_shutdown}, - `CacheMaxMessages` = {self._cache_max_messages} + `CacheMaxMessages` = {self._cache_max_messages}, + `FeatureFlags` = '{json.dumps(self._feature_flags)}' WHERE `Id` = {self._id}; """ ) diff --git a/kdb-bot/src/bot_data/service/server_config_repository_service.py b/kdb-bot/src/bot_data/service/server_config_repository_service.py index 09c1bff5..2f4879f9 100644 --- a/kdb-bot/src/bot_data/service/server_config_repository_service.py +++ b/kdb-bot/src/bot_data/service/server_config_repository_service.py @@ -1,3 +1,5 @@ +import json + from cpl_core.database.context import DatabaseContextABC from cpl_query.extension import List @@ -62,11 +64,12 @@ class ServerConfigRepositoryService(ServerConfigRepositoryABC): result[11], result[12], result[13], - self._servers.get_server_by_id(result[14]), - self._get_afk_channel_ids(result[14]), - self._get_team_role_ids(result[14]), - result[15], + json.loads(result[14]), + self._servers.get_server_by_id(result[15]), + self._get_afk_channel_ids(result[15]), + self._get_team_role_ids(result[15]), result[16], + result[17], id=result[0], ) diff --git a/kdb-bot/src/bot_data/service/server_config_seeder.py b/kdb-bot/src/bot_data/service/server_config_seeder.py index c80f3997..926c09b4 100644 --- a/kdb-bot/src/bot_data/service/server_config_seeder.py +++ b/kdb-bot/src/bot_data/service/server_config_seeder.py @@ -48,6 +48,7 @@ class ServerConfigSeeder(DataSeederABC): guild.system_channel.id, guild.system_channel.id, guild.system_channel.id, + {}, server, [], [], diff --git a/kdb-bot/src/bot_data/service/technician_config_repository_service.py b/kdb-bot/src/bot_data/service/technician_config_repository_service.py index 3e1354cb..fe32eded 100644 --- a/kdb-bot/src/bot_data/service/technician_config_repository_service.py +++ b/kdb-bot/src/bot_data/service/technician_config_repository_service.py @@ -1,3 +1,5 @@ +import json + from cpl_core.database.context import DatabaseContextABC from cpl_query.extension import List @@ -41,10 +43,11 @@ class TechnicianConfigRepositoryService(TechnicianConfigRepositoryABC): result[2], result[3], result[4], + json.loads(result[5]), self._get_technician_ids(), self._get_technician_ping_urls(), - result[5], result[6], + result[7], id=result[0], ) diff --git a/kdb-bot/src/bot_data/service/technician_config_seeder.py b/kdb-bot/src/bot_data/service/technician_config_seeder.py index ce2be05d..209b3e02 100644 --- a/kdb-bot/src/bot_data/service/technician_config_seeder.py +++ b/kdb-bot/src/bot_data/service/technician_config_seeder.py @@ -32,6 +32,7 @@ class TechnicianConfigSeeder(DataSeederABC): 8, 8, 1000000, + {}, List(int, [240160344557879316]), List(str, ["www.google.com", "www.sh-edraft.de", "www.keksdose-gaming.de"]), ) diff --git a/kdb-bot/src/bot_graphql/abc/query_abc.py b/kdb-bot/src/bot_graphql/abc/query_abc.py index a0435218..22db1896 100644 --- a/kdb-bot/src/bot_graphql/abc/query_abc.py +++ b/kdb-bot/src/bot_graphql/abc/query_abc.py @@ -9,6 +9,7 @@ from cpl_query.extension import List from bot_api.exception.service_error_code_enum import ServiceErrorCode from bot_api.exception.service_exception import ServiceException from bot_api.route.route import Route +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_user import AuthUser from bot_data.model.auto_role import AutoRole @@ -17,6 +18,7 @@ from bot_data.model.client import Client from bot_data.model.known_user import KnownUser from bot_data.model.level import Level from bot_data.model.server import Server +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User from bot_data.model.user_joined_game_server import UserJoinedGameServer from bot_data.model.user_joined_server import UserJoinedServer @@ -66,8 +68,12 @@ class QueryABC(ObjectType): self.set_field(f"{name}s", wrapper) self.set_field(f"{name}Count", lambda *args: wrapper(*args).count()) - @ServiceProviderABC.inject - def _can_user_see_element(self, user: AuthUser, element: T, services: ServiceProviderABC) -> bool: + def _can_user_see_element(self, user: AuthUser, element: T) -> bool: + @ServiceProviderABC.inject + def get_services(services: ServiceProviderABC) -> ServiceProviderABC: + return services + + services = get_services() permissions: PermissionService = services.get_service(PermissionService) bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) @@ -158,6 +164,24 @@ class QueryABC(ObjectType): access = True break + elif type(element) == ServerConfig: + for u in user.users: + u: User = u + guild = bot.get_guild(u.server.discord_id) + member = guild.get_member(u.discord_id) + if permissions.is_member_technician(member): + access = True + break + + elif type(element) == dict and "key" in element and element["key"] in [e.value for e in FeatureFlagsEnum]: + for u in user.users: + u: User = u + guild = bot.get_guild(u.server.discord_id) + member = guild.get_member(u.discord_id) + if permissions.is_member_technician(member): + access = True + break + return access @ServiceProviderABC.inject diff --git a/kdb-bot/src/bot_graphql/model/featureFlags.gql b/kdb-bot/src/bot_graphql/model/featureFlags.gql new file mode 100644 index 00000000..1eed1031 --- /dev/null +++ b/kdb-bot/src/bot_graphql/model/featureFlags.gql @@ -0,0 +1,9 @@ +type FeatureFlag { + key: String + value: Boolean +} + +input FeatureFlagInput { + key: String + value: Boolean +} \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/query.gql b/kdb-bot/src/bot_graphql/model/query.gql index abb3048a..8c8a9de1 100644 --- a/kdb-bot/src/bot_graphql/model/query.gql +++ b/kdb-bot/src/bot_graphql/model/query.gql @@ -40,4 +40,5 @@ type Query { technicianConfig: TechnicianConfig guilds(filter: GuildFilter): [Guild] + possibleFeatureFlags: [String] } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/model/serverConfig.gql b/kdb-bot/src/bot_graphql/model/serverConfig.gql index b16fa139..1734114a 100644 --- a/kdb-bot/src/bot_graphql/model/serverConfig.gql +++ b/kdb-bot/src/bot_graphql/model/serverConfig.gql @@ -13,6 +13,8 @@ type ServerConfig implements TableWithHistoryQuery { helpVoiceChannelId: String teamChannelId: String loginMessageChannelId: String + featureFlagCount: Int + featureFlags: [FeatureFlag] afkChannelIds: [String] moderatorRoleIds: [String] @@ -41,6 +43,8 @@ type ServerConfigHistory implements HistoryTableQuery { helpVoiceChannelId: String teamChannelId: String loginMessageChannelId: String + featureFlagCount: Int + featureFlags: [FeatureFlag] serverId: ID @@ -87,6 +91,7 @@ input ServerConfigInput { helpVoiceChannelId: String teamChannelId: String loginMessageChannelId: String + featureFlags: [FeatureFlagInput] afkChannelIds: [String] moderatorRoleIds: [String] diff --git a/kdb-bot/src/bot_graphql/model/technicianConfig.gql b/kdb-bot/src/bot_graphql/model/technicianConfig.gql index dc35704a..dd91ceab 100644 --- a/kdb-bot/src/bot_graphql/model/technicianConfig.gql +++ b/kdb-bot/src/bot_graphql/model/technicianConfig.gql @@ -4,6 +4,8 @@ type TechnicianConfig implements TableWithHistoryQuery { waitForRestart: Int waitForShutdown: Int cacheMaxMessages: Int + featureFlagCount: Int + featureFlags: [FeatureFlag] pingURLs: [String] technicianIds: [String] @@ -21,6 +23,8 @@ type TechnicianConfigHistory implements HistoryTableQuery { waitForRestart: Int waitForShutdown: Int cacheMaxMessages: Int + featureFlagCount: Int + featureFlags: [FeatureFlag] deleted: Boolean dateFrom: String @@ -55,6 +59,7 @@ input TechnicianConfigInput { waitForRestart: Int waitForShutdown: Int cacheMaxMessages: Int + featureFlags: [FeatureFlagInput] pingURLs: [String] technicianIds: [String] } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py index 2480be82..25fe1f18 100644 --- a/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py @@ -3,6 +3,8 @@ from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route +from bot_core.service.config_service import ConfigService from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.server_afk_channel_ids_config import ServerAFKChannelIdsConfig @@ -11,6 +13,7 @@ from bot_data.model.server_team_role_ids_config import ServerTeamRoleIdsConfig from bot_data.model.team_member_type_enum import TeamMemberTypeEnum from bot_data.model.user_role_enum import UserRoleEnum from bot_graphql.abc.query_abc import QueryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC class ServerConfigMutation(QueryABC): @@ -21,6 +24,8 @@ class ServerConfigMutation(QueryABC): servers: ServerRepositoryABC, server_configs: ServerConfigRepositoryABC, db: DatabaseContextABC, + config_service: ConfigService, + permissions: PermissionServiceABC, ): QueryABC.__init__(self, "ServerConfigMutation") @@ -29,6 +34,8 @@ class ServerConfigMutation(QueryABC): self._servers = servers self._server_configs = server_configs self._db = db + self._config_service = config_service + self._permissions = permissions self.set_field("updateServerConfig", self.resolve_update_server_config) @@ -37,7 +44,7 @@ class ServerConfigMutation(QueryABC): raise ValueError("Id not set") server_config = self._server_configs.get_server_config_by_server(input["id"]) - self._can_user_mutate_data(server_config, UserRoleEnum.admin) + self._can_user_mutate_data(Route.get_user().users[0].server, UserRoleEnum.technician) server_config.message_delete_timer = ( input["messageDeleteTimer"] if "messageDeleteTimer" in input else server_config.message_delete_timer @@ -82,6 +89,11 @@ class ServerConfigMutation(QueryABC): if "loginMessageChannelId" in input else server_config.login_message_channel_id ) + server_config.feature_flags = ( + dict(zip([x["key"] for x in input["featureFlags"]], [x["value"] for x in input["featureFlags"]])) + if "featureFlags" in input + else server_config.feature_flags + ) server_config.afk_channel_ids = ( List(int).extend([int(x) for x in input["afkChannelIds"]]) if "afkChannelIds" in input @@ -161,3 +173,6 @@ class ServerConfigMutation(QueryABC): continue self._server_configs.add_server_team_role_id_config(role_id) + + self._config_service.reload_server_config(new_config.server) + self._permissions.on_ready() diff --git a/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py b/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py index da3b7651..f3e3148b 100644 --- a/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/technician_config_mutation.py @@ -3,6 +3,8 @@ from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route +from bot_core.service.config_service import ConfigService from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC from bot_data.model.technician_config import TechnicianConfig @@ -10,6 +12,7 @@ from bot_data.model.technician_id_config import TechnicianIdConfig from bot_data.model.technician_ping_url_config import TechnicianPingUrlConfig from bot_data.model.user_role_enum import UserRoleEnum from bot_graphql.abc.query_abc import QueryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC class TechnicianConfigMutation(QueryABC): @@ -20,6 +23,8 @@ class TechnicianConfigMutation(QueryABC): servers: ServerRepositoryABC, technician_configs: TechnicianConfigRepositoryABC, db: DatabaseContextABC, + permissions: PermissionServiceABC, + config_service: ConfigService, ): QueryABC.__init__(self, "TechnicianConfigMutation") @@ -28,12 +33,14 @@ class TechnicianConfigMutation(QueryABC): self._servers = servers self._technician_configs = technician_configs self._db = db + self._permissions = permissions + self._config_service = config_service self.set_field("updateTechnicianConfig", self.resolve_update_technician_config) def resolve_update_technician_config(self, *_, input: dict): technician_config = self._technician_configs.get_technician_config() - self._can_user_mutate_data(technician_config, UserRoleEnum.admin) + self._can_user_mutate_data(Route.get_user().users[0].server, UserRoleEnum.technician) technician_config.help_command_reference_url = ( input["helpCommandReferenceUrl"] @@ -49,6 +56,11 @@ class TechnicianConfigMutation(QueryABC): technician_config.cache_max_messages = ( input["cacheMaxMessages"] if "cacheMaxMessages" in input else technician_config.cache_max_messages ) + technician_config.feature_flags = ( + dict(zip([x["key"] for x in input["featureFlags"]], [x["value"] for x in input["featureFlags"]])) + if "featureFlags" in input + else technician_config.feature_flags + ) technician_config.ping_urls = ( List(str, input["pingURLs"]) if "pingURLs" in input else technician_config.ping_urls ) @@ -100,3 +112,6 @@ class TechnicianConfigMutation(QueryABC): continue self._technician_configs.add_technician_id_config(TechnicianIdConfig(technician_id)) + + self._config_service.reload_technician_config() + self._permissions.on_ready() diff --git a/kdb-bot/src/bot_graphql/queries/server_config_query.py b/kdb-bot/src/bot_graphql/queries/server_config_query.py index 8672c4d2..73e61828 100644 --- a/kdb-bot/src/bot_graphql/queries/server_config_query.py +++ b/kdb-bot/src/bot_graphql/queries/server_config_query.py @@ -1,4 +1,5 @@ from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List from bot_data.model.server_config_history import ServerConfigHistory from bot_data.model.team_member_type_enum import TeamMemberTypeEnum @@ -23,6 +24,10 @@ class ServerConfigQuery(DataQueryWithHistoryABC): self.set_field("helpVoiceChannelId", lambda config, *_: config.help_voice_channel_id) self.set_field("teamChannelId", lambda config, *_: config.team_channel_id) self.set_field("loginMessageChannelId", lambda config, *_: config.login_message_channel_id) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) self.set_field("afkChannelIds", lambda config, *_: config.afk_channel_ids) self.set_field( "moderatorRoleIds", diff --git a/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py b/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py index 8d945fe0..a63f88f8 100644 --- a/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py +++ b/kdb-bot/src/bot_graphql/queries/technician_config_history_query.py @@ -1,3 +1,5 @@ +from cpl_query.extension import List + from bot_graphql.abc.history_query_abc import HistoryQueryABC @@ -9,3 +11,7 @@ class TechnicianConfigHistoryQuery(HistoryQueryABC): self.set_field("waitForRestart", lambda config, *_: config.wait_for_restart) self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) diff --git a/kdb-bot/src/bot_graphql/queries/technician_config_query.py b/kdb-bot/src/bot_graphql/queries/technician_config_query.py index 3cda423f..2d8bb8f9 100644 --- a/kdb-bot/src/bot_graphql/queries/technician_config_query.py +++ b/kdb-bot/src/bot_graphql/queries/technician_config_query.py @@ -16,6 +16,10 @@ class TechnicianConfigQuery(DataQueryWithHistoryABC): self.set_field("waitForRestart", lambda config, *_: config.wait_for_restart) self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) + self.add_collection( + "featureFlag", + lambda config, *_: List(any, [{"key": x, "value": config.feature_flags[x]} for x in config.feature_flags]), + ) self.set_field("pingURLs", lambda config, *_: config.ping_urls) self.set_field("technicianIds", lambda config, *_: config.technician_ids) diff --git a/kdb-bot/src/bot_graphql/query.py b/kdb-bot/src/bot_graphql/query.py index 030d7bdd..7ec97aee 100644 --- a/kdb-bot/src/bot_graphql/query.py +++ b/kdb-bot/src/bot_graphql/query.py @@ -1,5 +1,6 @@ from cpl_discord.service import DiscordBotServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC @@ -75,6 +76,7 @@ class Query(QueryABC): 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()) + self.set_field("possibleFeatureFlags", lambda x, *_: [e.value for e in FeatureFlagsEnum]) def _resolve_guilds(self, *_, filter=None): if filter is None or "id" not in filter: diff --git a/kdb-bot/src/modules/achievements/achievements_module.py b/kdb-bot/src/modules/achievements/achievements_module.py index dc59af6c..cfe1ce2b 100644 --- a/kdb-bot/src/modules/achievements/achievements_module.py +++ b/kdb-bot/src/modules/achievements/achievements_module.py @@ -14,7 +14,7 @@ from modules.achievements.events.achievement_on_message_event import Achievement class AchievementsModule(ModuleABC): def __init__(self, dc: DiscordCollectionABC): - ModuleABC.__init__(self, dc, FeatureFlagsEnum.auto_role_module) + ModuleABC.__init__(self, dc, FeatureFlagsEnum.achievements_module) def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): pass diff --git a/kdb-bot/src/modules/achievements/commands/achievements_group.py b/kdb-bot/src/modules/achievements/commands/achievements_group.py index 189b8dae..a0c73ccb 100644 --- a/kdb-bot/src/modules/achievements/commands/achievements_group.py +++ b/kdb-bot/src/modules/achievements/commands/achievements_group.py @@ -1,4 +1,5 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe @@ -6,16 +7,20 @@ from discord.ext import commands from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.achievements.achievement_service import AchievementService class AchievementGroup(DiscordCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, message_service: MessageServiceABC, bot: DiscordBotServiceABC, @@ -26,6 +31,7 @@ class AchievementGroup(DiscordCommandABC): ): DiscordCommandABC.__init__(self) + self._config = config self._logger = logger self._message_service = message_service self._bot = bot @@ -45,6 +51,14 @@ class AchievementGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def check(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f"Received command achievement check {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return server = self._servers.get_server_by_discord_id(member.guild.id) user = self._users.get_user_by_discord_id_and_server_id(member.id, server.id) diff --git a/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py b/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py index 5b45cc5d..091e041b 100644 --- a/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py +++ b/kdb-bot/src/modules/achievements/events/achievement_on_message_event.py @@ -1,18 +1,23 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnMessageABC from cpl_discord.service import DiscordBotServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.achievements.achievement_service import AchievementService class AchievementOnMessageEvent(OnMessageABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, achievements: AchievementService, @@ -22,6 +27,7 @@ class AchievementOnMessageEvent(OnMessageABC): ): OnMessageABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._achievements = achievements @@ -31,9 +37,18 @@ class AchievementOnMessageEvent(OnMessageABC): @EventChecks.check_is_ready() async def on_message(self, message: discord.Message): + if message.guild is None: + return + if message.author.bot: return + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.achievements_module + ): + return + server = self._servers.get_server_by_discord_id(message.guild.id) user = self._users.get_user_by_discord_id_and_server_id(message.author.id, server.id) diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 3969aea9..7cf36c5d 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -1,6 +1,7 @@ from typing import List as TList import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC @@ -12,18 +13,22 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role_rule import AutoRoleRule +from bot_data.model.server_config import ServerConfig from modules.permission.abc.permission_service_abc import PermissionServiceABC class AutoRoleGroup(DiscordCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, message_service: MessageServiceABC, bot: DiscordBotServiceABC, @@ -36,6 +41,7 @@ class AutoRoleGroup(DiscordCommandABC): ): DiscordCommandABC.__init__(self) + self._config = config self._logger = logger self._message_service = message_service self._bot = bot @@ -69,10 +75,13 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f"Received command auto-role list {ctx}") - if ctx.guild is None: return + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return + embed = discord.Embed( title=self._t.transform("modules.auto_role.list.title"), description=self._t.transform("modules.auto_role.list.description"), @@ -110,6 +119,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, channel: discord.TextChannel, message_id: str): self._logger.debug(__name__, f"Received command auto-role add {ctx} {message_id}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return message = ( List( @@ -170,6 +185,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role: int): self._logger.debug(__name__, f"Received command auto-role remove {ctx} {auto_role}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return auto_role_from_db = self._auto_roles.find_auto_role_by_id(auto_role) if auto_role_from_db is None: @@ -210,6 +231,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, auto_role: int, wait: int = None): self._logger.debug(__name__, f"Received command auto-role rule list {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return embed = discord.Embed( title=self._t.transform("modules.auto_role.list.title"), @@ -262,6 +289,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, auto_role: int, emoji_name: str, role_id: str): self._logger.debug(__name__, f"Received command auto-role add {ctx} {auto_role}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return emoji = discord.utils.get(self._bot.emojis, name=emoji_name) if emoji is None: @@ -354,6 +387,12 @@ class AutoRoleGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role_rule: int): self._logger.debug(__name__, f"Received command auto-role remove {ctx} {auto_role_rule}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return auto_role_from_db = self._auto_roles.get_auto_role_rule_by_id(auto_role_rule) if auto_role_from_db is None: diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index 72566c40..f5d14804 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -1,17 +1,22 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, @@ -20,6 +25,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): ): OnRawReactionAddABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._servers = servers @@ -29,6 +35,9 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): @EventChecks.check_is_ready() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return await self._reaction_handler.handle(payload, "add") diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index 394c9578..a174c8f5 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -1,17 +1,22 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.server_config import ServerConfig from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, @@ -20,6 +25,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): ): OnRawReactionRemoveABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._servers = servers @@ -29,6 +35,9 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @EventChecks.check_is_ready() async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.auto_role_module): + return await self._reaction_handler.handle(payload, "remove") diff --git a/kdb-bot/src/modules/base/events/base_on_command_event.py b/kdb-bot/src/modules/base/events/base_on_command_event.py index 4f33abd9..407a875f 100644 --- a/kdb-bot/src/modules/base/events/base_on_command_event.py +++ b/kdb-bot/src/modules/base/events/base_on_command_event.py @@ -1,6 +1,7 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.time import TimeFormatSettings from cpl_discord.events import OnCommandABC @@ -13,12 +14,14 @@ from bot_core.logging.command_logger import CommandLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User class BaseOnCommandEvent(OnCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, bot: DiscordBotServiceABC, messenger: MessageServiceABC, @@ -30,6 +33,7 @@ class BaseOnCommandEvent(OnCommandABC): servers: ServerRepositoryABC, ): OnCommandABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._messenger = messenger diff --git a/kdb-bot/src/modules/base/events/base_on_message_delete_event.py b/kdb-bot/src/modules/base/events/base_on_message_delete_event.py index afd065a8..3d3aad1c 100644 --- a/kdb-bot/src/modules/base/events/base_on_message_delete_event.py +++ b/kdb-bot/src/modules/base/events/base_on_message_delete_event.py @@ -1,6 +1,7 @@ from typing import Optional import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnMessageDeleteABC from cpl_discord.service import DiscordBotServiceABC @@ -10,12 +11,14 @@ from bot_core.logging.message_logger import MessageLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server_config import ServerConfig from bot_data.model.user import User class BaseOnMessageDeleteEvent(OnMessageDeleteABC): def __init__( self, + config: ConfigurationABC, logger: MessageLogger, db: DatabaseContextABC, bot: DiscordBotServiceABC, @@ -24,6 +27,7 @@ class BaseOnMessageDeleteEvent(OnMessageDeleteABC): servers: ServerRepositoryABC, ): OnMessageDeleteABC.__init__(self) + self._config = config self._logger = logger self._db = db self._bot = bot diff --git a/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py b/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py index 20810163..fd42102f 100644 --- a/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py +++ b/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py @@ -8,6 +8,8 @@ from cpl_translation import TranslatePipe from discord import guild from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_data.model.server_config import ServerConfig @@ -60,6 +62,11 @@ class BootLogOnReadyEvent(OnReadyABC): self._logger.error(__name__, f"Config {type(self).__name__}_{g.id} not found!") return + if not FeatureFlagsSettings.get_flag_from_dict( + server_config.feature_flags, FeatureFlagsEnum.boot_log_module + ): + continue + self._bot.loop.create_task( self._message_service.send_channel_message( self._bot.get_channel(server_config.login_message_channel_id), diff --git a/kdb-bot/src/modules/config/config_extension.py b/kdb-bot/src/modules/config/config_extension.py index 56f383e3..ce164180 100644 --- a/kdb-bot/src/modules/config/config_extension.py +++ b/kdb-bot/src/modules/config/config_extension.py @@ -5,8 +5,7 @@ from cpl_core.logging import LoggerABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings -from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC -from bot_data.model.technician_config import TechnicianConfig +from bot_core.service.config_service import ConfigService class ConfigExtension(ApplicationExtensionABC): @@ -19,6 +18,5 @@ class ConfigExtension(ApplicationExtensionABC): return logger: LoggerABC = services.get_service(LoggerABC) logger.debug(__name__, "Config extension started") - technician_config_repo: TechnicianConfigRepositoryABC = services.get_service(TechnicianConfigRepositoryABC) - technician_config = technician_config_repo.get_technician_config() - config.add_configuration(TechnicianConfig, technician_config) + config: ConfigService = services.get_service(ConfigService) + config.reload_technician_config() diff --git a/kdb-bot/src/modules/config/events/config_on_ready_event.py b/kdb-bot/src/modules/config/events/config_on_ready_event.py index f9bf3012..40fa3cf9 100644 --- a/kdb-bot/src/modules/config/events/config_on_ready_event.py +++ b/kdb-bot/src/modules/config/events/config_on_ready_event.py @@ -1,10 +1,9 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC -from cpl_discord.container import Guild from cpl_discord.events import OnReadyABC from cpl_discord.service import DiscordBotServiceABC -from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC +from bot_core.service.config_service import ConfigService from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -15,7 +14,7 @@ class ConfigOnReadyEvent(OnReadyABC): logger: LoggerABC, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, - server_config_repo: ServerConfigRepositoryABC, + config_service: ConfigService, ): OnReadyABC.__init__(self) @@ -23,13 +22,8 @@ class ConfigOnReadyEvent(OnReadyABC): self._logger = logger self._bot = bot self._servers = servers - self._server_config_repo = server_config_repo + self._config_service = config_service async def on_ready(self): for guild in self._bot.guilds: - guild: Guild = guild - server = self._servers.get_server_by_discord_id(guild.id) - server_config = self._server_config_repo.get_server_config_by_server(server.id) - self._config.add_configuration( - f"{type(server_config).__name__}_{server_config.server.discord_id}", server_config - ) + self._config_service.reload_server_config(self._servers.get_server_by_discord_id(guild.id)) diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 1e7c4ae5..50e03692 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -1,6 +1,7 @@ from typing import List as TList import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC from cpl_discord.container import Guild, Role @@ -12,12 +13,15 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_abc import ClientUtilsABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.level import Level +from bot_data.model.server_config import ServerConfig from modules.level.level_seeder import LevelSeeder from modules.level.service.level_service import LevelService from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -26,6 +30,7 @@ from modules.permission.abc.permission_service_abc import PermissionServiceABC class LevelGroup(DiscordCommandABC): def __init__( self, + config: ConfigurationABC, logger: CommandLogger, message_service: MessageServiceABC, bot: DiscordBotServiceABC, @@ -41,6 +46,7 @@ class LevelGroup(DiscordCommandABC): ): DiscordCommandABC.__init__(self) + self._config = config self._logger = logger self._message_service = message_service self._bot = bot @@ -125,10 +131,13 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f"Received command level list {ctx}") - if ctx.guild is None: return + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + server = self._servers.get_server_by_discord_id(ctx.guild.id) levels = self._levels.get_levels_by_server_id(server.id) if levels.count() < 1: @@ -169,6 +178,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_admin() async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f"Received command level create {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return try: color = hex(discord.Colour.from_str(color).value) @@ -258,6 +273,12 @@ class LevelGroup(DiscordCommandABC): permissions: int = None, ): self._logger.debug(__name__, f"Received command level edit {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = ( @@ -350,6 +371,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_admin() async def remove(self, ctx: Context, level: str): self._logger.debug(__name__, f"Received command level remove {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = ( @@ -394,6 +421,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def down(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f"Received command level down {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -436,6 +469,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def up(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f"Received command level up {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -477,6 +516,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def set(self, ctx: Context, member: discord.Member, level: str): self._logger.debug(__name__, f"Received command level up {ctx} {member}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return if member.bot: return @@ -529,5 +574,12 @@ class LevelGroup(DiscordCommandABC): @CommandChecks.check_is_member_moderator() async def reload(self, ctx: Context): self._logger.debug(__name__, f"Received command level reload {ctx}") + if ctx.guild is None: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{ctx.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._seed_levels(ctx) self._logger.trace(__name__, f"Finished command level reload") diff --git a/kdb-bot/src/modules/level/events/level_on_member_join_event.py b/kdb-bot/src/modules/level/events/level_on_member_join_event.py index 26a9dccb..7113f761 100644 --- a/kdb-bot/src/modules/level/events/level_on_member_join_event.py +++ b/kdb-bot/src/modules/level/events/level_on_member_join_event.py @@ -1,18 +1,27 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_discord.events import OnMemberJoinABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_core.logging.message_logger import MessageLogger +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnMemberJoinEvent(OnMemberJoinABC): - def __init__(self, logger: MessageLogger, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: MessageLogger, level: LevelService): OnMemberJoinABC.__init__(self) + self._config = config self._logger = logger self._level = level @EventChecks.check_is_ready() async def on_member_join(self, member: discord.Member): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._level.check_level(member) diff --git a/kdb-bot/src/modules/level/events/level_on_message_event.py b/kdb-bot/src/modules/level/events/level_on_message_event.py index 8bdd054c..4c98706f 100644 --- a/kdb-bot/src/modules/level/events/level_on_message_event.py +++ b/kdb-bot/src/modules/level/events/level_on_message_event.py @@ -1,20 +1,35 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_discord.events import OnMessageABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks from bot_core.logging.message_logger import MessageLogger +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnMessageEvent(OnMessageABC): - def __init__(self, logger: MessageLogger, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: MessageLogger, level: LevelService): OnMessageABC.__init__(self) + self._config = config self._logger = logger self._level = level @EventChecks.check_is_ready() async def on_message(self, message: discord.Message): self._logger.debug(__name__, f"Module {type(self)} started") + if message.guild is None: + return + + if message.author.bot: + return + + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{message.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: await self._level.check_level(message.author) except Exception as e: diff --git a/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py b/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py index 5535b0e7..bffc56ef 100644 --- a/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py +++ b/kdb-bot/src/modules/level/events/level_on_raw_reaction_add_event.py @@ -1,21 +1,27 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnRawReactionAddEvent(OnRawReactionAddABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, level: LevelService, ): OnRawReactionAddABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._level = level @@ -23,6 +29,10 @@ class LevelOnRawReactionAddEvent(OnRawReactionAddABC): @EventChecks.check_is_ready() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: self._logger.trace(__name__, f"Handle reaction {payload} for level") diff --git a/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py b/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py index 3e4c32d7..fa94e334 100644 --- a/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py +++ b/kdb-bot/src/modules/level/events/level_on_raw_reaction_remove_event.py @@ -1,21 +1,27 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnRawReactionRemoveEvent(OnRawReactionRemoveABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, level: LevelService, ): OnRawReactionRemoveABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._level = level @@ -23,6 +29,10 @@ class LevelOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @EventChecks.check_is_ready() async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{payload.guild_id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + try: self._logger.trace(__name__, f"Handle reaction {payload} for level") diff --git a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py index d5c51f95..9a0b5978 100644 --- a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py @@ -1,14 +1,19 @@ import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnVoiceStateUpdateABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.event_checks import EventChecks +from bot_data.model.server_config import ServerConfig from modules.level.service.level_service import LevelService class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): - def __init__(self, logger: LoggerABC, level: LevelService): + def __init__(self, config: ConfigurationABC, logger: LoggerABC, level: LevelService): OnVoiceStateUpdateABC.__init__(self) + self._config = config self._logger = logger self._level = level @@ -22,4 +27,8 @@ class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): after: discord.VoiceState, ): self._logger.debug(__name__, f"Module {type(self)} started") + server_config: ServerConfig = self._config.get_configuration(f"ServerConfig_{member.guild.id}") + if not FeatureFlagsSettings.get_flag_from_dict(server_config.feature_flags, FeatureFlagsEnum.level_module): + return + await self._level.check_level(member) diff --git a/kdb-web/package.json b/kdb-web/package.json index 998a4411..24908690 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -1,6 +1,6 @@ { "name": "kdb-web", - "version": "1.1.dev79_hide_table_columns", + "version": "1.1.dev334_feature_flags_in_wi", "scripts": { "ng": "ng", "update-version": "ts-node-esm update-version.ts", diff --git a/kdb-web/src/app/base/component-with-table.ts b/kdb-web/src/app/base/component-with-table.ts index ae39c2b3..b8550644 100644 --- a/kdb-web/src/app/base/component-with-table.ts +++ b/kdb-web/src/app/base/component-with-table.ts @@ -26,7 +26,12 @@ export class ComponentWithTable { this.columns = columns.map(column => { return { key: this.getKey(column), name: column }; }); - this._hiddenColumns = JSON.parse(localStorage.getItem("hiddenColumns") ?? ""); + let hiddenColumns = localStorage.getItem("hiddenColumns"); + if (!hiddenColumns) { + localStorage.setItem("hiddenColumns", JSON.stringify([{}])); + hiddenColumns = localStorage.getItem("hiddenColumns") ?? JSON.stringify([{}]); + } + this._hiddenColumns = JSON.parse(hiddenColumns); } private getKey(column: string): string { diff --git a/kdb-web/src/app/models/config/feature-flags.model.ts b/kdb-web/src/app/models/config/feature-flags.model.ts new file mode 100644 index 00000000..010843a3 --- /dev/null +++ b/kdb-web/src/app/models/config/feature-flags.model.ts @@ -0,0 +1,4 @@ +export interface FeatureFlag { + key: string; + value: boolean; +} diff --git a/kdb-web/src/app/models/config/server-config.model.ts b/kdb-web/src/app/models/config/server-config.model.ts index b55c4136..c2b1d1c8 100644 --- a/kdb-web/src/app/models/config/server-config.model.ts +++ b/kdb-web/src/app/models/config/server-config.model.ts @@ -1,4 +1,5 @@ import { DataWithHistory } from "../data/data.model"; +import { FeatureFlag } from "./feature-flags.model"; export interface ServerConfig extends DataWithHistory { id?: number; @@ -15,6 +16,7 @@ export interface ServerConfig extends DataWithHistory { helpVoiceChannelId?: string; teamChannelId?: string; loginMessageChannelId?: string; + featureFlags: FeatureFlag[]; afkChannelIds: string[]; moderatorRoleIds: string[]; adminRoleIds: string[]; diff --git a/kdb-web/src/app/models/config/technician-config.model.ts b/kdb-web/src/app/models/config/technician-config.model.ts index 6e1430f0..e70ae752 100644 --- a/kdb-web/src/app/models/config/technician-config.model.ts +++ b/kdb-web/src/app/models/config/technician-config.model.ts @@ -1,4 +1,5 @@ import { DataWithHistory } from "../data/data.model"; +import { FeatureFlag } from "./feature-flags.model"; export interface TechnicianConfig extends DataWithHistory { id?: number; @@ -6,6 +7,7 @@ export interface TechnicianConfig extends DataWithHistory { waitForRestart?: number; waitForShutdown?: number; cacheMaxMessages?: number; + featureFlags: FeatureFlag[]; pingURLs: string[]; technicianIds: string[]; } diff --git a/kdb-web/src/app/models/graphql/mutations.model.ts b/kdb-web/src/app/models/graphql/mutations.model.ts index 7905eff4..20263626 100644 --- a/kdb-web/src/app/models/graphql/mutations.model.ts +++ b/kdb-web/src/app/models/graphql/mutations.model.ts @@ -167,7 +167,7 @@ export class Mutations { `; static updateTechnicianConfig = ` - mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $pingURLs: [String], $technicianIds: [String]) { + mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $featureFlags: [FeatureFlagInput], $pingURLs: [String], $technicianIds: [String]) { technicianConfig { updateTechnicianConfig(input: { id: $id, @@ -175,6 +175,7 @@ export class Mutations { waitForRestart: $waitForRestart, waitForShutdown: $waitForShutdown, cacheMaxMessages: $cacheMaxMessages, + featureFlags: $featureFlags, pingURLs: $pingURLs, technicianIds: $technicianIds }) { @@ -183,6 +184,10 @@ export class Mutations { waitForRestart waitForShutdown cacheMaxMessages + featureFlags { + key + value + } pingURLs technicianIds } @@ -206,6 +211,7 @@ export class Mutations { $helpVoiceChannelId: String, $teamChannelId: String, $loginMessageChannelId: String, + $featureFlags: [FeatureFlagInput], $afkChannelIds: [String], $moderatorRoleIds: [String], $adminRoleIds: [String] @@ -213,21 +219,22 @@ export class Mutations { serverConfig { updateServerConfig(input: { id: $id, - messageDeleteTimer: $messageDeleteTimer - notificationChatId: $notificationChatId - maxVoiceStateHours: $maxVoiceStateHours - xpPerMessage: $xpPerMessage - xpPerReaction: $xpPerReaction - maxMessageXpPerHour: $maxMessageXpPerHour - xpPerOntimeHour: $xpPerOntimeHour - xpPerEventParticipation: $xpPerEventParticipation - xpPerAchievement: $xpPerAchievement - afkCommandChannelId: $afkCommandChannelId - helpVoiceChannelId: $helpVoiceChannelId - teamChannelId: $teamChannelId - loginMessageChannelId: $loginMessageChannelId - afkChannelIds: $afkChannelIds - moderatorRoleIds: $moderatorRoleIds + messageDeleteTimer: $messageDeleteTimer, + notificationChatId: $notificationChatId, + maxVoiceStateHours: $maxVoiceStateHours, + xpPerMessage: $xpPerMessage, + xpPerReaction: $xpPerReaction, + maxMessageXpPerHour: $maxMessageXpPerHour, + xpPerOntimeHour: $xpPerOntimeHour, + xpPerEventParticipation: $xpPerEventParticipation, + xpPerAchievement: $xpPerAchievement, + afkCommandChannelId: $afkCommandChannelId, + helpVoiceChannelId: $helpVoiceChannelId, + teamChannelId: $teamChannelId, + loginMessageChannelId: $loginMessageChannelId, + featureFlags: $featureFlags, + afkChannelIds: $afkChannelIds, + moderatorRoleIds: $moderatorRoleIds, adminRoleIds: $adminRoleIds }) { id @@ -244,6 +251,10 @@ export class Mutations { helpVoiceChannelId teamChannelId loginMessageChannelId + featureFlags { + key + value + } afkChannelIds moderatorRoleIds adminRoleIds diff --git a/kdb-web/src/app/models/graphql/queries.model.ts b/kdb-web/src/app/models/graphql/queries.model.ts index 4f6856b4..7e7e212f 100644 --- a/kdb-web/src/app/models/graphql/queries.model.ts +++ b/kdb-web/src/app/models/graphql/queries.model.ts @@ -353,6 +353,10 @@ export class Queries { waitForRestart waitForShutdown cacheMaxMessages + featureFlags { + key + value + } pingURLs technicianIds @@ -381,6 +385,10 @@ export class Queries { helpVoiceChannelId teamChannelId loginMessageChannelId + featureFlags { + key + value + } afkChannelIds moderatorRoleIds adminRoleIds diff --git a/kdb-web/src/app/models/graphql/query.model.ts b/kdb-web/src/app/models/graphql/query.model.ts index 42eeb69d..12b5ddaa 100644 --- a/kdb-web/src/app/models/graphql/query.model.ts +++ b/kdb-web/src/app/models/graphql/query.model.ts @@ -59,3 +59,8 @@ export interface AutoRoleRuleQuery { autoRoleRules: AutoRoleRule[]; } + +export interface PossibleFeatureFlagsQuery { + possibleFeatureFlags: string[]; +} + diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html index b7e68a1b..7887819d 100644 --- a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html @@ -163,6 +163,7 @@
+
+
+ + + + + + + + + + + {{value.value.key}} + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+ + + + +
diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.scss b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts new file mode 100644 index 00000000..4151e4d6 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FeatureFlagListComponent } from './feature-flag-list.component'; + +describe('FeatureFlagListComponent', () => { + let component: FeatureFlagListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FeatureFlagListComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FeatureFlagListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts new file mode 100644 index 00000000..141ed1a4 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/feature-flag-list/feature-flag-list.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from "@angular/core"; +import { ConfigListComponent } from "../config-list/config-list.component"; +import { Table } from "primeng/table"; +import { PossibleFeatureFlagsQuery } from "../../../../models/graphql/query.model"; +import { DataService } from "../../../../services/data/data.service"; + +@Component({ + selector: "app-feature-flag-list", + templateUrl: "./feature-flag-list.component.html", + styleUrls: ["./feature-flag-list.component.scss"] +}) +export class FeatureFlagListComponent extends ConfigListComponent implements OnInit { + featureFlags: string[] = []; + + + constructor( + private dataService: DataService + ) { + super(); + } + + ngOnInit() { + this.dataService.query("{possibleFeatureFlags}" + ).subscribe(data => { + this.featureFlags = data.possibleFeatureFlags; + }); + } + + override addNew(table: Table) { + const id = Math.max.apply(Math, this.internal_data.map(value => { + return value.id ?? 0; + })) + 1; + const newItem = { id: id, value: { key: "", value: false } }; + this.internal_data.push(newItem); + + table.initRowEdit(newItem); + const index = this.internal_data.findIndex(l => l.id == newItem.id); + this.editInit(newItem, index); + } +} diff --git a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts index deb1be40..4711dad9 100644 --- a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts +++ b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts @@ -28,8 +28,11 @@ export class AuthGuard implements CanActivate { const role = route.data['role']; if (role) { - this.authService.hasUserPermission(role).then(hasPermission => { - if (!hasPermission) { + this.authService.hasUserPermission(role).then(async hasPermission => { + let authUser = await this.authService.getLoggedInUser(); + let isTechnician = authUser?.users?.map(u => u.isTechnician).filter(u => u) ?? []; + + if (!hasPermission && !isTechnician) { this.router.navigate(['/dashboard']); return false; } diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts index ae7d2ebd..9cea8e7f 100644 --- a/kdb-web/src/app/modules/shared/shared.module.ts +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -30,6 +30,8 @@ import { MultiSelectModule } from "primeng/multiselect"; import { HideableColumnComponent } from './components/hideable-column/hideable-column.component'; import { HideableHeaderComponent } from './components/hideable-header/hideable-header.component'; import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-select-columns.component'; +import { FeatureFlagListComponent } from './components/feature-flag-list/feature-flag-list.component'; +import { InputSwitchModule } from "primeng/inputswitch"; @NgModule({ @@ -42,6 +44,7 @@ import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-s HideableColumnComponent, HideableHeaderComponent, MultiSelectColumnsComponent, + FeatureFlagListComponent, ], imports: [ CommonModule, @@ -68,6 +71,7 @@ import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-s SidebarModule, DataViewModule, MultiSelectModule, + InputSwitchModule, ], exports: [ ButtonModule, @@ -102,6 +106,8 @@ import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-s HideableColumnComponent, HideableHeaderComponent, MultiSelectColumnsComponent, + FeatureFlagListComponent, + InputSwitchModule, ] }) export class SharedModule { diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.html b/kdb-web/src/app/modules/view/server/config/components/config/config.component.html index 9f7701c1..405dbe26 100644 --- a/kdb-web/src/app/modules/view/server/config/components/config/config.component.html +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.html @@ -113,6 +113,7 @@ +