diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 7eeac4a343..c8e3ac0963 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 7eeac4a343fcefb0aaff63a8fd28d35b46930f9a +Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 diff --git a/kdb-bot/src/modules/auto_role/auto_role_module.py b/kdb-bot/src/modules/auto_role/auto_role_module.py index cf53c6c4cd..f4528625ed 100644 --- a/kdb-bot/src/modules/auto_role/auto_role_module.py +++ b/kdb-bot/src/modules/auto_role/auto_role_module.py @@ -9,7 +9,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.auto_role.command.auto_role_group import AutoRoleGroup from modules.auto_role.events.auto_role_on_raw_reaction_add import AutoRoleOnRawReactionAddEvent from modules.auto_role.events.auto_role_on_raw_reaction_remove import AutoRoleOnRawReactionRemoveEvent -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleModule(ModuleABC): @@ -21,7 +21,7 @@ class AutoRoleModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - services.add_transient(ReactionHandler) + services.add_transient(AutoRoleReactionHandler) # commands self._dc.add_command(AutoRoleGroup) # events 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 5893d4638e..5c891c0288 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 @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent 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 modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionAddABC.__init__(self) 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 49899a0e5d..5e85f740a7 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 @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent 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 modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionRemoveABC.__init__(self) diff --git a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py similarity index 98% rename from kdb-bot/src/modules/auto_role/helper/reaction_handler.py rename to kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py index 56c8df8715..713af5cc3a 100644 --- a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py +++ b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py @@ -10,7 +10,7 @@ from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role_rule import AutoRoleRule -class ReactionHandler: +class AutoRoleReactionHandler: def __init__( self, diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index cacde84ba7..0282a18000 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -18,8 +18,11 @@ from modules.base.events.base_on_command_event import BaseOnCommandEvent from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent from modules.base.events.base_on_message_event import BaseOnMessageEvent +from modules.base.events.base_on_raw_reaction_add import BaseOnRawReactionAddEvent +from modules.base.events.base_on_raw_reaction_remove import BaseOnRawReactionRemoveEvent from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent from modules.base.events.base_on_voice_state_update_event_help_channel import BaseOnVoiceStateUpdateEventHelpChannel +from modules.base.helper.base_reaction_handler import BaseReactionHandler from modules.base.service.base_helper_service import BaseHelperService @@ -33,6 +36,7 @@ class BaseModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(BaseHelperABC, BaseHelperService) + services.add_transient(BaseReactionHandler) # commands self._dc.add_command(AFKCommand) self._dc.add_command(HelpCommand) @@ -47,5 +51,7 @@ class BaseModule(ModuleABC): self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent) self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_add.value, BaseOnRawReactionAddEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_remove.value, BaseOnRawReactionRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEventHelpChannel) diff --git a/kdb-bot/src/modules/base/command/ping_command.py b/kdb-bot/src/modules/base/command/ping_command.py index 9ccf1420af..7633d8a060 100644 --- a/kdb-bot/src/modules/base/command/ping_command.py +++ b/kdb-bot/src/modules/base/command/ping_command.py @@ -1,3 +1,4 @@ +import discord from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe @@ -8,6 +9,10 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings +from modules.permission.abc.permission_service_abc import PermissionServiceABC class PingCommand(DiscordCommandABC): @@ -18,7 +23,10 @@ class PingCommand(DiscordCommandABC): message_service: MessageServiceABC, bot: DiscordBotServiceABC, client_utils: ClientUtilsServiceABC, - translate: TranslatePipe + translate: TranslatePipe, + permissions: PermissionServiceABC, + base_helper: BaseHelperABC, + servers: ServerRepositoryABC, ): DiscordCommandABC.__init__(self) @@ -27,13 +35,34 @@ class PingCommand(DiscordCommandABC): self._bot = bot self._client_utils = client_utils self._t = translate + self._permissions = permissions + self._base_helper = base_helper + self._servers = servers self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + @staticmethod + def _get_ping(url: str) -> float: + from icmplib import ping + ping_result = ping(url, count=4, interval=0.2, privileged=False) + return ping_result.avg_rtt + @commands.hybrid_command() @commands.guild_only() @CommandChecks.check_is_ready() async def ping(self, ctx: Context): self._logger.debug(__name__, f'Received command ping {ctx}') - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong')) + if self._permissions.is_member_technician(ctx.author): + embed = discord.Embed( + title=self._t.transform('modules.base.info.title'), + description=self._t.transform('modules.base.info.description'), + color=int('ef9d0d', 16) + ) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + settings: BaseServerSettings = self._base_helper.get_config(server.discord_server_id) + for server in settings.ping_urls: + embed.add_field(name=server, value=f'{self._get_ping(server)} ms', inline=False) + await self._message_service.send_ctx_msg(ctx, embed) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong')) self._logger.trace(__name__, f'Finished ping command') diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index b08bd07230..712730b8f2 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -62,10 +62,14 @@ class UserGroup(DiscordCommandABC): @user.command() @commands.guild_only() @CommandChecks.check_is_ready() - @CommandChecks.check_is_member_moderator() async def info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None): self._logger.debug(__name__, f'Received command user-info {ctx}:{member},{wait}') + is_mod = self._permissions.is_member_moderator(ctx.author) + if member is not None and not is_mod: + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + return + if member is None or not isinstance(member, discord.Member): member = ctx.author @@ -94,20 +98,26 @@ class UserGroup(DiscordCommandABC): roles += f'{role.name}\n' embed.add_field(name=self._t.transform('modules.base.user_info.fields.roles'), value=roles, inline=False) - joins_string = '' - for join in joins: - joins_string += f'{self._date.transform(join.joined_on)}\n' - embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string) + if is_mod or member == ctx.author: + joins_string = '' + for join in joins: + joins_string += f'{self._date.transform(join.joined_on)}\n' + embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string) - lefts_string = '' - for join in joins: - if join.leaved_on is None: - if lefts_string == '': - lefts_string = '/' - continue - lefts_string += f'{self._date.transform(join.leaved_on)}\n' - embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string) - embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False) + if is_mod or member == ctx.author: + lefts_string = '' + for join in joins: + if join.leaved_on is None: + if lefts_string == '': + lefts_string = '/' + continue + lefts_string += f'{self._date.transform(join.leaved_on)}\n' - await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string) + + if is_mod or member == ctx.author: + embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False) + + # send to interaction because of sensitive data + await self._message_service.send_interaction_msg(ctx.interaction, embed, wait_before_delete=wait) self._logger.trace(__name__, f'Finished user-info command') diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py index 578839b984..b583863384 100644 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ b/kdb-bot/src/modules/base/configuration/base_server_settings.py @@ -13,11 +13,13 @@ class BaseServerSettings(ConfigurationModelABC): self._id: int = 0 self._max_voice_state_hours: int = 0 self._xp_per_message: int = 0 + self._xp_per_reaction: int = 0 self._xp_per_ontime_hour: int = 0 self._afk_channel_ids: List[int] = List(int) self._afk_command_channel_id: int = 0 self._help_command_reference_url: str = '' self._help_voice_channel_id: int = 0 + self._ping_urls = List(str) @property def id(self) -> int: @@ -31,6 +33,10 @@ class BaseServerSettings(ConfigurationModelABC): def xp_per_message(self) -> int: return self._xp_per_message + @property + def xp_per_reaction(self) -> int: + return self._xp_per_reaction + @property def xp_per_ontime_hour(self) -> int: return self._xp_per_ontime_hour @@ -51,17 +57,24 @@ class BaseServerSettings(ConfigurationModelABC): def help_voice_channel_id(self) -> int: return self._help_voice_channel_id + @property + def ping_urls(self) -> List[str]: + return self._ping_urls + def from_dict(self, settings: dict): try: self._id = int(settings['Id']) self._max_voice_state_hours = int(settings['MaxVoiceStateHours']) self._xp_per_message = int(settings['XpPerMessage']) + self._xp_per_reaction = int(settings['XpPerReaction']) self._xp_per_ontime_hour = int(settings['XpPerOntimeHour']) for index in settings['AFKChannelIds']: self._afk_channel_ids.append(int(index)) self._afk_command_channel_id = settings['AFKCommandChannelId'] self._help_command_reference_url = settings['HelpCommandReferenceUrl'] self._help_voice_channel_id = settings['HelpVoiceChannelId'] + for url in settings['PingURLs']: + self._ping_urls.append(url) except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py new file mode 100644 index 0000000000..b8fe597bed --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py @@ -0,0 +1,36 @@ +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.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 modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionAddEvent(OnRawReactionAddABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler + ): + OnRawReactionAddABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_add(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'add') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py new file mode 100644 index 0000000000..4f2f0bb74b --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py @@ -0,0 +1,36 @@ +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.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 modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionRemoveEvent(OnRawReactionRemoveABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler, + ): + OnRawReactionRemoveABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'remove') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/helper/__init__.py b/kdb-bot/src/modules/base/helper/__init__.py new file mode 100644 index 0000000000..a19a540f30 --- /dev/null +++ b/kdb-bot/src/modules/base/helper/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.auto_role.helper' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py new file mode 100644 index 0000000000..401f97b1f4 --- /dev/null +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -0,0 +1,52 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings + + +class BaseReactionHandler: + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + base_helper: BaseHelperABC, + db: DatabaseContextABC, + ): + self._logger = logger + self._bot = bot + self._servers = servers + self._users = users + self._base_helper = base_helper + self._db = db + + async def handle(self, payload: RawReactionActionEvent, r_type=None) -> None: + self._logger.trace(__name__, f'Handle reaction {payload} {r_type}') + + guild = self._bot.get_guild(payload.guild_id) + member = guild.get_member(payload.user_id) + if member is None: + self._logger.warn(__name__, f'User {payload.user_id} in {guild.name} not found - skipping') + return + + server = self._servers.get_server_by_discord_id(guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + settings: BaseServerSettings = self._base_helper.get_config(guild.id) + + if r_type == 'add': + user.xp += settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() + elif r_type == 'remove': + user.xp -= settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() + else: + self._logger.warn(__name__, f'Invalid reaction type {r_type}') diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 8e6eb9e743..2708324cc0 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -10,16 +10,26 @@ from bot_data.model.level import Level from bot_data.model.server import Server from bot_data.service.level_repository_service import LevelRepositoryService from modules.level.configuration.default_level_settings import DefaultLevelSettings +from modules.level.service.level_service import LevelService class LevelSeeder(DataSeederABC): - def __init__(self, logger: DatabaseLogger, levels: DefaultLevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): + def __init__( + self, + logger: DatabaseLogger, + levels: DefaultLevelSettings, + level_repo: LevelRepositoryService, + servers: ServerRepositoryABC, + level: LevelService, + bot: DiscordBotServiceABC + ): DataSeederABC.__init__(self) self._logger = logger self._levels = level_repo self._servers = servers + self._level = level self._bot = bot self._level_header = levels.level_header @@ -87,4 +97,7 @@ class LevelSeeder(DataSeederABC): except Exception as e: self._logger.error(__name__, f'Cannot change position of {role.name}', e) + for m in guild.members: + await self._level.check_level(m) + self._logger.debug(__name__, f'Checked role order') diff --git a/kdb-bot/src/modules/permission/service/permission_service.py b/kdb-bot/src/modules/permission/service/permission_service.py index f43f2f699b..825a9ea073 100644 --- a/kdb-bot/src/modules/permission/service/permission_service.py +++ b/kdb-bot/src/modules/permission/service/permission_service.py @@ -128,8 +128,7 @@ class PermissionService(PermissionServiceABC): return member.guild.id in self._admins and member in self._admins[member.guild.id] def is_member_moderator(self, member: discord.Member) -> bool: - return member.guild.id in self._moderators \ - and member in self._moderators[member.guild.id] or self.is_member_admin(member) + return member.guild.id in self._moderators and member in self._moderators[member.guild.id] or self.is_member_admin(member) def is_member_technician(self, member: discord.Member) -> bool: return member in self._technicians