diff --git a/cpl-workspace.json b/cpl-workspace.json index b0db2c0..891044f 100644 --- a/cpl-workspace.json +++ b/cpl-workspace.json @@ -10,7 +10,8 @@ "boot-log": "src/modules/boot_log/boot-log.json", "level-generator": "tools/level_generator/level-generator.json", "ontime-calculator": "tools/ontime_calculator/ontime-calculator.json", - "database": "src/modules/database/database.json" + "database": "src/modules/database/database.json", + "base": "src/modules/base/base.json" }, "Scripts": { "build-start": "cd src/gismo_cli; echo 'gismo-cli:'; cpl build; cd ../gismo; echo 'gismo:'; cpl build; cd ../../dist/gismo/build/gismo; bash gismo", diff --git a/src/gismo/__init__.py b/src/gismo/__init__.py index a9e4465..e2d1c61 100644 --- a/src/gismo/__init__.py +++ b/src/gismo/__init__.py @@ -15,11 +15,11 @@ __title__ = 'gismo' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo/application.py b/src/gismo/application.py index 9602da6..849c55e 100644 --- a/src/gismo/application.py +++ b/src/gismo/application.py @@ -3,8 +3,10 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.console import Console from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.logging import LoggerABC + from gismo_core.abc.bot_service_abc import BotServiceABC from gismo_core.service.bot_service import BotService +from gismo_data.service.migration_service import MigrationService class Gismo(ApplicationABC): @@ -14,12 +16,14 @@ class Gismo(ApplicationABC): self._bot: BotService = services.get_service(BotServiceABC) self._logger: LoggerABC = services.get_service(LoggerABC) + self._migrations: MigrationService = services.get_service(MigrationService) async def configure(self): pass async def main(self): try: + self._migrations.migrate() self._logger.trace(__name__, f'Try to start {BotService}') await self._bot.start_async() except Exception as e: diff --git a/src/gismo/appsettings.example.json b/src/gismo/appsettings.example.json new file mode 100644 index 0000000..7bd96f0 --- /dev/null +++ b/src/gismo/appsettings.example.json @@ -0,0 +1,36 @@ +{ + "TimeFormatSettings": { + "DateFormat": "%Y-%m-%d", + "TimeFormat": "%H:%M:%S", + "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", + "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" + }, + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "ERROR", + "FileLogLevel": "WARN" + }, + "Discord": { + "Token": "" + }, + "Bot": { + "Prefix": "!g", + "Servers": [ + { + "Id": "", + "LoginMessageChannelId": "", + "LoginMessage": "", + "MessageDeleteTimer": 0, + "WelcomeMessage": "", + "GoodbyeMessage": "", + "MaxVoiceStateHours": 0, + "XpPerMessage": 0, + "XpPerOntimeHour": 0, + "AFKChannelIds": [], + "AdminRoleIds": [], + "ModeratorRoleIds": [] + } + ] + } +} \ No newline at end of file diff --git a/src/gismo/appsettings.json b/src/gismo/appsettings.json index 629e6eb..a02074c 100644 --- a/src/gismo/appsettings.json +++ b/src/gismo/appsettings.json @@ -5,11 +5,10 @@ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" }, - "LoggingSettings": { "Path": "logs/", "Filename": "log_$start_time.log", "ConsoleLogLevel": "ERROR", "FileLogLevel": "WARN" } -} +} \ No newline at end of file diff --git a/src/gismo/gismo.json b/src/gismo/gismo.json index d8f8c8d..81c9765 100644 --- a/src/gismo/gismo.json +++ b/src/gismo/gismo.json @@ -3,7 +3,7 @@ "Name": "gismo", "Version": { "Major": "0", - "Minor": "2", + "Minor": "3", "Micro": "0" }, "Author": "Sven Heidemann", @@ -48,7 +48,9 @@ "../gismo_core/gismo-core.json", "../gismo_data/gismo-data.json", "../modules_core/modules-core.json", - "../modules/boot_log/boot-log.json" + "../modules/boot_log/boot-log.json", + "../modules/base/base.json", + "../modules/database/database.json" ] } } \ No newline at end of file diff --git a/src/gismo/startup.py b/src/gismo/startup.py index f359a18..1d38c1d 100644 --- a/src/gismo/startup.py +++ b/src/gismo/startup.py @@ -9,15 +9,32 @@ from cpl_core.dependency_injection import (ServiceCollectionABC, ServiceProviderABC) from cpl_core.environment import ApplicationEnvironment from cpl_core.logging import LoggerABC + from gismo_core.abc.bot_service_abc import BotServiceABC from gismo_core.abc.message_service_abc import MessageServiceABC from gismo_core.service.bot_service import BotService from gismo_core.service.message_service import MessageService +from gismo_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from gismo_data.abc.migration_abc import MigrationABC from gismo_data.abc.server_repository_abc import ServerRepositoryABC +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.db_context import DBContext -from gismo_data.service.server_repository_service import ServerRepositoryService +from gismo_data.migration.initial_migration import InitialMigration +from gismo_data.migration.migration_0_3 import Migration_0_3 +from gismo_data.migration.migration_0_3_1 import Migration_0_3_1 +from gismo_data.service.client_repository_service import ( + ClientRepositoryABC, ClientRepositoryService) +from gismo_data.service.known_user_repository_service import \ + KnownUserRepositoryService +from gismo_data.service.migration_service import MigrationService +from gismo_data.service.server_repository_service import \ + ServerRepositoryService +from gismo_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService +from gismo_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService from gismo_data.service.user_repository_service import UserRepositoryService +from modules.base.base import Base from modules.boot_log.boot_log import BootLog from modules.database.database import Database from modules_core.abc.module_abc import ModuleABC @@ -51,15 +68,31 @@ class Startup(StartupABC): services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings)) + # modules services.add_singleton(ModuleServiceABC, ModuleService) services.add_singleton(BotServiceABC, BotService) services.add_transient(MessageServiceABC, MessageService) + # services + services.add_transient(MigrationService) + + # data services services.add_transient(ServerRepositoryABC, ServerRepositoryService) services.add_transient(UserRepositoryABC, UserRepositoryService) - + services.add_transient(ClientRepositoryABC, ClientRepositoryService) + services.add_transient(KnownUserRepositoryABC, KnownUserRepositoryService) + services.add_transient(UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService) + services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) + + # modules services.add_transient(ModuleABC, Database) + services.add_transient(ModuleABC, Base) services.add_transient(ModuleABC, BootLog) + + # migrations + services.add_transient(MigrationABC, InitialMigration) + services.add_transient(MigrationABC, Migration_0_3) + services.add_transient(MigrationABC, Migration_0_3_1) provider: ServiceProviderABC = services.build_service_provider() diff --git a/src/gismo_core/__init__.py b/src/gismo_core/__init__.py index f1838fc..98c9727 100644 --- a/src/gismo_core/__init__.py +++ b/src/gismo_core/__init__.py @@ -15,11 +15,11 @@ __title__ = 'gismo_core' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_core/abc/__init__.py b/src/gismo_core/abc/__init__.py index b41756e..2ef29e0 100644 --- a/src/gismo_core/abc/__init__.py +++ b/src/gismo_core/abc/__init__.py @@ -15,11 +15,11 @@ __title__ = 'gismo_core.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_core/configuration/__init__.py b/src/gismo_core/configuration/__init__.py index affff5f..256c23c 100644 --- a/src/gismo_core/configuration/__init__.py +++ b/src/gismo_core/configuration/__init__.py @@ -15,11 +15,11 @@ __title__ = 'gismo_core.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_core/configuration/server_settings.py b/src/gismo_core/configuration/server_settings.py index f339b9c..d9e3402 100644 --- a/src/gismo_core/configuration/server_settings.py +++ b/src/gismo_core/configuration/server_settings.py @@ -13,6 +13,15 @@ class ServerSettings(ConfigurationModelABC): self._message_delete_timer: int = 0 self._login_message_channel_id: int = 0 self._login_message: str = '' + self._welcome_message: str = '' + self._welcome_message_for_team: str = '' + self._goodbye_message: str = '' + self._max_voice_state_hours: int = 0 + self._xp_per_message: int = 0 + self._xp_per_ontime_hour: int = 0 + self._afk_channel_ids: list[int] = [] + self._admin_roles: list[int] = [] + self._moderator_roles: list[int] = [] @property def id(self) -> str: @@ -30,12 +39,62 @@ class ServerSettings(ConfigurationModelABC): def login_message(self) -> str: return self._login_message + @property + def welcome_message(self) -> str: + return self._welcome_message + + @property + def welcome_message_for_team(self) -> str: + return self._welcome_message_for_team + + @property + def goodbye_message(self) -> str: + return self._goodbye_message + + @property + def max_voice_state_hours(self) -> int: + return self._max_voice_state_hours + + @property + def xp_per_message(self) -> int: + return self._xp_per_message + + @property + def xp_per_ontime_hour(self) -> int: + return self._xp_per_ontime_hour + + @property + def afk_channel_ids(self) -> list[int]: + return self._afk_channel_ids + + @property + def admin_roles(self) -> list[int]: + return self._admin_roles + + @property + def moderator_roles(self) -> list[int]: + return self._moderator_roles + def from_dict(self, settings: dict): try: self._id = int(settings['Id']) self._message_delete_timer = int(settings['MessageDeleteTimer']) self._login_message_channel_id = int(settings['LoginMessageChannelId']) self._login_message = settings['LoginMessage'] + self._welcome_message = settings['WelcomeMessage'] + self._welcome_message_for_team = settings['WelcomeMessageForTeam'] + self._goodbye_message = settings['GoodbyeMessage'] + self._max_voice_state_hours = int(settings['MaxVoiceStateHours']) + self._xp_per_message = int(settings['XpPerMessage']) + self._xp_per_ontime_hour = int(settings['XpPerOntimeHour']) + for id in settings['AFKChannelIds']: + self._afk_channel_ids.append(int(id)) + + for id in settings['AdminRoleIds']: + self._admin_roles.append(int(id)) + + for id in settings['ModeratorRoleIds']: + self._moderator_roles.append(int(id)) except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/src/gismo_core/service/__init__.py b/src/gismo_core/service/__init__.py index 97f2495..016bce9 100644 --- a/src/gismo_core/service/__init__.py +++ b/src/gismo_core/service/__init__.py @@ -15,11 +15,11 @@ __title__ = 'gismo_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_core/service/message_service.py b/src/gismo_core/service/message_service.py index 32ae1c3..b86a712 100644 --- a/src/gismo_core/service/message_service.py +++ b/src/gismo_core/service/message_service.py @@ -3,6 +3,7 @@ from typing import Union import discord from cpl_core.configuration.configuration_abc import ConfigurationABC +from cpl_core.database.context.database_context_abc import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_query.extension import List from discord.ext.commands import Context @@ -10,14 +11,17 @@ from discord.ext.commands import Context from gismo_core.abc.bot_service_abc import BotServiceABC from gismo_core.abc.message_service_abc import MessageServiceABC from gismo_core.configuration.server_settings import ServerSettings +from gismo_data.abc.client_repository_abc import ClientRepositoryABC class MessageService(MessageServiceABC): - def __init__(self, config: ConfigurationABC, logger: LoggerABC, bot: BotServiceABC): + def __init__(self, config: ConfigurationABC, logger: LoggerABC, bot: BotServiceABC, clients: ClientRepositoryABC, db: DatabaseContextABC): self._config = config self._logger = logger self._bot = bot + self._clients = clients + self._db = db async def delete_messages(self, messages: List[discord.Message]): self._logger.debug(__name__, f'Try to delete {messages.count()} messages') @@ -29,11 +33,14 @@ class MessageService(MessageServiceABC): server_st: ServerSettings = self._config.get_configuration(f'DSERVER_{message.guild.id}') await asyncio.sleep(server_st.message_delete_timer) self._logger.debug(__name__, f'Try to delete message:\n\t{message}\n\t{message.content}') + guild_id = message.guild.id try: await message.delete() except Exception as e: self._logger.error(__name__, f'Deleting message failed', e) else: + self._clients.apppend_deleted_message_count(self._bot.user.id, guild_id, 1) + self._db.save_changes() self._logger.info(__name__, f'Deleted message {message}') async def send_channel_message(self, channel: discord.TextChannel, message: str): @@ -45,6 +52,8 @@ class MessageService(MessageServiceABC): self._logger.error(__name__, f'Send message to channel {channel.id} failed', e) else: self._logger.info(__name__, f'Sent message to channel {channel.id}') + self._clients.apppend_sent_message_count(self._bot.user.id, channel.guild.id, 1) + self._db.save_changes() await self.delete_message(msg) async def send_dm_message(self, message: str, receiver: Union[discord.User, discord.Member]): @@ -54,6 +63,8 @@ class MessageService(MessageServiceABC): except Exception as e: self._logger.error(__name__, f'Send message to user {receiver.id} failed', e) else: + self._clients.apppend_sent_message_count(self._bot.user.id, receiver.guild.id, 1) + self._db.save_changes() self._logger.info(__name__, f'Sent message to user {receiver.id}') async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.File]): @@ -73,4 +84,6 @@ class MessageService(MessageServiceABC): self._logger.error(__name__, f'Send message to channel {ctx.channel.id} failed', e) else: self._logger.info(__name__, f'Sent message to channel {ctx.channel.id}') + self._clients.apppend_sent_message_count(self._bot.user.id, ctx.guild.id, 1) + self._db.save_changes() await self.delete_message(msg) diff --git a/src/gismo_data/__init__.py b/src/gismo_data/__init__.py index 425ab6c..f8cc746 100644 --- a/src/gismo_data/__init__.py +++ b/src/gismo_data/__init__.py @@ -1 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'gismo_data' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_data/abc/__init__.py b/src/gismo_data/abc/__init__.py index 425ab6c..1845f2d 100644 --- a/src/gismo_data/abc/__init__.py +++ b/src/gismo_data/abc/__init__.py @@ -1 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'gismo_data.abc' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_data/abc/client_repository_abc.py b/src/gismo_data/abc/client_repository_abc.py new file mode 100644 index 0000000..4c61661 --- /dev/null +++ b/src/gismo_data/abc/client_repository_abc.py @@ -0,0 +1,53 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List +from gismo_data.model.client import Client + + +class ClientRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_clients(self) -> List[Client]: pass + + @abstractmethod + def get_client_by_id(self, id: int) -> Client: pass + + @abstractmethod + def get_client_by_discord_id(self, discord_id: int) -> Client: pass + + @abstractmethod + def find_client_by_discord_id(self, discord_id: int) -> Optional[Client]: pass + + @abstractmethod + def find_client_by_server_id(self, server_id: int) -> Optional[Client]: pass + + @abstractmethod + def find_client_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[Client]: pass + + @abstractmethod + def add_client(self, client: Client): pass + + @abstractmethod + def update_client(self, client: Client): pass + + @abstractmethod + def delete_client(self, client: Client): pass + + @abstractmethod + def apppend_sent_message_count(self, id: int, server_id: int, value: int): pass + + @abstractmethod + def apppend_received_message_count(self, id: int, server_id: int, value: int): pass + + @abstractmethod + def apppend_deleted_message_count(self, id: int, server_id: int, value: int): pass + + @abstractmethod + def apppend_received_command_count(self, id: int, server_id: int, value: int): pass + + @abstractmethod + def apppend_moved_users_count(self, id: int, server_id: int, value: int): pass diff --git a/src/gismo_data/abc/known_user_repository_abc.py b/src/gismo_data/abc/known_user_repository_abc.py new file mode 100644 index 0000000..5370677 --- /dev/null +++ b/src/gismo_data/abc/known_user_repository_abc.py @@ -0,0 +1,30 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List + +from gismo_data.model.known_user import KnownUser + + +class KnownUserRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_users(self) -> List[KnownUser]: pass + + @abstractmethod + def get_user_by_id(self, id: int) -> KnownUser: pass + + @abstractmethod + def get_user_by_discord_id(self, discord_id: int) -> KnownUser: pass + + @abstractmethod + def find_user_by_discord_id(self, discord_id: int) -> Optional[KnownUser]: pass + + @abstractmethod + def add_user(self, known_user: KnownUser): pass + + @abstractmethod + def delete_user(self, known_user: KnownUser): pass diff --git a/src/gismo_data/abc/migration_abc.py b/src/gismo_data/abc/migration_abc.py new file mode 100644 index 0000000..53b1696 --- /dev/null +++ b/src/gismo_data/abc/migration_abc.py @@ -0,0 +1,13 @@ +from abc import ABC, abstractmethod + + +class MigrationABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def upgrade(self): pass + + @abstractmethod + def downgrade(self): pass diff --git a/src/gismo_data/abc/user_joined_server_repository_abc.py b/src/gismo_data/abc/user_joined_server_repository_abc.py new file mode 100644 index 0000000..55de271 --- /dev/null +++ b/src/gismo_data/abc/user_joined_server_repository_abc.py @@ -0,0 +1,35 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List +from gismo_data.model.user_joined_server import UserJoinedServer + + +class UserJoinedServerRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_user_joined_servers(self) -> List[UserJoinedServer]: pass + + @abstractmethod + def get_user_joined_server_by_id(self, id: int) -> UserJoinedServer: pass + + @abstractmethod + def get_user_joined_servers_by_user_id(self, user_id: int) -> list[UserJoinedServer]: pass + + @abstractmethod + def get_active_user_joined_server_by_user_id(self, user_id: int) -> UserJoinedServer: pass + + @abstractmethod + def find_active_user_joined_server_by_user_id(self, user_id: int) -> Optional[UserJoinedServer]: pass + + @abstractmethod + def add_user_joined_server(self, user_joined_server: UserJoinedServer): pass + + @abstractmethod + def update_user_joined_server(self, user_joined_server: UserJoinedServer): pass + + @abstractmethod + def delete_user_joined_server(self, user_joined_server: UserJoinedServer): pass diff --git a/src/gismo_data/abc/user_joined_voice_channel_abc.py b/src/gismo_data/abc/user_joined_voice_channel_abc.py new file mode 100644 index 0000000..57ad410 --- /dev/null +++ b/src/gismo_data/abc/user_joined_voice_channel_abc.py @@ -0,0 +1,34 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List +from gismo_data.model.user_joined_voice_channel import UserJoinedVoiceChannel + +class UserJoinedVoiceChannelRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_user_joined_voice_channels(self) -> List[UserJoinedVoiceChannel]: pass + + @abstractmethod + def get_user_joined_voice_channel_by_id(self, id: int) -> UserJoinedVoiceChannel: pass + + @abstractmethod + def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> list[UserJoinedVoiceChannel]: pass + + @abstractmethod + def get_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> UserJoinedVoiceChannel: pass + + @abstractmethod + def find_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> Optional[UserJoinedVoiceChannel]: pass + + @abstractmethod + def add_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): pass + + @abstractmethod + def update_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): pass + + @abstractmethod + def delete_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): pass diff --git a/src/gismo_data/abc/user_repository_abc.py b/src/gismo_data/abc/user_repository_abc.py index c198753..5a647f0 100644 --- a/src/gismo_data/abc/user_repository_abc.py +++ b/src/gismo_data/abc/user_repository_abc.py @@ -18,10 +18,13 @@ class UserRepositoryABC(ABC): def get_user_by_id(self, id: int) -> User: pass @abstractmethod - def get_user_by_discord_id(self, discord_id: int) -> User: pass + def get_users_by_discord_id(self, discord_id: int) -> List[User]: pass @abstractmethod - def find_user_by_discord_id(self, discord_id: int) -> Optional[User]: pass + def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: pass + + @abstractmethod + def find_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[User]: pass @abstractmethod def add_user(self, user: User): pass diff --git a/src/gismo_data/db_context.py b/src/gismo_data/db_context.py index aae38ca..c45dafa 100644 --- a/src/gismo_data/db_context.py +++ b/src/gismo_data/db_context.py @@ -16,10 +16,6 @@ class DBContext(DatabaseContext): try: self._logger.debug(__name__, "Connecting to database") self._db.connect(database_settings) - for table in self._tables: - self._logger.debug(__name__, f"Create table if not exists: {table}") - self._logger.trace(__name__, f'Send SQL command: {table.get_create_string()}') - self._db.cursor.execute(table.get_create_string()) self.save_changes() self._logger.info(__name__, "Connected to database") diff --git a/src/gismo_data/migration/__init__.py b/src/gismo_data/migration/__init__.py new file mode 100644 index 0000000..ff6ccc6 --- /dev/null +++ b/src/gismo_data/migration/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'gismo_data.migration' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + +# imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_data/migration/initial_migration.py b/src/gismo_data/migration/initial_migration.py new file mode 100644 index 0000000..b2882c5 --- /dev/null +++ b/src/gismo_data/migration/initial_migration.py @@ -0,0 +1,57 @@ +from cpl_core.logging import LoggerABC + +from gismo_data.abc.migration_abc import MigrationABC +from gismo_data.db_context import DBContext + + +class InitialMigration(MigrationABC): + + def __init__(self, logger: LoggerABC, db: DBContext): + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `MigrationHistory` ( + `MigrationId` VARCHAR(255), + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`MigrationId`) + ); + """) + ) + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Servers` ( + `ServerId` BIGINT NOT NULL AUTO_INCREMENT, + `DiscordServerId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`ServerId`) + ); + """) + ) + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Users` ( + `UserId` BIGINT NOT NULL AUTO_INCREMENT, + `DiscordId` BIGINT NOT NULL, + `XP` BIGINT NOT NULL DEFAULT 0, + `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + FOREIGN KEY (`ServerId`) REFERENCES Servers(`ServerId`), + PRIMARY KEY(`UserId`) + ); + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `Servers`;') + self._cursor.execute('DROP TABLE `Users`;') diff --git a/src/gismo_data/migration/migration_0_3.py b/src/gismo_data/migration/migration_0_3.py new file mode 100644 index 0000000..138de96 --- /dev/null +++ b/src/gismo_data/migration/migration_0_3.py @@ -0,0 +1,65 @@ +from cpl_core.logging import LoggerABC + +from gismo_data.abc.migration_abc import MigrationABC +from gismo_data.db_context import DBContext + +class Migration_0_3(MigrationABC): + + def __init__(self, logger: LoggerABC, db: DBContext): + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Clients` ( + `ClientId` BIGINT NOT NULL AUTO_INCREMENT, + `DiscordClientId` BIGINT NOT NULL, + `SentMessageCount` BIGINT NOT NULL DEFAULT 0, + `ReceivedMessageCount` BIGINT NOT NULL DEFAULT 0, + `DeletedMessageCount` BIGINT NOT NULL DEFAULT 0, + `ReceivedCommandsCount` BIGINT NOT NULL DEFAULT 0, + `MovedUsersCount` BIGINT NOT NULL DEFAULT 0, + `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + FOREIGN KEY (`ServerId`) REFERENCES Servers(`ServerId`), + PRIMARY KEY(`ClientId`) + ); + """) + ) + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `KnownUsers` ( + `KnownUserId` BIGINT NOT NULL AUTO_INCREMENT, + `DiscordId` BIGINT NOT NULL, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`KnownUserId`) + ); + """) + ) + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `UserJoinedServers` ( + `JoinId` BIGINT NOT NULL AUTO_INCREMENT, + `UserId` BIGINT NOT NULL, + `JoinedOn` DATETIME(6) NOT NULL, + `LeavedOn` DATETIME(6), + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + FOREIGN KEY (`UserId`) REFERENCES Users(`UserId`), + PRIMARY KEY(`JoinId`) + ); + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `Clients`;') + self._cursor.execute('DROP TABLE `KnownUsers`;') + self._cursor.execute('DROP TABLE `UserJoinedServers`;') diff --git a/src/gismo_data/migration/migration_0_3_1.py b/src/gismo_data/migration/migration_0_3_1.py new file mode 100644 index 0000000..6102720 --- /dev/null +++ b/src/gismo_data/migration/migration_0_3_1.py @@ -0,0 +1,35 @@ +from cpl_core.logging import LoggerABC + +from gismo_data.abc.migration_abc import MigrationABC +from gismo_data.db_context import DBContext + +class Migration_0_3_1(MigrationABC): + + def __init__(self, logger: LoggerABC, db: DBContext): + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `UserJoinedVoiceChannel` ( + `JoinId` BIGINT NOT NULL AUTO_INCREMENT, + `UserId` BIGINT NOT NULL, + `DiscordChannelId` BIGINT NOT NULL, + `JoinedOn` DATETIME(6) NOT NULL, + `LeavedOn` DATETIME(6), + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + FOREIGN KEY (`UserId`) REFERENCES Users(`UserId`), + PRIMARY KEY(`JoinId`) + ); + """) + ) + + def downgrade(self): + self._logger.debug(__name__, 'Running downgrade') + self._cursor.execute('DROP TABLE `UserJoinedVoiceChannel`;') + diff --git a/src/gismo_data/model/__init__.py b/src/gismo_data/model/__init__.py index 425ab6c..7009a86 100644 --- a/src/gismo_data/model/__init__.py +++ b/src/gismo_data/model/__init__.py @@ -1 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'gismo_data.model' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_data/model/client.py b/src/gismo_data/model/client.py new file mode 100644 index 0000000..0382cfc --- /dev/null +++ b/src/gismo_data/model/client.py @@ -0,0 +1,170 @@ +from datetime import datetime +from cpl_core.database import TableABC + +from gismo_data.model.server import Server + + +class Client(TableABC): + + def __init__(self, + dc_id: int, + smc: int, + rmc: int, + dmc: int, + rcc: int, + muc: int, + server: Server, + created_at: datetime = None, + modified_at: datetime = None, + id=0 + ): + self._client_id = id + self._discord_client_id = dc_id + self._sent_message_count = smc + self._received_message_count = rmc + self._deleted_message_count = dmc + self._received_command_count = rcc + self._moved_users_count = muc + self._server: Server = server + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def client_id(self) -> int: + return self._client_id + + @property + def discord_id(self) -> int: + return self._discord_client_id + + @property + def sent_message_count(self) -> int: + return self._sent_message_count + + @sent_message_count.setter + def sent_message_count(self, value: int): + self._modified_at = datetime.now().isoformat() + self._sent_message_count = value + + @property + def received_message_count(self) -> int: + return self._received_message_count + + @received_message_count.setter + def received_message_count(self, value: int): + self._modified_at = datetime.now().isoformat() + self._received_message_count = value + + @property + def deleted_message_count(self) -> int: + return self._deleted_message_count + + @deleted_message_count.setter + def deleted_message_count(self, value: int): + self._modified_at = datetime.now().isoformat() + self._deleted_message_count = value + + @property + def received_command_count(self) -> int: + return self._received_command_count + + @received_command_count.setter + def received_command_count(self, value: int): + self._modified_at = datetime.now().isoformat() + self._received_command_count = value + + @property + def moved_users_count(self) -> int: + return self._moved_users_count + + @moved_users_count.setter + def moved_users_count(self, value: int): + self._modified_at = datetime.now().isoformat() + self._moved_users_count = value + + @property + def server(self) -> Server: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `Clients`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Clients` + WHERE `ClientId` = {id}; + """) + + @staticmethod + def get_select_by_discord_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Clients` + WHERE `DiscordClientId` = {id}; + """) + + @staticmethod + def get_select_by_server_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Clients` + WHERE `ServerId` = {id}; + """) + + @staticmethod + def get_select_by_discord_id_and_server_id_string(id: int, server_id: int) -> str: + return str(f""" + SELECT * FROM `Clients` + WHERE `DiscordClientId` = {id} + AND `ServerId` = {server_id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `Clients` ( + `DiscordClientId`, + `SentMessageCount`, + `ReceivedMessageCount`, + `DeletedMessageCount`, + `ReceivedCommandsCount`, + `MovedUsersCount`, + `ServerId`, + `CreatedAt`, + `LastModifiedAt` + ) VALUES ( + {self._discord_client_id}, + {self._sent_message_count}, + {self._received_message_count}, + {self._deleted_message_count}, + {self._received_message_count}, + {self._moved_users_count}, + {self._server.server_id}, + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `Clients` + SET `SentMessageCount` = {self._sent_message_count}, + `ReceivedMessageCount` = {self._received_message_count}, + `DeletedMessageCount` = {self._deleted_message_count}, + `ReceivedCommandsCount` = {self._received_command_count}, + `MovedUsersCount` = {self._moved_users_count}, + `LastModifiedAt` = '{self._modified_at}' + WHERE `ClientId` = {self._client_id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `Clients` + WHERE `ClientId` = {self._client_id}; + """) diff --git a/src/gismo_data/model/known_user.py b/src/gismo_data/model/known_user.py new file mode 100644 index 0000000..7dcda21 --- /dev/null +++ b/src/gismo_data/model/known_user.py @@ -0,0 +1,67 @@ +from datetime import datetime +from typing import Optional +from cpl_core.database import TableABC + +from gismo_data.model.server import Server + + +class KnownUser(TableABC): + + def __init__(self, dc_id: int, created_at: datetime = None, modified_at: datetime = None, id=0): + self._known_user_id = id + self._discord_id = dc_id + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def known_user_id(self) -> int: + return self._known_user_id + + @property + def discord_id(self) -> int: + return self._discord_id + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `KnownUsers`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `KnownUsers` + WHERE `KnownUserId` = {id}; + """) + + @staticmethod + def get_select_by_discord_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `KnownUsers` + WHERE `DiscordId` = {id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `KnownUsers` ( + `DiscordId`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + {self._discord_id}, + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return '' + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `KnownUsers` + WHERE `Id` = {self._known_user_id}; + """) diff --git a/src/gismo_data/model/migration_history.py b/src/gismo_data/model/migration_history.py new file mode 100644 index 0000000..c00ba7a --- /dev/null +++ b/src/gismo_data/model/migration_history.py @@ -0,0 +1,47 @@ +from cpl_core.database import TableABC + + +class MigrationHistory(TableABC): + + def __init__(self, id: str): + self._id = id + + TableABC.__init__(self) + + @property + def migration_id(self) -> str: + return self._id + + @staticmethod + def get_select_by_id_string(id: str) -> str: + return str(f""" + SELECT * FROM `MigrationHistory` + WHERE `MigrationId` = '{id}'; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `MigrationHistory` ( + `MigrationId`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + '{self._id}', + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `MigrationHistory` + SET LastModifiedAt` = '{self._modified_at}' + WHERE `MigrationId` = '{self._id}'; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `MigrationHistory` + WHERE `MigrationId` = '{self._id}'; + """) diff --git a/src/gismo_data/model/server.py b/src/gismo_data/model/server.py index 207bc35..14cddef 100644 --- a/src/gismo_data/model/server.py +++ b/src/gismo_data/model/server.py @@ -21,18 +21,6 @@ class Server(TableABC): @property def discord_server_id(self) -> int: return self._discord_server_id - - @staticmethod - def get_create_string() -> str: - return str(f""" - CREATE TABLE IF NOT EXISTS `Servers` ( - `ServerId` BIGINT NOT NULL AUTO_INCREMENT, - `DiscordServerId` BIGINT NOT NULL, - `CreatedAt` DATETIME(6), - `LastModifiedAt` DATETIME(6), - PRIMARY KEY(`ServerId`) - ); - """) @staticmethod def get_select_all_string() -> str: @@ -72,12 +60,12 @@ class Server(TableABC): UPDATE `Servers` SET `DiscordServerId` = {self._discord_server_id}, `LastModifiedAt` = '{self._modified_at}' - WHERE `Id` = {self._id}; + WHERE `Id` = {self._server_id}; """) @property def delete_string(self) -> str: return str(f""" DELETE FROM `Servers` - WHERE `Id` = {self._id}; + WHERE `Id` = {self._server_id}; """) diff --git a/src/gismo_data/model/user.py b/src/gismo_data/model/user.py index 3bbc27a..83f4b9f 100644 --- a/src/gismo_data/model/user.py +++ b/src/gismo_data/model/user.py @@ -38,21 +38,6 @@ class User(TableABC): def server(self) -> Optional[Server]: return self._server - @staticmethod - def get_create_string() -> str: - return str(f""" - CREATE TABLE IF NOT EXISTS `Users` ( - `UserId` BIGINT NOT NULL AUTO_INCREMENT, - `DiscordId` BIGINT NOT NULL, - `XP` BIGINT NOT NULL DEFAULT 0, - `ServerId` BIGINT, - `CreatedAt` DATETIME(6), - `LastModifiedAt` DATETIME(6), - FOREIGN KEY (`ServerId`) REFERENCES Servers(`ServerId`), - PRIMARY KEY(`UserId`) - ); - """) - @staticmethod def get_select_all_string() -> str: return str(f""" @@ -73,6 +58,14 @@ class User(TableABC): WHERE `DiscordId` = {id}; """) + @staticmethod + def get_select_by_discord_id_and_server_id_string(dc_id: int, s_id: int) -> str: + return str(f""" + SELECT * FROM `Users` + WHERE `DiscordId` = {dc_id} + AND `ServerId` = {s_id}; + """) + @property def insert_string(self) -> str: return str(f""" @@ -93,12 +86,12 @@ class User(TableABC): UPDATE `Users` SET `XP` = {self._xp}, `LastModifiedAt` = '{self._modified_at}' - WHERE `Id` = {self._id}; + WHERE `UserId` = {self._user_id}; """) @property def delete_string(self) -> str: return str(f""" DELETE FROM `Users` - WHERE `Id` = {self._id}; + WHERE `UserId` = {self._user_id}; """) diff --git a/src/gismo_data/model/user_joined_server.py b/src/gismo_data/model/user_joined_server.py new file mode 100644 index 0000000..7ec4806 --- /dev/null +++ b/src/gismo_data/model/user_joined_server.py @@ -0,0 +1,116 @@ +from datetime import datetime +from typing import Optional + +from cpl_core.database import TableABC + +from gismo_data.model.user import User +from gismo_data.model.server import Server + + +class UserJoinedServer(TableABC): + + def __init__(self, user: User, joined_on: datetime, leaved_on: datetime=None, created_at: datetime=None, modified_at: datetime=None, id=0): + self._join_id = id + self._user = user + self._joined_on = joined_on + self._leaved_on = leaved_on + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def join_id(self) -> int: + return self._join_id + + @property + def user(self) -> User: + return self._user + + @property + def joined_on(self) -> datetime: + return self._joined_on + + @joined_on.setter + def joined_on(self, value: datetime): + self._modified_at = datetime.now() + self.joined_on = value + + @property + def leaved_on(self) -> datetime: + return self._leaved_on + + @leaved_on.setter + def leaved_on(self, value: datetime): + self._modified_at = datetime.now() + self._leaved_on = value + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `UserJoinedServers`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedServers` + WHERE `JoinId` = {id}; + """) + + @staticmethod + def get_select_by_user_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedServers` + WHERE `UserId` = {id}; + """) + + @staticmethod + def get_select_active_by_user_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedServers` + WHERE `UserId` = {id} + AND `LeavedOn` IS NULL; + """) + + @property + def insert_string(self) -> str: + if self._leaved_on is not None: + return str(f""" + INSERT INTO `UserJoinedServers` ( + `UserId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + {self._user.user_id}, + '{self._joined_on}', + '{self._leaved_on}', + '{self._created_at}', + '{self._modified_at}' + ); + """) + else: + return str(f""" + INSERT INTO `UserJoinedServers` ( + `UserId`, `JoinedOn`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + {self._user.user_id}, + '{self._joined_on}', + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `UserJoinedServers` + SET `LeavedOn` = '{self._leaved_on}', + `LastModifiedAt` = '{self._modified_at}' + WHERE `UserId` = {self._user.user_id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `UserJoinedServers` + WHERE `Id` = {self._join_id}; + """) diff --git a/src/gismo_data/model/user_joined_voice_channel.py b/src/gismo_data/model/user_joined_voice_channel.py new file mode 100644 index 0000000..8d29d04 --- /dev/null +++ b/src/gismo_data/model/user_joined_voice_channel.py @@ -0,0 +1,121 @@ +from datetime import datetime + +from cpl_core.database import TableABC + +from gismo_data.model.user import User + + +class UserJoinedVoiceChannel(TableABC): + + def __init__(self, user: User, dc_channel_id: int, joined_on: datetime, leaved_on: datetime = None, created_at: datetime = None, modified_at: datetime = None, id=0): + self._join_id = id + self._dc_channel_id = dc_channel_id + self._user = user + self._joined_on = joined_on + self._leaved_on = leaved_on + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def join_id(self) -> int: + return self._join_id + + @property + def dc_channel_id(self) -> int: + return self._dc_channel_id + + @property + def user(self) -> User: + return self._user + + @property + def joined_on(self) -> datetime: + return self._joined_on + + @joined_on.setter + def joined_on(self, value: datetime): + self._modified_at = datetime.now() + self.joined_on = value + + @property + def leaved_on(self) -> datetime: + return self._leaved_on + + @leaved_on.setter + def leaved_on(self, value: datetime): + self._modified_at = datetime.now() + self._leaved_on = value + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `UserJoinedVoiceChannel`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedVoiceChannel` + WHERE `JoinId` = {id}; + """) + + @staticmethod + def get_select_by_user_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedVoiceChannel` + WHERE `UserId` = {id}; + """) + + @staticmethod + def get_select_active_by_user_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `UserJoinedVoiceChannel` + WHERE `UserId` = {id} + AND `LeavedOn` IS NULL; + """) + + @property + def insert_string(self) -> str: + if self._leaved_on is not None: + return str(f""" + INSERT INTO `UserJoinedVoiceChannel` ( + `UserId`, `DiscordChannelId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + {self._user.user_id}, + {self._dc_channel_id}, + '{self._joined_on}', + '{self._leaved_on}', + '{self._created_at}', + '{self._modified_at}' + ); + """) + else: + return str(f""" + INSERT INTO `UserJoinedVoiceChannel` ( + `UserId`, `DiscordChannelId`, `JoinedOn`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + {self._user.user_id}, + {self._dc_channel_id}, + '{self._joined_on}', + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `UserJoinedVoiceChannel` + SET `LeavedOn` = '{self._leaved_on}', + `LastModifiedAt` = '{self._modified_at}' + WHERE `JoinId` = {self._join_id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `UserJoinedVoiceChannel` + WHERE `JoinId` = {self._join_id}; + """) diff --git a/src/gismo_data/service/__init__.py b/src/gismo_data/service/__init__.py index 425ab6c..76e0775 100644 --- a/src/gismo_data/service/__init__.py +++ b/src/gismo_data/service/__init__.py @@ -1 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'gismo_data.service' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/gismo_data/service/client_repository_service.py b/src/gismo_data/service/client_repository_service.py new file mode 100644 index 0000000..fc2b9e7 --- /dev/null +++ b/src/gismo_data/service/client_repository_service.py @@ -0,0 +1,173 @@ +from typing import Optional +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_query.extension import List + +from gismo_data.abc.client_repository_abc import ClientRepositoryABC +from gismo_data.abc.server_repository_abc import ServerRepositoryABC +from gismo_data.model.client import Client + + +class ClientRepositoryService(ClientRepositoryABC): + + def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, servers: ServerRepositoryABC): + self._logger = logger + self._context = db_context + + self._servers = servers + + ClientRepositoryABC.__init__(self) + + def get_clients(self) -> List[Client]: + clients = List(Client) + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_all_string()}') + results = self._context.select(Client.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get client with id {result[0]}') + clients.append(Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + )) + + return clients + + def get_client_by_id(self, id: int) -> Client: + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_by_id_string(id)}') + result = self._context.select(Client.get_select_by_id_string(id)) + return Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + ) + + def get_client_by_discord_id(self, discord_id: int) -> Client: + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_by_discord_id_string(discord_id)}') + result = self._context.select(Client.get_select_by_discord_id_string(discord_id))[0] + return Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + ) + + def find_client_by_discord_id(self, discord_id: int) -> Optional[Client]: + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_by_discord_id_string(discord_id)}') + result = self._context.select(Client.get_select_by_discord_id_string(discord_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + ) + + def find_client_by_server_id(self, discord_id: int) -> Optional[Client]: + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_by_server_id_string(discord_id)}') + result = self._context.select(Client.get_select_by_server_id_string(discord_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + ) + + def find_client_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[Client]: + self._logger.trace(__name__, f'Send SQL command: {Client.get_select_by_discord_id_and_server_id_string(discord_id, server_id)}') + result = self._context.select(Client.get_select_by_discord_id_and_server_id_string(discord_id, server_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return Client( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + self._servers.get_server_by_id(result[7]), + id=result[0] + ) + + def add_client(self, client: Client): + self._logger.trace(__name__, f'Send SQL command: {client.insert_string}') + self._context.cursor.execute(client.insert_string) + + def update_client(self, client: Client): + self._logger.trace(__name__, f'Send SQL command: {client.udpate_string}') + self._context.cursor.execute(client.udpate_string) + + def delete_client(self, client: Client): + self._logger.trace(__name__, f'Send SQL command: {client.delete_string}') + self._context.cursor.execute(client.delete_string) + + def _get_client_and_server(self, id: int, server_id: int) -> Client: + server = self._servers.find_server_by_discord_id(server_id) + if server is None: + self._logger.warn(__name__, f'Cannot find server by id {server_id}') + raise Exception('Value not found') + + client = self.find_client_by_discord_id_and_server_id(id, server.server_id) + if client is None: + self._logger.warn(__name__, f'Cannot find client by ids {id}@{server.server_id}') + raise Exception('Value not found') + + return client + + def apppend_sent_message_count(self, id: int, server_id: int, value: int): + client = self._get_client_and_server(id, server_id) + client.sent_message_count += value + self.update_client(client) + + def apppend_received_message_count(self, id: int, server_id: int, value: int): + client = self._get_client_and_server(id, server_id) + client.received_message_count += value + self.update_client(client) + + def apppend_deleted_message_count(self, id: int, server_id: int, value: int): + client = self._get_client_and_server(id, server_id) + client.deleted_message_count += value + self.update_client(client) + + def apppend_received_command_count(self, id: int, server_id: int, value: int): + client = self._get_client_and_server(id, server_id) + client.received_command_count += value + self.update_client(client) + + def apppend_moved_users_count(self, id: int, server_id: int, value: int): + client = self._get_client_and_server(id, server_id) + client.moved_users_count += value + self.update_client(client) diff --git a/src/gismo_data/service/known_user_repository_service.py b/src/gismo_data/service/known_user_repository_service.py new file mode 100644 index 0000000..d85b44c --- /dev/null +++ b/src/gismo_data/service/known_user_repository_service.py @@ -0,0 +1,77 @@ +from typing import Optional +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_query.extension import List +from gismo_data.abc.known_user_repository_abc import KnownUserRepositoryABC + +from gismo_data.abc.server_repository_abc import ServerRepositoryABC +from gismo_data.model.known_user import KnownUser + + +class KnownUserRepositoryService(KnownUserRepositoryABC): + + def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, servers: ServerRepositoryABC): + self._logger = logger + self._context = db_context + + self._servers = servers + + KnownUserRepositoryABC.__init__(self) + + def get_users(self) -> List[KnownUser]: + users = List(KnownUser) + self._logger.trace(__name__, f'Send SQL command: {KnownUser.get_select_all_string()}') + results = self._context.select(KnownUser.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get known_user with id {result[0]}') + users.append(KnownUser( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + )) + + return users + + def get_user_by_id(self, id: int) -> KnownUser: + self._logger.trace(__name__, f'Send SQL command: {KnownUser.get_select_by_id_string(id)}') + result = self._context.select(KnownUser.get_select_by_id_string(id)) + return KnownUser( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + ) + + def get_user_by_discord_id(self, discord_id: int) -> KnownUser: + self._logger.trace(__name__, f'Send SQL command: {KnownUser.get_select_by_discord_id_string(discord_id)}') + result = self._context.select(KnownUser.get_select_by_discord_id_string(discord_id))[0] + return KnownUser( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + ) + + def find_user_by_discord_id(self, discord_id: int) -> Optional[KnownUser]: + self._logger.trace(__name__, f'Send SQL command: {KnownUser.get_select_by_discord_id_string(discord_id)}') + result = self._context.select(KnownUser.get_select_by_discord_id_string(discord_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return KnownUser( + result[1], + result[2], + result[3], + id=result[0] + ) + + def add_user(self, known_user: KnownUser): + self._logger.trace(__name__, f'Send SQL command: {known_user.insert_string}') + self._context.cursor.execute(known_user.insert_string) + + def delete_user(self, known_user: KnownUser): + self._logger.trace(__name__, f'Send SQL command: {known_user.delete_string}') + self._context.cursor.execute(known_user.delete_string) diff --git a/src/gismo_data/service/migration_service.py b/src/gismo_data/service/migration_service.py new file mode 100644 index 0000000..cd34dd3 --- /dev/null +++ b/src/gismo_data/service/migration_service.py @@ -0,0 +1,43 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.logging import LoggerABC + +from gismo_data.abc.migration_abc import MigrationABC +from gismo_data.model.migration_history import MigrationHistory + + +class MigrationService: + + def __init__(self, logger: LoggerABC, services: ServiceProviderABC, db: DatabaseContextABC): + self._logger = logger + self._services = services + + self._db = db + self._cursor = db.cursor + + self._migrations: list[MigrationABC] = MigrationABC.__subclasses__() + + def migrate(self): + self._logger.info(__name__, f"Running Migrations") + for migration in self._migrations: + migration_id = migration.__name__ + try: + # check if table exists + self._cursor.execute("SHOW TABLES LIKE 'MigrationHistory'") + result = self._cursor.fetchone() + if result: + # there is a table named "tableName" + self._logger.trace(__name__, f"Running SQL Command: {MigrationHistory.get_select_by_id_string(migration_id)}") + migration_from_db: MigrationHistory = self._db.select(MigrationHistory.get_select_by_id_string(migration_id)) + self._logger.trace(__name__, migration_from_db) + if migration_from_db is not None and len(migration_from_db) > 0: + continue + + self._logger.debug(__name__, f"Running Migration {migration}") + migration_as_service: MigrationABC = self._services.get_service(migration) + migration_as_service.upgrade() + self._cursor.execute(MigrationHistory(migration_id).insert_string) + self._db.save_changes() + + except Exception as e: + self._logger.error(__name__, f'Cannot get migration with id {migration}', e) diff --git a/src/gismo_data/service/server_repository_service.py b/src/gismo_data/service/server_repository_service.py index cac5685..6096390 100644 --- a/src/gismo_data/service/server_repository_service.py +++ b/src/gismo_data/service/server_repository_service.py @@ -37,7 +37,7 @@ class ServerRepositoryService(ServerRepositoryABC): def get_server_by_discord_id(self, discord_id: int) -> Server: self._logger.trace(__name__, f'Send SQL command: {Server.get_select_by_discord_id_string(discord_id)}') - result = self._context.select(Server.get_select_by_discord_id_string(discord_id)) + result = self._context.select(Server.get_select_by_discord_id_string(discord_id))[0] return Server( result[1], id=result[0] diff --git a/src/gismo_data/service/user_joined_server_repository_service.py b/src/gismo_data/service/user_joined_server_repository_service.py new file mode 100644 index 0000000..3cb5311 --- /dev/null +++ b/src/gismo_data/service/user_joined_server_repository_service.py @@ -0,0 +1,107 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_query.extension import List +from gismo_data.abc.user_joined_server_repository_abc import \ + UserJoinedServerRepositoryABC +from gismo_data.abc.user_repository_abc import UserRepositoryABC +from gismo_data.model.user import User +from gismo_data.model.user_joined_server import UserJoinedServer + + +class UserJoinedServerRepositoryService(UserJoinedServerRepositoryABC): + + def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, users: UserRepositoryABC): + self._logger = logger + self._context = db_context + + self._users = users + + UserJoinedServerRepositoryABC.__init__(self) + + def get_user_joined_servers(self) -> List[UserJoinedServer]: + joins = List(UserJoinedServer) + self._logger.trace(__name__, f'Send SQL command: {UserJoinedServer.get_select_all_string()}') + results = self._context.select(UserJoinedServer.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get user-joined-server with id {result[0]}') + joins.append(UserJoinedServer( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + )) + + return joins + + def get_user_joined_server_by_id(self, id: int) -> UserJoinedServer: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedServer.get_select_by_id_string(id)}') + result = self._context.select(UserJoinedServer.get_select_by_id_string(id))[0] + return UserJoinedServer( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def get_user_joined_servers_by_user_id(self, user_id: int) -> List[UserJoinedServer]: + joins = List(UserJoinedServer) + self._logger.trace(__name__, f'Send SQL command: {UserJoinedServer.get_select_by_user_id_string(user_id)}') + results = self._context.select(UserJoinedServer.get_select_by_user_id_string(user_id)) + for result in results: + joins.append(UserJoinedServer( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + )) + + return joins + + def get_active_user_joined_server_by_user_id(self, user_id: int) -> UserJoinedServer: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedServer.get_select_by_user_id_string(user_id)}') + result = self._context.select(UserJoinedServer.get_select_active_by_user_id_string(user_id))[0] + return UserJoinedServer( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def find_active_user_joined_server_by_user_id(self, user_id: int) -> Optional[UserJoinedServer]: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedServer.get_select_by_user_id_string(user_id)}') + result = self._context.select(UserJoinedServer.get_select_active_by_user_id_string(user_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return UserJoinedServer( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def add_user_joined_server(self, user_joined_server: UserJoinedServer): + self._logger.trace(__name__, f'Send SQL command: {user_joined_server.insert_string}') + self._context.cursor.execute(user_joined_server.insert_string) + + def update_user_joined_server(self, user_joined_server: UserJoinedServer): + self._logger.trace(__name__, f'Send SQL command: {user_joined_server.udpate_string}') + self._context.cursor.execute(user_joined_server.udpate_string) + + def delete_user_joined_server(self, user_joined_server: UserJoinedServer): + self._logger.trace(__name__, f'Send SQL command: {user_joined_server.delete_string}') + self._context.cursor.execute(user_joined_server.delete_string) diff --git a/src/gismo_data/service/user_joined_voice_channel_service.py b/src/gismo_data/service/user_joined_voice_channel_service.py new file mode 100644 index 0000000..35f999d --- /dev/null +++ b/src/gismo_data/service/user_joined_voice_channel_service.py @@ -0,0 +1,107 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_query.extension import List +from gismo_data.abc.user_repository_abc import UserRepositoryABC +from gismo_data.model.user import User +from gismo_data.model.user_joined_voice_channel import UserJoinedVoiceChannel + +from gismo_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC + + +class UserJoinedVoiceChannelRepositoryService(UserJoinedVoiceChannelRepositoryABC): + + def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, users: UserRepositoryABC): + self._logger = logger + self._context = db_context + + self._users = users + + UserJoinedVoiceChannelRepositoryABC.__init__(self) + + def get_user_joined_voice_channels(self) -> List[UserJoinedVoiceChannel]: + joins = List(UserJoinedVoiceChannel) + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_all_string()}') + results = self._context.select(UserJoinedVoiceChannel.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get user-joined-voice-channel with id {result[0]}') + joins.append(UserJoinedVoiceChannel( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + )) + + return joins + + def get_user_joined_voice_channel_by_id(self, id: int) -> UserJoinedVoiceChannel: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_by_id_string(id)}') + result = self._context.select(UserJoinedVoiceChannel.get_select_by_id_string(id))[0] + return UserJoinedVoiceChannel( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[UserJoinedVoiceChannel]: + joins = List(UserJoinedVoiceChannel) + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_by_user_id_string(user_id)}') + results = self._context.select(UserJoinedVoiceChannel.get_select_by_user_id_string(user_id)) + for result in results: + joins.append(UserJoinedVoiceChannel( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + )) + + return joins + + def get_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> UserJoinedVoiceChannel: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_by_user_id_string(user_id)}') + result = self._context.select(UserJoinedVoiceChannel.get_select_active_by_user_id_string(user_id))[0] + return UserJoinedVoiceChannel( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def find_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> Optional[UserJoinedVoiceChannel]: + self._logger.trace(__name__, f'Send SQL command: {UserJoinedVoiceChannel.get_select_by_user_id_string(user_id)}') + result = self._context.select(UserJoinedVoiceChannel.get_select_active_by_user_id_string(user_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return UserJoinedVoiceChannel( + self._users.get_user_by_id(result[1]), + result[2], + result[3], + result[4], + result[5], + id=result[0] + ) + + def add_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): + self._logger.trace(__name__, f'Send SQL command: {user_joined_voice_channel.insert_string}') + self._context.cursor.execute(user_joined_voice_channel.insert_string) + + def update_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): + self._logger.trace(__name__, f'Send SQL command: {user_joined_voice_channel.udpate_string}') + self._context.cursor.execute(user_joined_voice_channel.udpate_string) + + def delete_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel): + self._logger.trace(__name__, f'Send SQL command: {user_joined_voice_channel.delete_string}') + self._context.cursor.execute(user_joined_voice_channel.delete_string) diff --git a/src/gismo_data/service/user_repository_service.py b/src/gismo_data/service/user_repository_service.py index 6854313..7bb29ec 100644 --- a/src/gismo_data/service/user_repository_service.py +++ b/src/gismo_data/service/user_repository_service.py @@ -35,7 +35,31 @@ class UserRepositoryService(UserRepositoryABC): def get_user_by_id(self, id: int) -> User: self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_id_string(id)}') - result = self._context.select(User.get_select_by_id_string(id)) + result = self._context.select(User.get_select_by_id_string(id))[0] + + return User( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + ) + + def get_users_by_discord_id(self, discord_id: int) -> List[User]: + users = List(User) + self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_discord_id_string(discord_id)}') + results = self._context.select(User.get_select_by_discord_id_string(discord_id)) + for result in results: + users.append(User( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + )) + + def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: + self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_discord_id_and_server_id_string(discord_id, server_id)}') + result = self._context.select(User.get_select_by_discord_id_and_server_id_string(discord_id, server_id))[0] + return User( result[1], result[2], @@ -43,19 +67,9 @@ class UserRepositoryService(UserRepositoryABC): id=result[0] ) - def get_user_by_discord_id(self, discord_id: int) -> User: - self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_discord_id_string(discord_id)}') - result = self._context.select(User.get_select_by_discord_id_string(discord_id))[0] - return User( - result[1], - result[2], - self._servers.get_server_by_id(result[3]), - id=result[0] - ) - - def find_user_by_discord_id(self, discord_id: int) -> Optional[User]: - self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_discord_id_string(discord_id)}') - result = self._context.select(User.get_select_by_discord_id_string(discord_id)) + def find_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[User]: + self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_discord_id_and_server_id_string(discord_id, server_id)}') + result = self._context.select(User.get_select_by_discord_id_and_server_id_string(discord_id, server_id)) if result is None or len(result) == 0: return None diff --git a/src/modules/base/__init__.py b/src/modules/base/__init__.py new file mode 100644 index 0000000..307aa80 --- /dev/null +++ b/src/modules/base/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.base' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/modules/base/base.json b/src/modules/base/base.json new file mode 100644 index 0000000..7c5150a --- /dev/null +++ b/src/modules/base/base.json @@ -0,0 +1,43 @@ +{ + "ProjectSettings": { + "Name": "modules/base", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "sh_cpl-core>=2021.11.0.post1" + ], + "PythonVersion": ">=3.9.2", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "modules/base.main", + "EntryPoint": "modules/base", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/src/modules/base/base.py b/src/modules/base/base.py new file mode 100644 index 0000000..5fd7892 --- /dev/null +++ b/src/modules/base/base.py @@ -0,0 +1,262 @@ +from datetime import datetime +from typing import Union + +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging.logger_abc import LoggerABC + +from gismo_core.abc.bot_service_abc import BotServiceABC +from gismo_core.abc.message_service_abc import MessageServiceABC +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.server_repository_abc import ServerRepositoryABC +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.known_user import KnownUser +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_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 +from modules_core.abc.events.on_voice_state_update_abc import \ + OnVoiceStateUpdateABC +from modules_core.abc.module_abc import ModuleABC + + +class Base(ModuleABC, OnMemberJoinABC, OnMemberRemoveABC, OnMessageABC, OnVoiceStateUpdateABC): + + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + clients: ClientRepositoryABC, + servers: ServerRepositoryABC, + known_users: KnownUserRepositoryABC, + users: UserRepositoryABC, + user_joins: UserJoinedServerRepositoryABC, + user_joins_vc: UserJoinedVoiceChannelRepositoryABC, + bot: BotServiceABC, + db: DatabaseContextABC, + messenger: MessageServiceABC + ): + self._config = config + self._logger = logger + self._clients = clients + self._servers = servers + self._known_users = known_users + self._users = users + self._user_joins = user_joins + self._user_joins_vc = user_joins_vc + self._bot = bot + self._db = db + self._messenger = messenger + + ModuleABC.__init__(self) + self._priorities[OnMemberJoinABC] = 1 + self._priorities[OnMemberRemoveABC] = 1 + self._priorities[OnMessageABC] = 30 + self._priorities[OnVoiceStateUpdateABC] = 10 + self._logger.trace(__name__, f'Module {type(self)} loaded') + + def _apppend_received_message_count(self, g_id: int): + try: + self._clients.apppend_received_message_count(self._bot.user.id, g_id, 1) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot edit client {self._bot.user.id}@{g_id}') + + def _apppend_deleted_message_count(self, g_id: int): + try: + self._clients.apppend_received_message_count(self._bot.user.id, g_id, 1) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot edit client {self._bot.user.id}@{g_id}') + + def _check_for_known_user(self, member: Union[discord.User, discord.Member]): + self._logger.debug(__name__, f'Check if user is already known {member}') + try: + user = self._known_users.find_user_by_discord_id(member.id) + if user is not None: + return + + self._logger.debug(__name__, f'Add user: {member.id}') + self._known_users.add_user(KnownUser(member.id)) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot get user {member.id}', e) + + async def _add_if_not_exists_user(self, member: Union[discord.User, discord.Member]): + self._logger.debug(__name__, f'Check if user exists {member}') + settings: ServerSettings = self._config.get_configuration(f'DSERVER_{member.guild.id}') + 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 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) + + try: + server = self._servers.get_server_by_discord_id(member.guild.id) + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) + if user is not None: + self._user_joins.add_user_joined_server(UserJoinedServer(user, datetime.now())) + return + + self._logger.debug(__name__, f'Add user: {member.id}') + self._users.add_user(User(member.id, 0, server)) + self._db.save_changes() + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + self._user_joins.add_user_joined_server(UserJoinedServer(user, datetime.now())) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot get user {member.id}', e) + + async def _remove_user(self, member: Union[discord.User, discord.Member]): + self._logger.debug(__name__, f'Remove user {member}') + settings: ServerSettings = self._config.get_configuration(f'DSERVER_{member.guild.id}') + await self._messenger.send_dm_message(settings.goodbye_message, member) + + try: + server = self._servers.get_server_by_discord_id(member.guild.id) + + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) + if user is None: + self._logger.error(__name__, f'Cannot find user {member}') + return + + join = self._user_joins.get_active_user_joined_server_by_user_id(user.user_id) + join.leaved_on = datetime.now() + self._user_joins.update_user_joined_server(join) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot get user {member.id}', e) + + def _update_voice_state(self, joined: bool, dc_user_id: int, dc_channel_id: int, srv_id: int): + user: User = None + try: + user = self._users.get_user_by_discord_id_and_server_id(dc_user_id, srv_id) + except Exception as e: + self._logger.error(__name__, f'Cannot get user {dc_user_id}', e) + return + + if user is None: + self._logger.error(__name__, f'User not found {dc_user_id}') + return + + try: + if joined: + join = UserJoinedVoiceChannel(user, dc_channel_id, datetime.now()) + self._user_joins_vc.add_user_joined_voice_channel(join) + self._db.save_changes() + return + + server_st: ServerSettings = self._config.get_configuration(f'DSERVER_{user.server.discord_server_id}') + + join = self._user_joins_vc.get_active_user_joined_voice_channel_by_user_id(user.user_id) + join.leaved_on = datetime.now() + + # ontime as hours + ontime = round((join.leaved_on - join.joined_on).total_seconds()/3600, 2) + old_xp = user.xp + user.xp += round(ontime * server_st.xp_per_ontime_hour) + + self._user_joins_vc.update_user_joined_voice_channel(join) + self._users.update_user(user) + self._db.save_changes() + + self._logger.debug(__name__, f'User {user} leaved_on {join.leaved_on}. Ontime: {ontime}h | xp: from {old_xp} to {user.xp}') + except Exception as e: + self._logger.error(__name__, f'Ontime validation failed', e) + + def _handle_message_for_xp(self, message: discord.Message): + dc_user_id = message.author.id + try: + server = self._servers.get_server_by_discord_id(message.guild.id) + except Exception as e: + self._logger.error(__name__, f'Cannot get server {message.guild.id}', e) + return + + user: User = None + try: + user = self._users.get_user_by_discord_id_and_server_id(dc_user_id, server.server_id) + except Exception as e: + self._logger.error(__name__, f'Cannot get user {dc_user_id}', e) + return + + if user is None: + self._logger.error(__name__, f'User not found {dc_user_id}') + return + + server_st: ServerSettings = self._config.get_configuration(f'DSERVER_{user.server.discord_server_id}') + old_xp = user.xp + user.xp += server_st._xp_per_message + self._users.update_user(user) + self._db.save_changes() + + self._logger.debug(__name__, f'User {user} sent message. xp: from {old_xp} to {user.xp}') + + async def on_member_join(self, member: Union[discord.User, discord.Member]): + self._logger.debug(__name__, f'Module {type(self)} started') + self._check_for_known_user(member) + await self._add_if_not_exists_user(member) + + async def on_member_remove(self, member: Union[discord.User, discord.Member]): + self._logger.debug(__name__, f'Module {type(self)} started') + await self._remove_user(member) + + 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: + return + self._apppend_received_message_count(message.guild.id) + + if not message.author.bot: + self._handle_message_for_xp(message) + + async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + self._logger.debug(__name__, f'Module {type(self)} started') + self._logger.trace(__name__, f'Detected on_voice_state_update {member.id} from {before} to {after}') + u: discord.User = member + server_st: ServerSettings = self._config.get_configuration(f'DSERVER_{member.guild.id}') + server = self._servers.get_server_by_discord_id(member.guild.id) + + try: + # join + if before.channel is None and after.channel is not None and after.channel.id not in server_st.afk_channel_ids: + self._logger.trace(__name__, f'User {u.id} joined {after.channel}') + self._update_voice_state(True, member.id, after.channel.id, server.server_id) + + # leave + elif before.channel is not None and after.channel is None and before.channel.id not in server_st.afk_channel_ids: + self._logger.trace(__name__, f'User {u.id} left {before.channel}') + self._update_voice_state(False, member.id, before.channel.id, server.server_id) + + # channel to channel + elif before.channel is not None and after.channel is not None: + # joined + if before.channel.id in server_st.afk_channel_ids and after.channel.id not in server_st.afk_channel_ids: + self._logger.trace(__name__, f'User {u.id} joined {after.channel}') + self._update_voice_state(True, member.id, after.channel.id, server.server_id) + + # left + elif after.channel.id in server_st.afk_channel_ids and before.channel.id not in server_st.afk_channel_ids: + self._logger.trace(__name__, f'User {u.id} left {before.channel}') + self._update_voice_state(False, member.id, before.channel.id, server.server_id) + + else: + self._logger.trace(__name__, f'User {u.id} switched to {after.channel}') + except Exception as e: + self._logger.error(__name__, f'Cannot handle voice state for user {u.id}', e) diff --git a/src/modules/boot_log/__init__.py b/src/modules/boot_log/__init__.py index a6127ce..d6b52d8 100644 --- a/src/modules/boot_log/__init__.py +++ b/src/modules/boot_log/__init__.py @@ -15,11 +15,11 @@ __title__ = 'modules.boot_log' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/modules/database/__init__.py b/src/modules/database/__init__.py index ad5eca3..3e9733f 100644 --- a/src/modules/database/__init__.py +++ b/src/modules/database/__init__.py @@ -1 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +gismo sh-edraft Gismo +~~~~~~~~~~~~~~~~~~~ + +sh-edraft Dicord bot Gismo + +:copyright: (c) 2021 - 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.database' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' +__version__ = '0.3.0' + +from collections import namedtuple + # imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/modules/database/database.py b/src/modules/database/database.py index 7dd1569..65730df 100644 --- a/src/modules/database/database.py +++ b/src/modules/database/database.py @@ -1,6 +1,5 @@ -import asyncio +from ctypes import Union from datetime import datetime -import time import discord from cpl_core.configuration import ConfigurationABC @@ -8,11 +7,19 @@ 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.abc.message_service_abc import MessageServiceABC 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_core.abc.events.on_ready_abc import OnReadyABC from modules_core.abc.module_abc import ModuleABC @@ -27,7 +34,11 @@ class Database(ModuleABC, OnReadyABC): bot: BotServiceABC, db_context: DatabaseContextABC, server_repo: ServerRepositoryABC, - user_repo: UserRepositoryABC + user_repo: UserRepositoryABC, + client_repo: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + user_joins: UserJoinedServerRepositoryABC, + user_joins_vc: UserJoinedVoiceChannelRepositoryABC ): self._config = config @@ -36,7 +47,11 @@ class Database(ModuleABC, OnReadyABC): self._db_context = db_context self._servers = server_repo self._users = user_repo - + self._clients = client_repo + self._known_users = known_users + self._user_joins = user_joins + self._user_joins_vc = user_joins_vc + ModuleABC.__init__(self) self._priorities[OnReadyABC] = 0 self._logger.trace(__name__, f'Module {type(self)} loaded') @@ -60,18 +75,49 @@ class Database(ModuleABC, OnReadyABC): self._logger.error(__name__, 'Database init time calculation failed', e) return + def _check_known_users(self): + self._logger.debug(__name__, f'Start checking KnownUsers table, {len(self._bot.users)}') + for u in self._bot.users: + u: discord.User = u + try: + if u.bot: + self._logger.trace(__name__, f'User {u.id} is ignored, because its a bot') + continue + + user = self._known_users.find_user_by_discord_id(u.id) + if user is not None: + continue + + self._logger.warn(__name__, f'Unknown user: {u.id}') + self._logger.debug(__name__, f'Add user: {u.id}') + self._known_users.add_user(KnownUser(u.id)) + self._db_context.save_changes() + + user = self._known_users.find_user_by_discord_id(u.id) + if user is None: + self._logger.fatal(__name__, f'Cannot add user: {u.id}') + + self._logger.debug(__name__, f'Added user: {u.id}') + except Exception as e: + self._logger.error(__name__, f'Cannot get user', e) + def _check_servers(self): + self._logger.debug(__name__, f'Start checking Servers table') for g in self._bot.guilds: g: discord.Guild = g try: server = self._servers.find_server_by_discord_id(g.id) if server is not None: - return + continue self._logger.warn(__name__, f'Server not found in database: {g.id}') self._logger.debug(__name__, f'Add server: {g.id}') self._servers.add_server(Server(g.id)) self._db_context.save_changes() + + server = self._servers.find_server_by_discord_id(g.id) + if server is None: + self._logger.fatal(__name__, f'Cannot add server: {g.id}') self._logger.debug(__name__, f'Added server: {g.id}') except Exception as e: @@ -80,8 +126,39 @@ class Database(ModuleABC, OnReadyABC): results = self._servers.get_servers() if results is None or len(results) == 0: self._logger.error(__name__, f'Table Servers is empty!') + + def _check_clients(self): + self._logger.debug(__name__, f'Start checking Clients table') + for g in self._bot.guilds: + g: discord.Guild = g + try: + server: Server = self._servers.find_server_by_discord_id(g.id) + if server is None: + self._logger.fatal(__name__, f'Server not found in database: {g.id}') + + client = self._clients.find_client_by_server_id(server.server_id) + if client is not None: + continue + + self._logger.warn(__name__, f'Client for server {g.id} not found in database: {self._bot.user.id}') + self._logger.debug(__name__, f'Add client: {self._bot.user.id}') + self._clients.add_client(Client(self._bot.user.id, 0, 0, 0, 0, 0, server)) + self._db_context.save_changes() + + client = self._clients.find_client_by_server_id(server.server_id) + if client is None: + self._logger.fatal(__name__, f'Cannot add client {self._bot.user.id} for server {g.id}') + + self._logger.debug(__name__, f'Added client: {g.id}') + except Exception as e: + self._logger.error(__name__, f'Cannot get client', e) + + results = self._servers.get_servers() + if results is None or len(results) == 0: + self._logger.error(__name__, f'Table Servers is empty!') def _check_users(self): + self._logger.debug(__name__, f'Start checking Users table') for g in self._bot.guilds: g: discord.Guild = g @@ -89,13 +166,16 @@ class Database(ModuleABC, OnReadyABC): server = self._servers.find_server_by_discord_id(g.id) if server is None: self._logger.fatal(__name__, f'Server not found in database: {g.id}') - break for u in g.members: - u: discord.Member = u - user = self._users.find_user_by_discord_id(u.id) + u: Union[discord.Member, discord.User] = u + if u.bot: + self._logger.trace(__name__, f'User {u.id} is ignored, because its a bot') + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.server_id) if user is not None: - break + continue self._logger.warn(__name__, f'User not found in database: {u.id}') self._logger.debug(__name__, f'Add user: {u.id}') @@ -110,11 +190,129 @@ class Database(ModuleABC, OnReadyABC): if results is None or len(results) == 0: self._logger.error(__name__, f'Table Users is empty!') + def _check_user_joins(self): + self._logger.debug(__name__, f'Start checking UserJoinedServers table') + for guild in self._bot.guilds: + guild: discord.Guild = guild + + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f'Server not found in database: {guild.id}') + + try: + for u in guild.members: + u: discord.User = u + if u.bot: + self._logger.trace(__name__, f'User {u.id} is ignored, because its a bot') + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.server_id) + if user is None: + self._logger.fatal(__name__, f'User not found in database: {u.id}') + + join = self._user_joins.find_active_user_joined_server_by_user_id(user.user_id) + if join is not None: + continue + + m: discord.Member = u + self._logger.warn(__name__, f'Active UserJoinedServer not found in database: {guild.id}:{u.id}@{m.joined_at}') + self._logger.debug(__name__, f'Add UserJoinedServer: {guild.id}:{u.id}@{m.joined_at}') + self._user_joins.add_user_joined_server(UserJoinedServer(user, m.joined_at, None)) + self._db_context.save_changes() + + self._logger.debug(__name__, f'Added UserJoinedServer: {u.id}') + except Exception as e: + self._logger.error(__name__, f'Cannot get UserJoinedServer', e) + + results = self._users.get_users() + if results is None or len(results) == 0: + self._logger.error(__name__, f'Table Users is empty!') + + + joins = self._user_joins.get_user_joined_servers() + for join in joins: + join: UserJoinedServer = join + if join.user.server.discord_server_id != guild.id: + continue + + if join.leaved_on is not None: + continue + + dc_user = guild.get_member(join.user.discord_id) + if dc_user is None: + self._logger.warn(__name__, f'User {join.user.discord_id} already left the server.') + join.leaved_on = datetime.now() + self._user_joins.update_user_joined_server(join) + + self._db_context.save_changes() + + def _check_user_joins_vc(self): + self._logger.debug(__name__, f'Start checking UserJoinedVoiceChannel table') + for guild in self._bot.guilds: + guild: discord.Guild = guild + + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + self._logger.fatal(__name__, f'Server not found in database: {guild.id}') + + try: + for u in guild.members: + u: discord.User = u + if u.bot: + self._logger.trace(__name__, f'User {u.id} is ignored, because its a bot') + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.server_id) + if user is None: + self._logger.fatal(__name__, f'User not found in database: {u.id}') + + join = self._user_joins_vc.find_active_user_joined_voice_channel_by_user_id(user.user_id) + if join is None: + continue + + m: discord.Member = u + self._logger.warn(__name__, f'Active UserJoinedVoiceChannel found in database: {guild.id}:{u.id}@{m.joined_at}') + join.leaved_on = datetime.now() + server_st: ServerSettings = self._config.get_configuration(f'DSERVER_{guild.id}') + + if ((join.leaved_on - join.joined_on).total_seconds()/60/60) > server_st.max_voice_state_hours: + join.leaved_on = join.joined_on + datetime.timedelta(hours=server_st.max_voice_state_hours) + + self._user_joins_vc.update_user_joined_voice_channel(join) + # todo: maybe add XP + self._db_context.save_changes() + + for u in guild.members: + if u.bot: + self._logger.trace(__name__, f'User {u.id} is ignored, because its a bot') + continue + + m: discord.Member = u + + if m.voice is None: + continue + + user = self._users.find_user_by_discord_id_and_server_id(u.id, server.server_id) + if user is None: + self._logger.fatal(__name__, f'User not found in database: {u.id}') + + join = UserJoinedVoiceChannel(user, m.voice.channel.id, datetime.now()) + self._user_joins_vc.add_user_joined_voice_channel(join) + self._db_context.save_changes() + self._logger.warn(__name__, f'VS {m.voice}') + + except Exception as e: + self._logger.error(__name__, f'Cannot get UserJoinedVoiceChannel', e) + async def on_ready(self): self._logger.debug(__name__, f'Module {type(self)} started') + self._check_known_users() self._check_servers() + self._check_clients() self._check_users() + self._check_user_joins() + self._check_user_joins_vc() self._validate_init_time() - self._logger.trace(__name__, f'Module {type(self)} stopped') \ No newline at end of file + self._logger.trace(__name__, f'Module {type(self)} stopped') diff --git a/src/modules_core/__init__.py b/src/modules_core/__init__.py index 60a2666..88a2e59 100644 --- a/src/modules_core/__init__.py +++ b/src/modules_core/__init__.py @@ -15,11 +15,11 @@ __title__ = 'modules_core' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/modules_core/abc/__init__.py b/src/modules_core/abc/__init__.py index 235e917..4e57337 100644 --- a/src/modules_core/abc/__init__.py +++ b/src/modules_core/abc/__init__.py @@ -15,11 +15,11 @@ __title__ = 'modules_core.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/src/modules_core/service/__init__.py b/src/modules_core/service/__init__.py index b5b68cb..bf3e373 100644 --- a/src/modules_core/service/__init__.py +++ b/src/modules_core/service/__init__.py @@ -15,11 +15,11 @@ __title__ = 'modules_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2021 - 2022 sh-edraft.de' -__version__ = '0.2.0' +__version__ = '0.3.0' from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='0') +version_info = VersionInfo(major='0', minor='3', micro='0')