From a8663b8d54f72321abeb8505cc2ac1a9209cdd89 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 29 Dec 2021 20:41:15 +0100 Subject: [PATCH] Improved permission handling --- src/gismo/startup.py | 15 ++-- src/modules/base/base.py | 22 ++--- src/modules/boot_log/boot_log.py | 2 +- src/modules/database/database.py | 2 +- .../permission/abc/permission_service_abc.py | 25 ++++++ .../configuration/permission_settings.py | 1 - src/modules/permission/permission.py | 46 +++++----- .../permission/service/permission_service.py | 90 ++++++++++++++++++- src/modules_core/service/module_service.py | 22 +++-- 9 files changed, 164 insertions(+), 61 deletions(-) diff --git a/src/gismo/startup.py b/src/gismo/startup.py index 1a1f79e..df453cb 100644 --- a/src/gismo/startup.py +++ b/src/gismo/startup.py @@ -71,12 +71,10 @@ class Startup(StartupABC): services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings)) - # modules + # general services services.add_singleton(ModuleServiceABC, ModuleService) services.add_singleton(BotServiceABC, BotService) services.add_transient(MessageServiceABC, MessageService) - - # general services services.add_transient(MigrationService) # data services @@ -87,14 +85,15 @@ class Startup(StartupABC): services.add_transient(UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService) services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) + # module services + services.add_singleton(PermissionServiceABC, PermissionService) + # modules - services.add_transient(ModuleABC, Permission) services.add_transient(ModuleABC, Database) - services.add_transient(ModuleABC, Base) services.add_transient(ModuleABC, BootLog) - - # permission module services - services.add_transient(PermissionServiceABC, PermissionService) + services.add_singleton(ModuleABC, Permission) + services.add_singleton(ModuleABC, Base) + # migrations services.add_transient(MigrationABC, InitialMigration) diff --git a/src/modules/base/base.py b/src/modules/base/base.py index ca41fb6..a10e745 100644 --- a/src/modules/base/base.py +++ b/src/modules/base/base.py @@ -20,6 +20,7 @@ from gismo_data.model.user import User from gismo_data.model.user_joined_server import UserJoinedServer from gismo_data.model.user_joined_voice_channel import UserJoinedVoiceChannel from modules.base.base_settings import BaseSettings +from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules_core.abc.events.on_member_join_abc import OnMemberJoinABC from modules_core.abc.events.on_member_remove_abc import OnMemberRemoveABC from modules_core.abc.events.on_message_abc import OnMessageABC @@ -42,7 +43,8 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS user_joins_vc: UserJoinedVoiceChannelRepositoryABC, bot: BotServiceABC, db: DatabaseContextABC, - messenger: MessageServiceABC + messenger: MessageServiceABC, + permission_service: PermissionServiceABC ): self._config = config self._logger = logger @@ -55,6 +57,7 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS self._bot = bot self._db = db self._messenger = messenger + self._permission_service = permission_service ModuleABC.__init__( self, @@ -66,7 +69,7 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS }, BaseSettings ) - self._logger.trace(__name__, f'Module {type(self)} loaded') + self._logger.info(__name__, f'Module {type(self)} loaded') def _get_config(self, g_id: int) -> BaseSettings: return self._config.get_configuration(f'{type(self).__name__}_{g_id}') @@ -100,21 +103,14 @@ class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceS async def _add_if_not_exists_user(self, member: Union[discord.User, discord.Member]): self._logger.debug(__name__, f'Check if user exists {member}') - # todo: user permission service settings: BaseSettings = self._get_config() await self._messenger.send_dm_message(settings.welcome_message.format(member.guild.name), member) - for roleId in settings.admin_roles: - g: discord.Guild = member.guild - role: discord.Role = g.get_role(roleId) - for admin in role.members: - await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), admin) + for admin in self._permission_service.get_admins(): + await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), admin) - for roleId in settings.moderator_roles: - g: discord.Guild = member.guild - role: discord.Role = g.get_role(roleId) - for mod in role.members: - await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), mod) + for moderator in self._permission_service.get_moderators(): + await self._messenger.send_dm_message(settings.welcome_message_for_team.format(member.name), moderator) try: server = self._servers.get_server_by_discord_id(member.guild.id) diff --git a/src/modules/boot_log/boot_log.py b/src/modules/boot_log/boot_log.py index b784bcb..d6e0928 100644 --- a/src/modules/boot_log/boot_log.py +++ b/src/modules/boot_log/boot_log.py @@ -35,7 +35,7 @@ class BootLog(ModuleABC, OnReadyABC): }, BootLogSettings ) - self._logger.trace(__name__, f'Module {type(self)} loaded') + self._logger.info(__name__, f'Module {type(self)} loaded') async def on_ready(self): self._logger.debug(__name__, f'Module {type(self)} started') diff --git a/src/modules/database/database.py b/src/modules/database/database.py index eb19850..2c6e226 100644 --- a/src/modules/database/database.py +++ b/src/modules/database/database.py @@ -57,7 +57,7 @@ class Database(ModuleABC, OnReadyABC): { OnReadyABC: 0 }, None ) - self._logger.trace(__name__, f'Module {type(self)} loaded') + self._logger.info(__name__, f'Module {type(self)} loaded') def _validate_init_time(self): try: diff --git a/src/modules/permission/abc/permission_service_abc.py b/src/modules/permission/abc/permission_service_abc.py index d74070b..c47ece7 100644 --- a/src/modules/permission/abc/permission_service_abc.py +++ b/src/modules/permission/abc/permission_service_abc.py @@ -1,7 +1,32 @@ from abc import ABC, abstractmethod +import discord class PermissionServiceABC(ABC): @abstractmethod def __init__(self): pass + + @abstractmethod + def on_ready(self): pass + + @abstractmethod + def on_member_update(self, before: discord.Member, after: discord.Member): pass + + @abstractmethod + def get_admin_role_ids(self, g_id: int) -> list[int]: pass + + @abstractmethod + def get_admin_roles(self, g_id: int) -> list[discord.Role]: pass + + @abstractmethod + def get_admins(self, g_id: int) -> list[discord.Member]: pass + + @abstractmethod + def get_moderator_role_ids(self, g_id: int) -> list[int]: pass + + @abstractmethod + def get_moderator_roles(self, g_id: int) -> list[discord.Role]: pass + + @abstractmethod + def get_moderators(self, g_id: int) -> list[discord.Member]: pass diff --git a/src/modules/permission/configuration/permission_settings.py b/src/modules/permission/configuration/permission_settings.py index 95cf5ef..dceabe8 100644 --- a/src/modules/permission/configuration/permission_settings.py +++ b/src/modules/permission/configuration/permission_settings.py @@ -12,7 +12,6 @@ class PermissionSettings(ConfigurationModelABC): self._admin_roles: list[int] = [] self._moderator_roles: list[int] = [] - @property def admin_roles(self) -> list[int]: return self._admin_roles diff --git a/src/modules/permission/permission.py b/src/modules/permission/permission.py index 2b71dc8..1f799d0 100644 --- a/src/modules/permission/permission.py +++ b/src/modules/permission/permission.py @@ -1,41 +1,37 @@ -from ctypes import Union -from datetime import datetime - import discord -from cpl_core.configuration import ConfigurationABC -from cpl_core.database.context import DatabaseContextABC -from cpl_core.logging import LoggerABC -from gismo_core.abc.bot_service_abc import BotServiceABC -from gismo_core.configuration.server_settings import ServerSettings -from gismo_data.abc.client_repository_abc import ClientRepositoryABC -from gismo_data.abc.known_user_repository_abc import KnownUserRepositoryABC -from gismo_data.abc.user_joined_server_repository_abc import \ - UserJoinedServerRepositoryABC -from gismo_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC -from gismo_data.abc.user_repository_abc import UserRepositoryABC -from gismo_data.model.client import Client -from gismo_data.model.known_user import KnownUser -from gismo_data.model.server import Server -from gismo_data.model.user import User -from gismo_data.model.user_joined_server import UserJoinedServer -from gismo_data.model.user_joined_voice_channel import UserJoinedVoiceChannel -from gismo_data.service.user_repository_service import ServerRepositoryABC -from modules.permission.configuration.permission_settings import PermissionSettings +from cpl_core.logging import LoggerABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC +from modules.permission.configuration.permission_settings import \ + PermissionSettings +from modules_core.abc.events.on_member_update_abc import OnMemberUpdateABC from modules_core.abc.events.on_ready_abc import OnReadyABC from modules_core.abc.module_abc import ModuleABC -class Permission(ModuleABC): +class Permission(ModuleABC, OnReadyABC, OnMemberUpdateABC): def __init__( self, logger: LoggerABC, + permission_service: PermissionServiceABC ): self._logger = logger + self._permission_service = permission_service + ModuleABC.__init__( self, - { OnReadyABC: 0 }, + { OnReadyABC: 1, OnMemberUpdateABC: 0 }, PermissionSettings ) - self._logger.trace(__name__, f'Module {type(self)} loaded') \ No newline at end of file + self._logger.info(__name__, f'Module {type(self)} loaded') + + async def on_ready(self): + self._logger.debug(__name__, f'Module {type(self)} started') + self._permission_service.on_ready() + + async def on_member_update(self, before: discord.Member, after: discord.Member): + self._logger.debug(__name__, f'Module {type(self)} started') + + if before.roles != after.roles: + self._permission_service.on_member_update(before, after) \ No newline at end of file diff --git a/src/modules/permission/service/permission_service.py b/src/modules/permission/service/permission_service.py index 461fe98..c0b9158 100644 --- a/src/modules/permission/service/permission_service.py +++ b/src/modules/permission/service/permission_service.py @@ -1,4 +1,88 @@ -class PermissionService: +import discord +from cpl_core.logging import LoggerABC +from cpl_core.configuration import ConfigurationABC +from gismo_core.abc.bot_service_abc import BotServiceABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC +from modules.permission.configuration.permission_settings import PermissionSettings - def __init__(self): - pass + +class PermissionService(PermissionServiceABC): + + def __init__(self, logger: LoggerABC, bot: BotServiceABC, config: ConfigurationABC): + PermissionServiceABC.__init__(self) + self._logger = logger + self._bot = bot + self._config = config + + self._admin_role_ids: dict[str, list[int]] = {} + self._admin_roles: dict[list[discord.Role]] = {} + self._admins: dict[list[discord.Member]] = {} + + self._moderator_role_ids: dict[list[int]] = {} + self._moderator_roles: dict[list[discord.Role]] = {} + self._moderators: dict[list[discord.Member]] = {} + + def on_ready(self): + for guild in self._bot.guilds: + guild: discord.Guild = guild + + settings: PermissionSettings = self._config.get_configuration(f'Permission_{guild.id}') + if settings is None: + self._logger.error(__name__, 'Permission settings not found') + return + + self._admin_role_ids[guild.id] = settings.admin_roles + self._admin_roles[guild.id] = [] + self._admins[guild.id] = [] + + self._moderator_role_ids[guild.id] = settings.moderator_roles + self._moderator_roles[guild.id] = [] + self._moderators[guild.id] = [] + + for role in guild.roles: + role: discord.Role = role + + if role.id in self._admin_role_ids: + self._admin_roles[guild.id].append(role) + + for member in role.members: + self._admins[guild.id].append(member) + + if role.id in self._moderator_role_ids: + self._moderator_roles[guild.id].append(role) + + for member in role.members: + self._moderators[guild.id].append(member) + + def on_member_update(self, before: discord.Member, after: discord.Member): + g_id = after.guild.id + + if before in self._admin_roles[g_id] and after not in self._admin_roles[g_id]: + self._admins[g_id].remove(after) + + elif before not in self._admin_roles[g_id] and after in self._admin_roles[g_id]: + self._admins[g_id].append(after) + + if before in self._moderator_roles[g_id] and after not in self._moderator_roles[g_id]: + self._moderators[g_id].remove(after) + + elif before not in self._moderator_roles[g_id] and after in self._moderator_roles[g_id]: + self._moderators[g_id].append(after) + + def get_admin_role_ids(self, g_id: int) -> list[int]: + return self._admin_role_ids[g_id] + + def get_admin_roles(self, g_id: int) -> list[discord.Role]: + return self._admin_roles[g_id] + + def get_admins(self, g_id: int) -> list[discord.Member]: + return self._admins[g_id] + + def get_moderator_role_ids(self, g_id: int) -> list[int]: + return self._moderator_role_ids[g_id] + + def get_moderator_roles(self, g_id: int) -> list[discord.Role]: + return self._moderator_roles[g_id] + + def get_moderators(self, g_id: int) -> list[discord.Member]: + return self._moderators[g_id] diff --git a/src/modules_core/service/module_service.py b/src/modules_core/service/module_service.py index d029056..ead37c5 100644 --- a/src/modules_core/service/module_service.py +++ b/src/modules_core/service/module_service.py @@ -90,7 +90,7 @@ class ModuleService(ModuleServiceABC, commands.Cog, metaclass=_MetaCogABC): settings: ConfigurationModelABC = module.settings_type() settings.from_dict(json_cfg[id]) self._config.add_configuration(f'{type(module).__name__}_{id}', settings) - self._logger.debug(__name__, f'Added config: {type(module).__name__}_{id}') + self._logger.info(__name__, f'Added config: {type(module).__name__}_{id}') modules.append(module) @@ -103,14 +103,18 @@ class ModuleService(ModuleServiceABC, commands.Cog, metaclass=_MetaCogABC): if modules.count() < 1: self._logger.debug(__name__, f'Stopped {event} modules') return - - func_name = String.convert_to_snake_case(event.__name__.split('ABC')[0]) - for module in modules: - func = getattr(module, func_name) - await func(*args) - if not module.success: - self._logger.debug(__name__, f'Stopped propagation for {event} from {type(module)}') - break + + try: + func_name = String.convert_to_snake_case(event.__name__.split('ABC')[0]) + for module in modules: + self._logger.trace(__name__, f'Start {type(module)} module') + func = getattr(module, func_name) + await func(*args) + if not module.success: + self._logger.debug(__name__, f'Stopped propagation for {event} from {type(module)}') + break + except Exception as e: + self._logger.error(__name__, f'Start {event} modules failed', e) self._logger.debug(__name__, f'Stopped {event} modules')