diff --git a/bot/src/bot/application.py b/bot/src/bot/application.py index 6d1e4dd9..aff7b618 100644 --- a/bot/src/bot/application.py +++ b/bot/src/bot/application.py @@ -11,6 +11,7 @@ from bot_api.api_thread import ApiThread from bot_core.abc.task_abc import TaskABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.environment_variables import MAINTENANCE from bot_core.service.data_integrity_service import DataIntegrityService @@ -58,8 +59,9 @@ class Application(DiscordBotApplicationABC): return self._logger.info(__name__, f"Try to start {DiscordBotService.__name__}") - for task in self._tasks: - await self._bot.add_cog(task) + if not self._config.get_configuration(MAINTENANCE): + for task in self._tasks: + await self._bot.add_cog(task) await self._bot.start_async() await self._bot.stop_async() diff --git a/bot/src/bot/startup_settings_extension.py b/bot/src/bot/startup_settings_extension.py index ea5cfbca..c3db171a 100644 --- a/bot/src/bot/startup_settings_extension.py +++ b/bot/src/bot/startup_settings_extension.py @@ -8,6 +8,7 @@ from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC from bot_core.configuration.bot_logging_settings import BotLoggingSettings +from bot_core.environment_variables import MAINTENANCE, MIGRATION_ONLY class StartupSettingsExtension(StartupExtensionABC): @@ -19,6 +20,12 @@ class StartupSettingsExtension(StartupExtensionABC): environment.set_working_directory(os.path.dirname(os.path.realpath(__file__))) configuration.add_environment_variables("SDB_") configuration.add_environment_variables("DISCORD_") + configuration.add_configuration( + MAINTENANCE, configuration.get_configuration(MAINTENANCE) in [True, "true", "True"] + ) + configuration.add_configuration( + MIGRATION_ONLY, configuration.get_configuration(MIGRATION_ONLY) in [True, "true", "True"] + ) configuration.add_json_file(f"config/appsettings.json", optional=False) configuration.add_json_file(f"config/appsettings.{environment.environment_name}.json", optional=True) diff --git a/bot/src/bot/translation/de.json b/bot/src/bot/translation/de.json index 9f0a57fa..45f64e14 100644 --- a/bot/src/bot/translation/de.json +++ b/bot/src/bot/translation/de.json @@ -90,7 +90,8 @@ "booting": "Ich fahre gerade hoch...", "restart": "Muss neue Kekse holen...", "running": "Ich esse Kekse :D", - "shutdown": "Ich werde bestimmt wieder kommen..." + "shutdown": "Ich werde bestimmt wieder kommen...", + "maintenance": "In Wartung!" } }, "modules": { diff --git a/bot/src/bot_core/abc/client_utils_abc.py b/bot/src/bot_core/abc/client_utils_abc.py index fdcf5118..66e25500 100644 --- a/bot/src/bot_core/abc/client_utils_abc.py +++ b/bot/src/bot_core/abc/client_utils_abc.py @@ -75,3 +75,7 @@ class ClientUtilsABC(ABC): @abstractmethod async def check_default_role(self, member: Union[discord.User, discord.Member]): pass + + @abstractmethod + async def set_maintenance_mode(self, state: bool): + pass diff --git a/bot/src/bot_core/abc/task_abc.py b/bot/src/bot_core/abc/task_abc.py index d068fd37..5f578a99 100644 --- a/bot/src/bot_core/abc/task_abc.py +++ b/bot/src/bot_core/abc/task_abc.py @@ -6,6 +6,7 @@ from cpl_core.dependency_injection import ServiceProviderABC from cpl_discord.service import DiscordBotServiceABC from discord.ext import commands +from bot_core.environment_variables import MAINTENANCE from bot_core.logging.task_logger import TaskLogger @@ -14,14 +15,18 @@ class TaskABC(commands.Cog): def __init__(self): commands.Cog.__init__(self) + @ServiceProviderABC.inject + def _is_maintenance(self, config: ConfigurationABC) -> bool: + return config.get_configuration(MAINTENANCE) is True + @ServiceProviderABC.inject async def _wait_until_ready(self, config: ConfigurationABC, logger: TaskLogger, bot: DiscordBotServiceABC): logger.debug(__name__, f"Waiting before {type(self).__name__}") await bot.wait_until_ready() async def wait(): - is_ready = config.get_configuration("IS_READY") - if is_ready != "true": + is_ready = config.get_configuration("IS_READY") is True + if not is_ready: await asyncio.sleep(1) await wait() diff --git a/bot/src/bot_core/core_extension/core_extension_on_ready_event.py b/bot/src/bot_core/core_extension/core_extension_on_ready_event.py index 78992473..277cf566 100644 --- a/bot/src/bot_core/core_extension/core_extension_on_ready_event.py +++ b/bot/src/bot_core/core_extension/core_extension_on_ready_event.py @@ -1,16 +1,19 @@ import asyncio +from cpl_core.configuration import ConfigurationABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnReadyABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe from bot_core.abc.client_utils_abc import ClientUtilsABC +from bot_core.environment_variables import MAINTENANCE class CoreExtensionOnReadyEvent(OnReadyABC): def __init__( self, + config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, client_utils: ClientUtilsABC, @@ -18,6 +21,7 @@ class CoreExtensionOnReadyEvent(OnReadyABC): ): OnReadyABC.__init__(self) + self._config = config self._logger = logger self._bot = bot self._client_utils = client_utils @@ -27,5 +31,5 @@ class CoreExtensionOnReadyEvent(OnReadyABC): async def on_ready(self): self._logger.debug(__name__, f"Module {type(self)} started") - await self._client_utils.presence_game("common.presence.running") + await self._client_utils.set_maintenance_mode(self._config.get_configuration(MAINTENANCE)) self._logger.trace(__name__, f"Module {type(self)} stopped") diff --git a/bot/src/bot_core/environment_variables.py b/bot/src/bot_core/environment_variables.py new file mode 100644 index 00000000..74a9dc01 --- /dev/null +++ b/bot/src/bot_core/environment_variables.py @@ -0,0 +1,2 @@ +MIGRATION_ONLY = "MIGRATION_ONLY" +MAINTENANCE = "MAINTENANCE" diff --git a/bot/src/bot_core/helper/event_checks.py b/bot/src/bot_core/helper/event_checks.py index 5639a3da..e94f10cf 100644 --- a/bot/src/bot_core/helper/event_checks.py +++ b/bot/src/bot_core/helper/event_checks.py @@ -1,3 +1,4 @@ +import inspect from typing import Optional from discord.ext import commands @@ -17,11 +18,18 @@ class EventChecks: cls._client_utils = client_utils @classmethod - def check_is_ready(cls): - async def check_if_bot_is_ready() -> bool: + def check_is_ready(cls, func): + async def check_if_bot_is_ready(*args, **kwargs): result = await cls._client_utils.check_if_bot_is_ready_yet() if not result: - raise CheckError(f"Bot is not ready") - return result - return commands.check(check_if_bot_is_ready) + def empty(*args, **kwargs): + return + + return empty + return await func(*args, **kwargs) + + check_if_bot_is_ready.__name__ = func.__name__ + sig = inspect.signature(func) + check_if_bot_is_ready.__signature__ = sig.replace(parameters=tuple(sig.parameters.values())[1:]) + return check_if_bot_is_ready diff --git a/bot/src/bot_core/service/client_utils_service.py b/bot/src/bot_core/service/client_utils_service.py index e0272390..e316bf03 100644 --- a/bot/src/bot_core/service/client_utils_service.py +++ b/bot/src/bot_core/service/client_utils_service.py @@ -16,6 +16,7 @@ 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.environment_variables import MAINTENANCE from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_voice_channel_repository_abc import ( @@ -87,7 +88,14 @@ class ClientUtilsService(ClientUtilsABC): return client async def check_if_bot_is_ready_yet(self) -> bool: - if self._config.get_configuration("IS_READY") == "true": + if self._config.get_configuration(MAINTENANCE): + self._logger.warn( + __name__, + f"Bot is in maintenance mode", + ) + return False + + if self._config.get_configuration("IS_READY") is True: return True self._logger.debug( @@ -239,3 +247,10 @@ class ClientUtilsService(ClientUtilsABC): except Exception as e: self._logger.error(__name__, f"Cannot check for default role for member {member.id}", e) + + async def set_maintenance_mode(self, state: bool): + self._config.add_configuration(MAINTENANCE, state) + if state: + await self.presence_game("common.presence.maintenance") + else: + await self.presence_game("common.presence.running") diff --git a/bot/src/bot_data/migration/db_history_scripts/config/technician.sql b/bot/src/bot_data/migration/db_history_scripts/config/technician.sql index 1df922ee..3866f88f 100644 --- a/bot/src/bot_data/migration/db_history_scripts/config/technician.sql +++ b/bot/src/bot_data/migration/db_history_scripts/config/technician.sql @@ -6,6 +6,7 @@ CREATE TABLE IF NOT EXISTS `CFG_TechnicianHistory` `WaitForShutdown` BIGINT NOT NULL DEFAULT 8, `CacheMaxMessages` BIGINT NOT NULL DEFAULT 1000000, `MaxSteamOfferCount` BIGINT NOT NULL DEFAULT 250, + `Maintenance` BOOLEAN DEFAULT FALSE, `FeatureFlags` JSON NULL DEFAULT ('{}'), `Deleted` BOOL DEFAULT FALSE, `DateFrom` DATETIME(6) NOT NULL, @@ -25,6 +26,7 @@ BEGIN `WaitForShutdown`, `CacheMaxMessages`, `MaxSteamOfferCount`, + `Maintenance`, `FeatureFlags`, `DateFrom`, `DateTo`) @@ -34,6 +36,7 @@ BEGIN OLD.WaitForShutdown, OLD.CacheMaxMessages, OLD.MaxSteamOfferCount, + OLD.Maintenance, OLD.FeatureFlags, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6)); @@ -52,6 +55,7 @@ BEGIN `WaitForShutdown`, `CacheMaxMessages`, `MaxSteamOfferCount`, + `Maintenance`, `FeatureFlags`, `Deleted`, `DateFrom`, @@ -62,6 +66,7 @@ BEGIN OLD.WaitForShutdown, OLD.CacheMaxMessages, OLD.MaxSteamOfferCount, + OLD.Maintenance, OLD.FeatureFlags, TRUE, OLD.LastModifiedAt, diff --git a/bot/src/bot_data/migration/maintenance_mode_migration.py b/bot/src/bot_data/migration/maintenance_mode_migration.py new file mode 100644 index 00000000..467c8604 --- /dev/null +++ b/bot/src/bot_data/migration/maintenance_mode_migration.py @@ -0,0 +1,51 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class MaintenanceModeMigration(MigrationABC): + name = "1.2.0_MaintenanceModeMigration" + + 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""" + ALTER TABLE CFG_Technician + ADD Maintenance BOOLEAN DEFAULT FALSE AFTER MaxSteamOfferCount; + """ + ) + ) + + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_TechnicianHistory + ADD Maintenance BOOLEAN DEFAULT FALSE AFTER MaxSteamOfferCount; + """ + ) + ) + self._exec(__file__, "config/technician.sql") + + def downgrade(self): + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_Technician DROP COLUMN Maintenance; + """ + ) + ) + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_TechnicianHistory DROP COLUMN Maintenance; + """ + ) + ) diff --git a/bot/src/bot_data/model/technician_config.py b/bot/src/bot_data/model/technician_config.py index 313bbb95..e3c4f5f3 100644 --- a/bot/src/bot_data/model/technician_config.py +++ b/bot/src/bot_data/model/technician_config.py @@ -16,6 +16,7 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): wait_for_shutdown: int, cache_max_messages: int, max_steam_offer_count: int, + maintenance: bool, feature_flags: dict[FeatureFlagsEnum], technician_ids: List[int], ping_urls: List[str], @@ -29,6 +30,8 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): self._wait_for_shutdown = wait_for_shutdown self._cache_max_messages = cache_max_messages self._max_steam_offer_count = max_steam_offer_count + self._maintenance = maintenance + self._feature_flags = feature_flags self._technician_ids = technician_ids self._ping_urls = ping_urls @@ -105,6 +108,14 @@ class TechnicianConfig(TableABC, ConfigurationModelABC): def ping_urls(self, value: List[str]): self._ping_urls = value + @property + def maintenance(self) -> bool: + return self._maintenance + + @maintenance.setter + def maintenance(self, value: bool): + self._maintenance = value + @staticmethod def get_select_all_string() -> str: return str( diff --git a/bot/src/bot_data/service/technician_config_repository_service.py b/bot/src/bot_data/service/technician_config_repository_service.py index eb9293f7..f5c27306 100644 --- a/bot/src/bot_data/service/technician_config_repository_service.py +++ b/bot/src/bot_data/service/technician_config_repository_service.py @@ -47,11 +47,12 @@ class TechnicianConfigRepositoryService(TechnicianConfigRepositoryABC): result[3], result[4], result[5], - json.loads(result[6]), + bool(result[6]), + json.loads(result[7]), self._get_technician_ids(), self._get_technician_ping_urls(), - result[7], result[8], + result[9], id=result[0], ) diff --git a/bot/src/bot_data/startup_migration_extension.py b/bot/src/bot_data/startup_migration_extension.py index 8780d94f..14a90938 100644 --- a/bot/src/bot_data/startup_migration_extension.py +++ b/bot/src/bot_data/startup_migration_extension.py @@ -20,6 +20,7 @@ from bot_data.migration.fix_updates_migration import FixUpdatesMigration from bot_data.migration.fix_user_history_migration import FixUserHistoryMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.level_migration import LevelMigration +from bot_data.migration.maintenance_mode_migration import MaintenanceModeMigration from bot_data.migration.max_steam_offer_count_migration import MaxSteamOfferCountMigration from bot_data.migration.remove_stats_migration import RemoveStatsMigration from bot_data.migration.short_role_name_migration import ShortRoleNameMigration @@ -70,3 +71,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, BirthdayMigration) # 10.10.2023 #401 - 1.2.0 services.add_transient(MigrationABC, SteamSpecialOfferMigration) # 10.10.2023 #188 - 1.2.0 services.add_transient(MigrationABC, MaxSteamOfferCountMigration) # 04.11.2023 #188 - 1.2.0 + services.add_transient(MigrationABC, MaintenanceModeMigration) # 06.11.2023 #424 - 1.2.0 diff --git a/bot/src/bot_graphql/abc/query_abc.py b/bot/src/bot_graphql/abc/query_abc.py index 850fb22b..506428fc 100644 --- a/bot/src/bot_graphql/abc/query_abc.py +++ b/bot/src/bot_graphql/abc/query_abc.py @@ -1,6 +1,7 @@ from typing import Callable from ariadne import ObjectType +from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.type import T from cpl_discord.service import DiscordBotServiceABC @@ -10,6 +11,7 @@ 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_core.environment_variables import MAINTENANCE from bot_data.model.achievement import Achievement from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_user import AuthUser @@ -75,9 +77,14 @@ class QueryABC(ObjectType): def get_services(services: ServiceProviderABC) -> ServiceProviderABC: return services + @ServiceProviderABC.inject + def get_config(config: ConfigurationABC) -> ConfigurationABC: + return config + services = get_services() permissions: PermissionService = services.get_service(PermissionService) bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) + config = get_config() if user.auth_role == AuthRoleEnum.admin: return True @@ -87,6 +94,9 @@ class QueryABC(ObjectType): if permissions.is_member_technician(guild.get_member(u.discord_id)): return True + if config.get_configuration(MAINTENANCE): + return False + access = False if type(element) == Achievement: element: Achievement = element @@ -215,7 +225,9 @@ class QueryABC(ObjectType): return access @ServiceProviderABC.inject - def _can_user_mutate_data(self, server: Server, permission: UserRoleEnum, services: ServiceProviderABC): + def _can_user_mutate_data( + self, server: Server, permission: UserRoleEnum, services: ServiceProviderABC, config: ConfigurationABC + ): permissions: PermissionService = services.get_service(PermissionService) bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) @@ -227,16 +239,20 @@ class QueryABC(ObjectType): auth_user.users.where(lambda x: x.server.id == server.id).single().discord_id ) - check_perm = lambda x: True + can_edit = lambda x: False match permission: case UserRoleEnum.moderator: - check_perm = lambda x: permissions.is_member_moderator(x) + can_edit = permissions.is_member_moderator + if config.get_configuration(MAINTENANCE): + can_edit = lambda x: False case UserRoleEnum.admin: - check_perm = lambda x: permissions.is_member_admin(x) + can_edit = permissions.is_member_admin + if config.get_configuration(MAINTENANCE): + can_edit = lambda x: False case UserRoleEnum.technician: - check_perm = lambda x: permissions.is_member_technician(x) + can_edit = permissions.is_member_technician - if not check_perm(member): + if not can_edit(member): ex = ServiceException(ServiceErrorCode.Forbidden, f"User not allowed to mutate data") raise ex diff --git a/bot/src/bot_graphql/graphql/technicianConfig.gql b/bot/src/bot_graphql/graphql/technicianConfig.gql index 1c97490a..63cc1fb8 100644 --- a/bot/src/bot_graphql/graphql/technicianConfig.gql +++ b/bot/src/bot_graphql/graphql/technicianConfig.gql @@ -5,6 +5,7 @@ type TechnicianConfig implements TableWithHistoryQuery { waitForShutdown: Int cacheMaxMessages: Int maxSteamOfferCount: Int + maintenance: Boolean featureFlagCount: Int featureFlags: [FeatureFlag] pingURLs: [String] @@ -25,6 +26,7 @@ type TechnicianConfigHistory implements HistoryTableQuery { waitForShutdown: Int cacheMaxMessages: Int maxSteamOfferCount: Int + maintenance: Boolean featureFlagCount: Int featureFlags: [FeatureFlag] @@ -62,6 +64,7 @@ input TechnicianConfigInput { waitForShutdown: Int cacheMaxMessages: Int maxSteamOfferCount: Int + maintenance: Boolean featureFlags: [FeatureFlagInput] pingURLs: [String] technicianIds: [String] diff --git a/bot/src/bot_graphql/mutations/technician_config_mutation.py b/bot/src/bot_graphql/mutations/technician_config_mutation.py index 28e947f9..67091955 100644 --- a/bot/src/bot_graphql/mutations/technician_config_mutation.py +++ b/bot/src/bot_graphql/mutations/technician_config_mutation.py @@ -1,9 +1,12 @@ +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from bot_api.logging.api_logger import ApiLogger from bot_api.route.route import Route +from bot_core.abc.client_utils_abc import ClientUtilsABC +from bot_core.environment_variables import MAINTENANCE 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 @@ -18,6 +21,7 @@ from bot_graphql.abc.query_abc import QueryABC class TechnicianConfigMutation(QueryABC): def __init__( self, + config: ConfigurationABC, logger: ApiLogger, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, @@ -25,9 +29,11 @@ class TechnicianConfigMutation(QueryABC): db: DatabaseContextABC, config_service: ConfigService, tech_seeder: TechnicianConfigSeeder, + client_utils: ClientUtilsABC, ): QueryABC.__init__(self, "TechnicianConfigMutation") + self._config = config self._logger = logger self._bot = bot self._servers = servers @@ -35,6 +41,7 @@ class TechnicianConfigMutation(QueryABC): self._db = db self._config_service = config_service self._tech_seeder = tech_seeder + self._client_utils = client_utils self.set_field("updateTechnicianConfig", self.resolve_update_technician_config) @@ -62,6 +69,9 @@ class TechnicianConfigMutation(QueryABC): technician_config.max_steam_offer_count = ( input["maxSteamOfferCount"] if "maxSteamOfferCount" in input else technician_config.max_steam_offer_count ) + technician_config.maintenance = ( + input["maintenance"] if "maintenance" in input else technician_config.maintenance + ) old_feature_flags = technician_config.feature_flags technician_config.feature_flags = ( dict( @@ -94,6 +104,9 @@ class TechnicianConfigMutation(QueryABC): self._update_technician_ids(technician_config) self._db.save_changes() + if technician_config.maintenance != self._config.get_configuration(MAINTENANCE): + self._bot.loop.create_task(self._client_utils.set_maintenance_mode(technician_config.maintenance)) + self._bot.loop.create_task(self._config_service.reload_technician_config()) return technician_config diff --git a/bot/src/bot_graphql/queries/technician_config_history_query.py b/bot/src/bot_graphql/queries/technician_config_history_query.py index 0db572bb..825acd7b 100644 --- a/bot/src/bot_graphql/queries/technician_config_history_query.py +++ b/bot/src/bot_graphql/queries/technician_config_history_query.py @@ -15,6 +15,7 @@ class TechnicianConfigHistoryQuery(HistoryQueryABC): self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) self.set_field("maxSteamOfferCount", lambda config, *_: config.max_steam_offer_count) + self.set_field("maintenance", lambda config, *_: config.maintenance) self.add_collection( "featureFlag", lambda config, *_: List( diff --git a/bot/src/bot_graphql/queries/technician_config_query.py b/bot/src/bot_graphql/queries/technician_config_query.py index 1fdb2643..4809a60d 100644 --- a/bot/src/bot_graphql/queries/technician_config_query.py +++ b/bot/src/bot_graphql/queries/technician_config_query.py @@ -28,6 +28,7 @@ class TechnicianConfigQuery(DataQueryWithHistoryABC): self.set_field("waitForShutdown", lambda config, *_: config.wait_for_shutdown) self.set_field("cacheMaxMessages", lambda config, *_: config.cache_max_messages) self.set_field("maxSteamOfferCount", lambda config, *_: config.max_steam_offer_count) + self.set_field("maintenance", lambda config, *_: config.maintenance) self.add_collection( "featureFlag", lambda config, *_: List( diff --git a/bot/src/modules/achievements/events/achievement_on_message_event.py b/bot/src/modules/achievements/events/achievement_on_message_event.py index b3564ccf..fa108de3 100644 --- a/bot/src/modules/achievements/events/achievement_on_message_event.py +++ b/bot/src/modules/achievements/events/achievement_on_message_event.py @@ -35,7 +35,7 @@ class AchievementOnMessageEvent(OnMessageABC): self._servers = servers self._users = users - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_message(self, message: discord.Message): if message.guild is None: return diff --git a/bot/src/modules/achievements/events/achievement_on_reaction_add_event.py b/bot/src/modules/achievements/events/achievement_on_reaction_add_event.py index 37959385..1709f132 100644 --- a/bot/src/modules/achievements/events/achievement_on_reaction_add_event.py +++ b/bot/src/modules/achievements/events/achievement_on_reaction_add_event.py @@ -9,6 +9,7 @@ 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 @@ -36,6 +37,7 @@ class AchievementOnReactionAddEvent(OnReactionAddABC): self._servers = servers self._users = users + @EventChecks.check_is_ready async def on_reaction_add( self, reaction: discord.reaction.Reaction, diff --git a/bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py b/bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py index a25e1868..4b639f3e 100644 --- a/bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py +++ b/bot/src/modules/achievements/events/achievement_on_reaction_remove_event.py @@ -9,6 +9,7 @@ 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 @@ -36,6 +37,7 @@ class AchievementOnReactionRemoveEvent(OnReactionRemoveABC): self._servers = servers self._users = users + @EventChecks.check_is_ready async def on_reaction_remove( self, reaction: discord.reaction.Reaction, diff --git a/bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py b/bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py index b28d5f64..57700206 100644 --- a/bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py +++ b/bot/src/modules/achievements/events/achievement_on_voice_state_update_event.py @@ -7,6 +7,7 @@ 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 @@ -34,6 +35,7 @@ class AchievementOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._servers = servers self._users = users + @EventChecks.check_is_ready async def on_voice_state_update( self, member: discord.member.Member, diff --git a/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index f5d14804..61c029cf 100644 --- a/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -32,7 +32,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler - @EventChecks.check_is_ready() + @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}") diff --git a/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index a174c8f5..b26bf5fb 100644 --- a/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -32,7 +32,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler - @EventChecks.check_is_ready() + @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}") diff --git a/bot/src/modules/base/birthday_watcher.py b/bot/src/modules/base/birthday_watcher.py index 7b19638a..50015415 100644 --- a/bot/src/modules/base/birthday_watcher.py +++ b/bot/src/modules/base/birthday_watcher.py @@ -34,7 +34,8 @@ class BirthdayWatcher(TaskABC): self._message_service = message_service self._t = t - self.watch.start() + if not self._is_maintenance(): + self.watch.start() @tasks.loop(time=datetime.time(hour=8, minute=0)) async def watch(self): diff --git a/bot/src/modules/base/events/base_on_command_error_event.py b/bot/src/modules/base/events/base_on_command_error_event.py index d5aab492..d371a2aa 100644 --- a/bot/src/modules/base/events/base_on_command_error_event.py +++ b/bot/src/modules/base/events/base_on_command_error_event.py @@ -11,6 +11,7 @@ from discord.ext.commands import Context, CommandError from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.exception.check_error import CheckError +from bot_core.helper.event_checks import EventChecks from bot_data.model.technician_config import TechnicianConfig @@ -32,6 +33,7 @@ class BaseOnCommandErrorEvent(OnCommandErrorABC): self._time_format_settings = time_format_settings self._t = translate + @EventChecks.check_is_ready async def on_command_error(self, ctx: Context, error: CommandError): if isinstance(error, CheckError): return diff --git a/bot/src/modules/base/events/base_on_command_event.py b/bot/src/modules/base/events/base_on_command_event.py index 407a875f..92f9c89e 100644 --- a/bot/src/modules/base/events/base_on_command_event.py +++ b/bot/src/modules/base/events/base_on_command_event.py @@ -10,6 +10,7 @@ from cpl_translation import TranslatePipe from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.event_checks import EventChecks 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 @@ -78,6 +79,7 @@ class BaseOnCommandEvent(OnCommandABC): self._logger.debug(__name__, f"User {user} sent message. xp: from {old_xp} to {user.xp}") + @EventChecks.check_is_ready async def on_command(self, ctx: Context): self._logger.debug(__name__, f"Module {type(self)} started") self._logger.info(__name__, f"Received command: {ctx.command} from {ctx.channel}") diff --git a/bot/src/modules/base/events/base_on_guild_join_event.py b/bot/src/modules/base/events/base_on_guild_join_event.py index 4a1582de..644fd194 100644 --- a/bot/src/modules/base/events/base_on_guild_join_event.py +++ b/bot/src/modules/base/events/base_on_guild_join_event.py @@ -4,6 +4,7 @@ from cpl_discord.events import OnGuildJoinABC from cpl_discord.service import DiscordBotServiceABC from discord import Guild +from bot_core.helper.event_checks import EventChecks from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.server import Server @@ -29,6 +30,7 @@ class BaseOnGuildJoinEvent(OnGuildJoinABC): self._db = db self._seeder = seeder + @EventChecks.check_is_ready async def on_guild_join(self, guild: Guild): if self._servers.find_server_by_discord_id(guild.id) is None: self._servers.add_server(Server(guild.id)) diff --git a/bot/src/modules/base/events/base_on_member_join_event.py b/bot/src/modules/base/events/base_on_member_join_event.py index b3cd2625..a3e9d456 100644 --- a/bot/src/modules/base/events/base_on_member_join_event.py +++ b/bot/src/modules/base/events/base_on_member_join_event.py @@ -101,7 +101,7 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): ) self._logger.trace(__name__, f"Notified team that a member left") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_member_join(self, member: discord.Member): self._logger.debug(__name__, f"Module {type(self)} started") if member.bot: diff --git a/bot/src/modules/base/events/base_on_member_remove_event.py b/bot/src/modules/base/events/base_on_member_remove_event.py index adfe7e87..c32d9be6 100644 --- a/bot/src/modules/base/events/base_on_member_remove_event.py +++ b/bot/src/modules/base/events/base_on_member_remove_event.py @@ -69,7 +69,7 @@ class BaseOnMemberRemoveEvent(OnMemberRemoveABC): ) self._logger.trace(__name__, f"Notified team that a member left") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_member_remove(self, member: discord.Member): self._logger.debug(__name__, f"Module {type(self)} started") await self._remove_user(member) diff --git a/bot/src/modules/base/events/base_on_message_delete_event.py b/bot/src/modules/base/events/base_on_message_delete_event.py index 3d3aad1c..0dd1fa0d 100644 --- a/bot/src/modules/base/events/base_on_message_delete_event.py +++ b/bot/src/modules/base/events/base_on_message_delete_event.py @@ -6,6 +6,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnMessageDeleteABC from cpl_discord.service import DiscordBotServiceABC +from bot_core.helper.event_checks import EventChecks from bot_core.helper.log_message_helper import LogMessageHelper from bot_core.logging.message_logger import MessageLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC @@ -72,6 +73,7 @@ class BaseOnMessageDeleteEvent(OnMessageDeleteABC): f"Removed message from user {user}. xp: from {old_xp} to {user.xp}", ) + @EventChecks.check_is_ready async def on_message_delete(self, message: discord.Message): self._logger.debug(__name__, f"Module {type(self)} started") if message is None or message.guild is None: diff --git a/bot/src/modules/base/events/base_on_message_event.py b/bot/src/modules/base/events/base_on_message_event.py index e6531385..18ba2bb3 100644 --- a/bot/src/modules/base/events/base_on_message_event.py +++ b/bot/src/modules/base/events/base_on_message_event.py @@ -79,7 +79,7 @@ class BaseOnMessageEvent(OnMessageABC): self._logger.debug(__name__, f"User {user} sent message. xp: from {old_xp} to {user.xp}") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_message(self, message: discord.Message): self._logger.debug(__name__, f"Module {type(self)} started") if message is None or message.guild is None: diff --git a/bot/src/modules/base/events/base_on_raw_reaction_add.py b/bot/src/modules/base/events/base_on_raw_reaction_add.py index 4591959e..72e06099 100644 --- a/bot/src/modules/base/events/base_on_raw_reaction_add.py +++ b/bot/src/modules/base/events/base_on_raw_reaction_add.py @@ -26,7 +26,7 @@ class BaseOnRawReactionAddEvent(OnRawReactionAddABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") diff --git a/bot/src/modules/base/events/base_on_raw_reaction_remove.py b/bot/src/modules/base/events/base_on_raw_reaction_remove.py index 9b32cbb1..273fada1 100644 --- a/bot/src/modules/base/events/base_on_raw_reaction_remove.py +++ b/bot/src/modules/base/events/base_on_raw_reaction_remove.py @@ -26,7 +26,7 @@ class BaseOnRawReactionRemoveEvent(OnRawReactionRemoveABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f"Module {type(self)} started") diff --git a/bot/src/modules/base/events/base_on_scheduled_event_update_event.py b/bot/src/modules/base/events/base_on_scheduled_event_update_event.py index 640a9a80..25cd22fb 100644 --- a/bot/src/modules/base/events/base_on_scheduled_event_update_event.py +++ b/bot/src/modules/base/events/base_on_scheduled_event_update_event.py @@ -4,6 +4,7 @@ from cpl_discord.events.on_scheduled_event_update_abc import OnScheduledEventUpd from cpl_discord.service import DiscordBotServiceABC from discord import EventStatus +from bot_core.helper.event_checks import EventChecks from modules.base.model.active_event import ActiveEvent from modules.base.service.event_service import EventService @@ -21,6 +22,7 @@ class BaseOnScheduledEventUpdateEvent(OnScheduledEventUpdateABC): self._bot = bot self._events = events + @EventChecks.check_is_ready async def on_scheduled_event_update(self, before: discord.ScheduledEvent, after: discord.ScheduledEvent): self._logger.debug(__name__, f"Module {type(self)} started") diff --git a/bot/src/modules/base/events/base_on_voice_state_update_event.py b/bot/src/modules/base/events/base_on_voice_state_update_event.py index 8815530b..7d8a2896 100644 --- a/bot/src/modules/base/events/base_on_voice_state_update_event.py +++ b/bot/src/modules/base/events/base_on_voice_state_update_event.py @@ -83,7 +83,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): except Exception as e: self._logger.error(__name__, f"Ontime validation failed", e) - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_voice_state_update( self, member: discord.Member, diff --git a/bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py b/bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py index 3e1b3da0..545620ac 100644 --- a/bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py +++ b/bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py @@ -42,7 +42,7 @@ class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC): ) self._logger.trace(__name__, f"Notified team that a member need help") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_voice_state_update( self, member: discord.Member, diff --git a/bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py b/bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py index fe5195c9..b18af00b 100644 --- a/bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py +++ b/bot/src/modules/base/events/base_on_voice_state_update_event_scheduled_event_bonus.py @@ -30,7 +30,7 @@ class BaseOnVoiceStateUpdateEventScheduledEventBonus(OnVoiceStateUpdateABC): self._logger.info(__name__, f"Module {type(self)} loaded") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_voice_state_update( self, member: discord.Member, diff --git a/bot/src/modules/boot_log/boot_log_on_ready_event.py b/bot/src/modules/boot_log/boot_log_on_ready_event.py index 4c8aeec7..0c5161fd 100644 --- a/bot/src/modules/boot_log/boot_log_on_ready_event.py +++ b/bot/src/modules/boot_log/boot_log_on_ready_event.py @@ -82,6 +82,6 @@ class BootLogOnReadyEvent(OnReadyABC): ) ) - self._config.add_configuration("IS_READY", "true") + self._config.add_configuration("IS_READY", True) self._logger.info(__name__, "Bot is ready") self._logger.trace(__name__, f"Module {type(self)} stopped") diff --git a/bot/src/modules/database/database_extension.py b/bot/src/modules/database/database_extension.py index 1c26e70b..cdfe28fa 100644 --- a/bot/src/modules/database/database_extension.py +++ b/bot/src/modules/database/database_extension.py @@ -8,6 +8,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_core.environment_variables import MIGRATION_ONLY from bot_core.logging.database_logger import DatabaseLogger from bot_data.service.migration_service import MigrationService @@ -25,6 +26,6 @@ class DatabaseExtension(ApplicationExtensionABC): config.add_configuration("Database_StartTime", str(datetime.now())) migrations: MigrationService = services.get_service(MigrationService) migrations.migrate() - if config.get_configuration("MIGRATION_ONLY"): + if config.get_configuration(MIGRATION_ONLY): logger.warn(__name__, "Migrations finished. Stopping application...") sys.exit() diff --git a/bot/src/modules/level/events/level_on_member_join_event.py b/bot/src/modules/level/events/level_on_member_join_event.py index 7113f761..38326ed3 100644 --- a/bot/src/modules/level/events/level_on_member_join_event.py +++ b/bot/src/modules/level/events/level_on_member_join_event.py @@ -17,7 +17,7 @@ class LevelOnMemberJoinEvent(OnMemberJoinABC): self._logger = logger self._level = level - @EventChecks.check_is_ready() + @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}") diff --git a/bot/src/modules/level/events/level_on_message_event.py b/bot/src/modules/level/events/level_on_message_event.py index 4c98706f..be7d4626 100644 --- a/bot/src/modules/level/events/level_on_message_event.py +++ b/bot/src/modules/level/events/level_on_message_event.py @@ -17,7 +17,7 @@ class LevelOnMessageEvent(OnMessageABC): self._logger = logger self._level = level - @EventChecks.check_is_ready() + @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: diff --git a/bot/src/modules/level/events/level_on_raw_reaction_add_event.py b/bot/src/modules/level/events/level_on_raw_reaction_add_event.py index bffc56ef..341a0a21 100644 --- a/bot/src/modules/level/events/level_on_raw_reaction_add_event.py +++ b/bot/src/modules/level/events/level_on_raw_reaction_add_event.py @@ -26,7 +26,7 @@ class LevelOnRawReactionAddEvent(OnRawReactionAddABC): self._bot = bot self._level = level - @EventChecks.check_is_ready() + @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}") diff --git a/bot/src/modules/level/events/level_on_raw_reaction_remove_event.py b/bot/src/modules/level/events/level_on_raw_reaction_remove_event.py index fa94e334..1c04f19b 100644 --- a/bot/src/modules/level/events/level_on_raw_reaction_remove_event.py +++ b/bot/src/modules/level/events/level_on_raw_reaction_remove_event.py @@ -26,7 +26,7 @@ class LevelOnRawReactionRemoveEvent(OnRawReactionRemoveABC): self._bot = bot self._level = level - @EventChecks.check_is_ready() + @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}") diff --git a/bot/src/modules/level/events/level_on_voice_state_update_event.py b/bot/src/modules/level/events/level_on_voice_state_update_event.py index 9a0b5978..f9bb2bf2 100644 --- a/bot/src/modules/level/events/level_on_voice_state_update_event.py +++ b/bot/src/modules/level/events/level_on_voice_state_update_event.py @@ -19,7 +19,7 @@ class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._logger.info(__name__, f"Module {type(self)} loaded") - @EventChecks.check_is_ready() + @EventChecks.check_is_ready async def on_voice_state_update( self, member: discord.Member, diff --git a/bot/src/modules/short_role_name/events/short_role_name_on_member_update_event.py b/bot/src/modules/short_role_name/events/short_role_name_on_member_update_event.py index 6b942ed1..92b9a08e 100644 --- a/bot/src/modules/short_role_name/events/short_role_name_on_member_update_event.py +++ b/bot/src/modules/short_role_name/events/short_role_name_on_member_update_event.py @@ -1,6 +1,7 @@ import discord from cpl_discord.events import OnMemberUpdateABC +from bot_core.helper.event_checks import EventChecks from modules.short_role_name.service.short_role_name_service import ShortRoleNameService @@ -9,6 +10,7 @@ class ShortRoleNameOnMemberUpdateEvent(OnMemberUpdateABC): OnMemberUpdateABC.__init__(self) self._service = service + @EventChecks.check_is_ready async def on_member_update(self, before: discord.member.Member, after: discord.member.Member): if before.roles != after.roles or before.name != after.name: await self._service.check_short_role_names(after) diff --git a/bot/src/modules/special_offers/steam_offer_watcher.py b/bot/src/modules/special_offers/steam_offer_watcher.py index daeba89b..d2af2b4d 100644 --- a/bot/src/modules/special_offers/steam_offer_watcher.py +++ b/bot/src/modules/special_offers/steam_offer_watcher.py @@ -51,7 +51,8 @@ class SteamOfferWatcher(TaskABC): self._urls = {} self._image_urls = {} - self.watch.start() + if not self._is_maintenance(): + self.watch.start() @staticmethod def _get_max_count() -> int: diff --git a/web/src/app/models/config/technician-config.model.ts b/web/src/app/models/config/technician-config.model.ts index 36be69b6..1f211ee8 100644 --- a/web/src/app/models/config/technician-config.model.ts +++ b/web/src/app/models/config/technician-config.model.ts @@ -8,6 +8,7 @@ export interface TechnicianConfig extends DataWithHistory { waitForShutdown?: number; cacheMaxMessages?: number; maxSteamOfferCount?: number; + maintenance?: boolean; featureFlags: FeatureFlag[]; pingURLs: string[]; technicianIds: string[]; diff --git a/web/src/app/models/graphql/mutations.model.ts b/web/src/app/models/graphql/mutations.model.ts index 9bffdc58..4f52fa01 100644 --- a/web/src/app/models/graphql/mutations.model.ts +++ b/web/src/app/models/graphql/mutations.model.ts @@ -216,7 +216,7 @@ export class Mutations { `; static updateTechnicianConfig = ` - mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $maxSteamOfferCount: Int, $featureFlags: [FeatureFlagInput], $pingURLs: [String], $technicianIds: [String]) { + mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $maxSteamOfferCount: Int, $maintenance: Boolean, $featureFlags: [FeatureFlagInput], $pingURLs: [String], $technicianIds: [String]) { technicianConfig { updateTechnicianConfig(input: { id: $id, @@ -225,6 +225,7 @@ export class Mutations { waitForShutdown: $waitForShutdown, cacheMaxMessages: $cacheMaxMessages, maxSteamOfferCount: $maxSteamOfferCount, + maintenance: $maintenance, featureFlags: $featureFlags, pingURLs: $pingURLs, technicianIds: $technicianIds @@ -235,6 +236,7 @@ export class Mutations { waitForShutdown cacheMaxMessages maxSteamOfferCount + maintenance featureFlags { key value diff --git a/web/src/app/models/graphql/queries.model.ts b/web/src/app/models/graphql/queries.model.ts index 1f6bb6a9..9b8e8059 100644 --- a/web/src/app/models/graphql/queries.model.ts +++ b/web/src/app/models/graphql/queries.model.ts @@ -511,6 +511,7 @@ export class Queries { waitForShutdown cacheMaxMessages maxSteamOfferCount + maintenance featureFlags { key value diff --git a/web/src/app/modules/admin/settings/components/settings/settings.component.html b/web/src/app/modules/admin/settings/components/settings/settings.component.html index c3370289..2e9048ec 100644 --- a/web/src/app/modules/admin/settings/components/settings/settings.component.html +++ b/web/src/app/modules/admin/settings/components/settings/settings.component.html @@ -109,7 +109,8 @@
- +
@@ -136,7 +137,8 @@
{{'admin.settings.bot.help_url' | translate}}:
- +
@@ -145,7 +147,8 @@
{{'admin.settings.bot.wait_for_restart' | translate}}:
- +
@@ -154,7 +157,8 @@
{{'admin.settings.bot.wait_for_shutdown' | translate}}:
- +
@@ -163,7 +167,8 @@
{{'admin.settings.bot.cache_max_messages' | translate}}:
- +
@@ -172,14 +177,23 @@
{{'admin.settings.bot.max_steam_offer_count' | translate}}:
- +
+
+
+
{{'view.server.config.bot.maintenance' | translate}}:
+ +
+
+
- diff --git a/web/src/app/modules/admin/settings/components/settings/settings.component.ts b/web/src/app/modules/admin/settings/components/settings/settings.component.ts index 8b5462f0..428051d2 100644 --- a/web/src/app/modules/admin/settings/components/settings/settings.component.ts +++ b/web/src/app/modules/admin/settings/components/settings/settings.component.ts @@ -56,6 +56,10 @@ export class SettingsComponent implements OnInit { }; possibleTechnicians?: DiscordUser[]; + stateOptions: any[] = [ + { label: this.translate.instant("common.state.off"), value: false }, + { label: this.translate.instant("common.state.on"), value: true } + ]; constructor( private dataService: DataService, @@ -157,6 +161,7 @@ export class SettingsComponent implements OnInit { waitForShutdown: this.config.waitForShutdown, cacheMaxMessages: this.config.cacheMaxMessages, maxSteamOfferCount: this.config.maxSteamOfferCount, + maintenance: this.config.maintenance, featureFlags: this.config.featureFlags, pingURLs: this.config.pingURLs, technicianIds: this.config.technicianIds diff --git a/web/src/app/modules/shared/shared.module.ts b/web/src/app/modules/shared/shared.module.ts index d4ce44bf..b3ca8e60 100644 --- a/web/src/app/modules/shared/shared.module.ts +++ b/web/src/app/modules/shared/shared.module.ts @@ -35,6 +35,7 @@ import { InputSwitchModule } from "primeng/inputswitch"; import { CalendarModule } from "primeng/calendar"; import { DataImportAndExportComponent } from './components/data-import-and-export/data-import-and-export.component'; import { FileUploadModule } from "primeng/fileupload"; +import { SelectButtonModule } from "primeng/selectbutton"; const PrimeNGModules = [ @@ -64,6 +65,7 @@ const PrimeNGModules = [ InputSwitchModule, CalendarModule, FileUploadModule, + SelectButtonModule, ] @NgModule({ diff --git a/web/src/app/modules/view/server/profile/profile.component.ts b/web/src/app/modules/view/server/profile/profile.component.ts index 6625f101..21ae1a88 100644 --- a/web/src/app/modules/view/server/profile/profile.component.ts +++ b/web/src/app/modules/view/server/profile/profile.component.ts @@ -18,6 +18,7 @@ import { Mutations } from "../../../../models/graphql/mutations.model"; import { MenuItem } from "primeng/api"; import { UserWarning } from "../../../../models/data/user_warning.model"; import moment from "moment"; +import { SidebarService } from "../../../../services/sidebar/sidebar.service"; @Component({ selector: "app-profile", @@ -34,6 +35,7 @@ export class ProfileComponent implements OnInit, OnDestroy { public isEditingNewUserWarning: boolean = false; public isEditing: boolean = false; public isModerator: boolean = false; + public hasTechnicianAccess: boolean = false; private unsubscriber = new Subject(); @@ -45,7 +47,8 @@ export class ProfileComponent implements OnInit, OnDestroy { private auth: AuthService, private toast: ToastService, private translate: TranslateService, - private toastService: ToastService + private toastService: ToastService, + private sidebarService: SidebarService, ) { } @@ -78,7 +81,8 @@ export class ProfileComponent implements OnInit, OnDestroy { let authUser = await this.auth.getLoggedInUser(); this.spinner.showSpinner(); let user: UserDTO | null = authUser?.users?.find(u => u.server == server.id) ?? null; - if (!user || user?.id != params["memberId"] && !user?.isModerator) { + this.hasTechnicianAccess = (this.sidebarService.hasFeature("TechnicianFullAccess") && user?.isTechnician) ?? false; + if (!user || user?.id != params["memberId"] && !user?.isModerator && !this.hasTechnicianAccess) { this.toast.error(this.translate.instant("view.server.profile.permission_denied"), this.translate.instant("view.server.profile.permission_denied_d")); this.spinner.hideSpinner(); await this.router.navigate(["/server", server.id]); diff --git a/web/src/assets/i18n/de.json b/web/src/assets/i18n/de.json index 471a0893..e8a6e082 100644 --- a/web/src/assets/i18n/de.json +++ b/web/src/assets/i18n/de.json @@ -200,6 +200,10 @@ "role": "Rolle", "rule_count": "Regeln", "save": "Speichern", + "state": { + "off": "Aus", + "on": "Ein" + }, "user_warnings": "Verwarnungen", "users": "Benutzer", "value": "Wert", @@ -433,6 +437,7 @@ "header": "Bot Konfiguration", "help_voice_channel_id": "Sprachkanal für Hilfsbenachrichtung", "login_message_channel_id": "Kanal für die Nachricht vom Bot nach Start", + "maintenance": "Wartungsmodus", "max_message_xp_per_hour": "Maximale XP pro Stunde durch Nachrichten", "max_voice_state_hours": "Maximale Stunden für eine ontime nach Bot neustart", "message_delete_timer": "Zeit bis zum löschen einer Botnachricht in sekunden", diff --git a/web/src/assets/i18n/en.json b/web/src/assets/i18n/en.json index 539f3570..4f324ec6 100644 --- a/web/src/assets/i18n/en.json +++ b/web/src/assets/i18n/en.json @@ -200,6 +200,10 @@ "role": "Role", "rule_count": "Rules", "save": "Save", + "state": { + "off": "Off", + "on": "On" + }, "user_warnings": "User warnings", "users": "User", "value": "Value", @@ -433,6 +437,7 @@ "header": "Bot configuration", "help_voice_channel_id": "Voicechannel für help notifications", "login_message_channel_id": "Channel for bot message after start", + "maintenance": "Maintenance mode", "max_message_xp_per_hour": "Max xp per hour with message", "max_voice_state_hours": "Max ontime hours after bot restart", "message_delete_timer": "Time to wait before delete bot messages", diff --git a/web/src/styles/themes/sh-edraft-dark-theme.scss b/web/src/styles/themes/sh-edraft-dark-theme.scss index 827d3933..6f483f02 100644 --- a/web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/web/src/styles/themes/sh-edraft-dark-theme.scss @@ -626,6 +626,27 @@ } } + .p-selectbutton { + .p-highlight { + background-color: $primaryHeaderColor !important; + } + + .p-button { + + border: 1px solid $primaryHeaderColor !important; + + &:hover { + background-color: $secondaryHeaderColor !important; + border: 1px solid $secondaryHeaderColor !important; + } + + &:focus { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + } + } + } + .p-multiselect { background-color: $primaryBackgroundColor !important; color: $primaryTextColor !important;