forked from sh-edraft.de/sh_discord_bot
Reviewed-on: sh-edraft.de/kd_discord_bot#102 Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com> Closes #46
This commit is contained in:
commit
2483faef01
@ -3,6 +3,7 @@
|
|||||||
"DefaultProject": "bot",
|
"DefaultProject": "bot",
|
||||||
"Projects": {
|
"Projects": {
|
||||||
"bot": "src/bot/bot.json",
|
"bot": "src/bot/bot.json",
|
||||||
|
"bot-api": "src/bot_api/bot-api.json",
|
||||||
"bot-core": "src/bot_core/bot-core.json",
|
"bot-core": "src/bot_core/bot-core.json",
|
||||||
"bot-data": "src/bot_data/bot-data.json",
|
"bot-data": "src/bot_data/bot-data.json",
|
||||||
"auto-role": "src/modules/auto_role/auto-role.json",
|
"auto-role": "src/modules/auto_role/auto-role.json",
|
||||||
@ -11,7 +12,7 @@
|
|||||||
"database": "src/modules/database/database.json",
|
"database": "src/modules/database/database.json",
|
||||||
"level": "src/modules/level/level.json",
|
"level": "src/modules/level/level.json",
|
||||||
"permission": "src/modules/permission/permission.json",
|
"permission": "src/modules/permission/permission.json",
|
||||||
"bot-api": "src/bot_api/bot-api.json",
|
"stats": "src/modules/stats/stats.json",
|
||||||
"get-version": "tools/get_version/get-version.json",
|
"get-version": "tools/get_version/get-version.json",
|
||||||
"post-build": "tools/post_build/post-build.json",
|
"post-build": "tools/post_build/post-build.json",
|
||||||
"set-version": "tools/set_version/set-version.json"
|
"set-version": "tools/set_version/set-version.json"
|
||||||
|
@ -10,6 +10,7 @@ from modules.boot_log.boot_log_module import BootLogModule
|
|||||||
from modules.database.database_module import DatabaseModule
|
from modules.database.database_module import DatabaseModule
|
||||||
from modules.level.level_module import LevelModule
|
from modules.level.level_module import LevelModule
|
||||||
from modules.permission.permission_module import PermissionModule
|
from modules.permission.permission_module import PermissionModule
|
||||||
|
from modules.stats.stats_module import StatsModule
|
||||||
|
|
||||||
|
|
||||||
class ModuleList:
|
class ModuleList:
|
||||||
@ -26,6 +27,7 @@ class ModuleList:
|
|||||||
LevelModule,
|
LevelModule,
|
||||||
PermissionModule,
|
PermissionModule,
|
||||||
ApiModule,
|
ApiModule,
|
||||||
|
StatsModule,
|
||||||
# has to be last!
|
# has to be last!
|
||||||
BootLogModule,
|
BootLogModule,
|
||||||
CoreExtensionModule,
|
CoreExtensionModule,
|
||||||
|
@ -8,6 +8,7 @@ from bot_data.migration.api_migration import ApiMigration
|
|||||||
from bot_data.migration.auto_role_migration import AutoRoleMigration
|
from bot_data.migration.auto_role_migration import AutoRoleMigration
|
||||||
from bot_data.migration.initial_migration import InitialMigration
|
from bot_data.migration.initial_migration import InitialMigration
|
||||||
from bot_data.migration.level_migration import LevelMigration
|
from bot_data.migration.level_migration import LevelMigration
|
||||||
|
from bot_data.migration.stats_migration import StatsMigration
|
||||||
from bot_data.service.migration_service import MigrationService
|
from bot_data.service.migration_service import MigrationService
|
||||||
|
|
||||||
|
|
||||||
@ -25,3 +26,4 @@ class StartupMigrationExtension(StartupExtensionABC):
|
|||||||
services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2
|
services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2
|
||||||
services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0
|
services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0
|
||||||
services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0
|
services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0
|
||||||
|
services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0
|
||||||
|
@ -217,7 +217,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"database": {},
|
"database": {},
|
||||||
"permission": {
|
"permission": {},
|
||||||
|
"stats": {
|
||||||
|
"list": {
|
||||||
|
"statistic": "Statistik",
|
||||||
|
"description": "Beschreibung",
|
||||||
|
"nothing_found": "Keine Statistiken gefunden."
|
||||||
|
},
|
||||||
|
"view": {
|
||||||
|
"statistic": "Statistik",
|
||||||
|
"description": "Beschreibung",
|
||||||
|
"failed": "Statistik kann nicht gezeigt werden :("
|
||||||
|
},
|
||||||
|
"add": {
|
||||||
|
"failed": "Statistik kann nicht hinzugefügt werden :(",
|
||||||
|
"success": "Statistik wurde hinzugefügt :D"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"failed": "Statistik kann nicht bearbeitet werden :(",
|
||||||
|
"success": "Statistik wurde gespeichert :D"
|
||||||
|
},
|
||||||
|
"remove": {
|
||||||
|
"failed": "Statistik kann nicht gelöscht werden :(",
|
||||||
|
"success": "Statistik wurde gelöscht :D"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
|
@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'bot_api.abc'
|
__title__ = 'bot_api.service'
|
||||||
__author__ = 'Sven Heidemann'
|
__author__ = 'Sven Heidemann'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||||
|
@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'bot_core.abc'
|
__title__ = 'bot_core.service'
|
||||||
__author__ = 'Sven Heidemann'
|
__author__ = 'Sven Heidemann'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||||
|
@ -3,6 +3,7 @@ from typing import Union
|
|||||||
|
|
||||||
import discord
|
import discord
|
||||||
from cpl_query.extension import List
|
from cpl_query.extension import List
|
||||||
|
from discord import Interaction
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
|
||||||
@ -10,18 +11,21 @@ class MessageServiceABC(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self): pass
|
def __init__(self): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass
|
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def delete_message(self, message: discord.Message, without_tracking=False): pass
|
async def delete_message(self, message: discord.Message, without_tracking=False): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass
|
async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass
|
async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
||||||
|
@ -2,7 +2,6 @@ from enum import Enum
|
|||||||
|
|
||||||
|
|
||||||
class FeatureFlagsEnum(Enum):
|
class FeatureFlagsEnum(Enum):
|
||||||
|
|
||||||
# modules
|
# modules
|
||||||
api_module = 'ApiModule'
|
api_module = 'ApiModule'
|
||||||
admin_module = 'AdminModule'
|
admin_module = 'AdminModule'
|
||||||
@ -16,6 +15,7 @@ class FeatureFlagsEnum(Enum):
|
|||||||
level_module = 'LevelModule'
|
level_module = 'LevelModule'
|
||||||
moderator_module = 'ModeratorModule'
|
moderator_module = 'ModeratorModule'
|
||||||
permission_module = 'PermissionModule'
|
permission_module = 'PermissionModule'
|
||||||
|
stats_module = 'StatsModule'
|
||||||
# features
|
# features
|
||||||
api_only = 'ApiOnly'
|
api_only = 'ApiOnly'
|
||||||
presence = 'Presence'
|
presence = 'Presence'
|
||||||
|
@ -25,6 +25,7 @@ class FeatureFlagsSettings(ConfigurationModelABC):
|
|||||||
FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48
|
FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48
|
||||||
FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48
|
FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48
|
||||||
FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48
|
FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48
|
||||||
|
FeatureFlagsEnum.stats_module.value: True, # 08.11.2022 #46
|
||||||
# features
|
# features
|
||||||
FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70
|
FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70
|
||||||
FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56
|
FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56
|
||||||
|
@ -6,6 +6,7 @@ from cpl_core.configuration.configuration_abc import ConfigurationABC
|
|||||||
from cpl_core.database.context.database_context_abc import DatabaseContextABC
|
from cpl_core.database.context.database_context_abc import DatabaseContextABC
|
||||||
from cpl_discord.service import DiscordBotServiceABC
|
from cpl_discord.service import DiscordBotServiceABC
|
||||||
from cpl_query.extension import List
|
from cpl_query.extension import List
|
||||||
|
from discord import Interaction
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
from bot_core.abc.message_service_abc import MessageServiceABC
|
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||||
@ -117,3 +118,31 @@ class MessageService(MessageServiceABC):
|
|||||||
|
|
||||||
if ctx.guild is not None:
|
if ctx.guild is not None:
|
||||||
await self.delete_message(msg, without_tracking)
|
await self.delete_message(msg, without_tracking)
|
||||||
|
|
||||||
|
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False):
|
||||||
|
if interaction is None:
|
||||||
|
self._logger.warn(__name__, 'Message context is empty')
|
||||||
|
self._logger.debug(__name__, f'Message: {message}')
|
||||||
|
return
|
||||||
|
|
||||||
|
self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}')
|
||||||
|
try:
|
||||||
|
if isinstance(message, discord.Embed):
|
||||||
|
await interaction.response.send_message(embed=message)
|
||||||
|
else:
|
||||||
|
await interaction.response.send_message(message)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e)
|
||||||
|
else:
|
||||||
|
self._logger.info(__name__, f'Sent message to channel {interaction.channel.id}')
|
||||||
|
if not without_tracking and interaction.guild is not None:
|
||||||
|
self._clients.append_sent_message_count(self._bot.user.id, interaction.guild.id, 1)
|
||||||
|
self._db.save_changes()
|
||||||
|
|
||||||
|
if wait_before_delete is not None:
|
||||||
|
await asyncio.sleep(wait_before_delete)
|
||||||
|
|
||||||
|
if is_persistent:
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.delete_message(await interaction.original_response(), without_tracking)
|
||||||
|
@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'bot_data.abc'
|
__title__ = 'bot_data.service'
|
||||||
__author__ = 'Sven Heidemann'
|
__author__ = 'Sven Heidemann'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||||
|
36
kdb-bot/src/bot_data/abc/statistic_repository_abc.py
Normal file
36
kdb-bot/src/bot_data/abc/statistic_repository_abc.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from cpl_query.extension import List
|
||||||
|
|
||||||
|
from bot_data.model.statistic import Statistic
|
||||||
|
|
||||||
|
|
||||||
|
class StatisticRepositoryABC(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __init__(self): pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_statistics(self) -> List[Statistic]: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_statistic_by_id(self, id: int) -> Statistic: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_statistic_by_name(self, name: str, server_id: int) -> Statistic: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_statistic(self, statistic: Statistic): pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_statistic(self, statistic: Statistic): pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete_statistic(self, statistic: Statistic): pass
|
@ -11,6 +11,7 @@ from bot_data.abc.client_repository_abc import ClientRepositoryABC
|
|||||||
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
|
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
|
||||||
from bot_data.abc.level_repository_abc import LevelRepositoryABC
|
from bot_data.abc.level_repository_abc import LevelRepositoryABC
|
||||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
|
from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC
|
||||||
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
|
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
|
||||||
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
|
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
|
||||||
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
||||||
@ -21,6 +22,7 @@ from bot_data.service.known_user_repository_service import KnownUserRepositorySe
|
|||||||
from bot_data.service.level_repository_service import LevelRepositoryService
|
from bot_data.service.level_repository_service import LevelRepositoryService
|
||||||
from bot_data.service.seeder_service import SeederService
|
from bot_data.service.seeder_service import SeederService
|
||||||
from bot_data.service.server_repository_service import ServerRepositoryService
|
from bot_data.service.server_repository_service import ServerRepositoryService
|
||||||
|
from bot_data.service.statistic_repository_service import StatisticRepositoryService
|
||||||
from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService
|
from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService
|
||||||
from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService
|
from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService
|
||||||
from bot_data.service.user_repository_service import UserRepositoryService
|
from bot_data.service.user_repository_service import UserRepositoryService
|
||||||
@ -44,5 +46,6 @@ class DataModule(ModuleABC):
|
|||||||
services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService)
|
services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService)
|
||||||
services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService)
|
services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService)
|
||||||
services.add_transient(LevelRepositoryABC, LevelRepositoryService)
|
services.add_transient(LevelRepositoryABC, LevelRepositoryService)
|
||||||
|
services.add_transient(StatisticRepositoryABC, StatisticRepositoryService)
|
||||||
|
|
||||||
services.add_transient(SeederService)
|
services.add_transient(SeederService)
|
||||||
|
36
kdb-bot/src/bot_data/migration/stats_migration.py
Normal file
36
kdb-bot/src/bot_data/migration/stats_migration.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from bot_core.logging.database_logger import DatabaseLogger
|
||||||
|
from bot_data.abc.migration_abc import MigrationABC
|
||||||
|
from bot_data.db_context import DBContext
|
||||||
|
|
||||||
|
|
||||||
|
class StatsMigration(MigrationABC):
|
||||||
|
name = '0.3_StatsMigration'
|
||||||
|
|
||||||
|
def __init__(self, logger: DatabaseLogger, db: DBContext):
|
||||||
|
MigrationABC.__init__(self)
|
||||||
|
self._logger = logger
|
||||||
|
self._db = db
|
||||||
|
self._cursor = db.cursor
|
||||||
|
|
||||||
|
def upgrade(self):
|
||||||
|
self._logger.debug(__name__, 'Running upgrade')
|
||||||
|
|
||||||
|
self._cursor.execute(
|
||||||
|
str(f"""
|
||||||
|
CREATE TABLE IF NOT EXISTS `Statistics` (
|
||||||
|
`Id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`Name` VARCHAR(255) NOT NULL,
|
||||||
|
`Description` VARCHAR(255) NOT NULL,
|
||||||
|
`Code` LONGTEXT NOT NULL,
|
||||||
|
`ServerId` BIGINT,
|
||||||
|
`CreatedAt` DATETIME(6),
|
||||||
|
`LastModifiedAt` DATETIME(6),
|
||||||
|
PRIMARY KEY(`Id`),
|
||||||
|
FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`)
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
)
|
||||||
|
|
||||||
|
def downgrade(self):
|
||||||
|
self._cursor.execute('DROP TABLE `Statistics`;')
|
||||||
|
|
109
kdb-bot/src/bot_data/model/statistic.py
Normal file
109
kdb-bot/src/bot_data/model/statistic.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from cpl_core.database import TableABC
|
||||||
|
from cpl_core.utils import CredentialManager
|
||||||
|
|
||||||
|
from bot_data.model.server import Server
|
||||||
|
|
||||||
|
|
||||||
|
class Statistic(TableABC):
|
||||||
|
|
||||||
|
def __init__(self, name: str, description: str, code: str, server: Server, created_at: datetime=None, modified_at: datetime=None, id=0):
|
||||||
|
self._id = id
|
||||||
|
self._name = name
|
||||||
|
self._description = description
|
||||||
|
self._code = CredentialManager.encrypt(code)
|
||||||
|
self._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 id(self) -> int:
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return self._description
|
||||||
|
|
||||||
|
@description.setter
|
||||||
|
def description(self, value: str):
|
||||||
|
self._description = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code(self) -> str:
|
||||||
|
return CredentialManager.decrypt(self._code)
|
||||||
|
|
||||||
|
@code.setter
|
||||||
|
def code(self, value: str):
|
||||||
|
self._code = CredentialManager.encrypt(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server(self) -> Server:
|
||||||
|
return self._server
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_select_all_string() -> str:
|
||||||
|
return str(f"""
|
||||||
|
SELECT * FROM `Statistics`;
|
||||||
|
""")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_select_by_id_string(id: int) -> str:
|
||||||
|
return str(f"""
|
||||||
|
SELECT * FROM `Statistics`
|
||||||
|
WHERE `Id` = {id};
|
||||||
|
""")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_select_by_name_string(name: str, s_id: int) -> str:
|
||||||
|
return str(f"""
|
||||||
|
SELECT * FROM `Statistics`
|
||||||
|
WHERE `ServerId` = {s_id}
|
||||||
|
AND `Name` = '{name}';
|
||||||
|
""")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_select_by_server_string(s_id: int) -> str:
|
||||||
|
return str(f"""
|
||||||
|
SELECT * FROM `Statistics`
|
||||||
|
WHERE `ServerId` = {s_id};
|
||||||
|
""")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def insert_string(self) -> str:
|
||||||
|
return str(f"""
|
||||||
|
INSERT INTO `Statistics` (
|
||||||
|
`Name`, `Description`, `Code`, `ServerId`, `CreatedAt`, `LastModifiedAt`
|
||||||
|
) VALUES (
|
||||||
|
'{self._name}',
|
||||||
|
'{self._description}',
|
||||||
|
'{self._code}',
|
||||||
|
{self._server.server_id},
|
||||||
|
'{self._created_at}',
|
||||||
|
'{self._modified_at}'
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def udpate_string(self) -> str:
|
||||||
|
return str(f"""
|
||||||
|
UPDATE `Statistics`
|
||||||
|
SET `Name` = '{self._name}',
|
||||||
|
`Description` = '{self._description}',
|
||||||
|
`Code` = '{self._code}',
|
||||||
|
`LastModifiedAt` = '{self._modified_at}'
|
||||||
|
WHERE `Id` = {self._id};
|
||||||
|
""")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def delete_string(self) -> str:
|
||||||
|
return str(f"""
|
||||||
|
DELETE FROM `Statistics`
|
||||||
|
WHERE `Id` = {self._id};
|
||||||
|
""")
|
93
kdb-bot/src/bot_data/service/statistic_repository_service.py
Normal file
93
kdb-bot/src/bot_data/service/statistic_repository_service.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from cpl_core.database.context import DatabaseContextABC
|
||||||
|
from cpl_core.utils import CredentialManager
|
||||||
|
from cpl_query.extension import List
|
||||||
|
|
||||||
|
from bot_core.logging.database_logger import DatabaseLogger
|
||||||
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
|
from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC
|
||||||
|
from bot_data.model.statistic import Statistic
|
||||||
|
|
||||||
|
|
||||||
|
class StatisticRepositoryService(StatisticRepositoryABC):
|
||||||
|
|
||||||
|
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, statistics: ServerRepositoryABC):
|
||||||
|
self._logger = logger
|
||||||
|
self._context = db_context
|
||||||
|
|
||||||
|
self._statistics = statistics
|
||||||
|
|
||||||
|
StatisticRepositoryABC.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_value_from_result(value: any) -> Optional[any]:
|
||||||
|
if isinstance(value, str) and 'NULL' in value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _statistic_from_result(self, sql_result: tuple) -> Statistic:
|
||||||
|
code = self._get_value_from_result(sql_result[3])
|
||||||
|
if code is not None:
|
||||||
|
code = CredentialManager.decrypt(code)
|
||||||
|
|
||||||
|
statistic = Statistic(
|
||||||
|
self._get_value_from_result(sql_result[1]),
|
||||||
|
self._get_value_from_result(sql_result[2]),
|
||||||
|
code,
|
||||||
|
self._statistics.get_server_by_id(sql_result[4]),
|
||||||
|
id=self._get_value_from_result(sql_result[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
return statistic
|
||||||
|
|
||||||
|
def get_statistics(self) -> List[Statistic]:
|
||||||
|
statistics = List(Statistic)
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_all_string()}')
|
||||||
|
results = self._context.select(Statistic.get_select_all_string())
|
||||||
|
for result in results:
|
||||||
|
statistics.append(self._statistic_from_result(result))
|
||||||
|
|
||||||
|
return statistics
|
||||||
|
|
||||||
|
def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]:
|
||||||
|
statistics = List(Statistic)
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_server_string(server_id)}')
|
||||||
|
results = self._context.select(Statistic.get_select_by_server_string(server_id))
|
||||||
|
for result in results:
|
||||||
|
statistics.append(self._statistic_from_result(result))
|
||||||
|
|
||||||
|
return statistics
|
||||||
|
|
||||||
|
def get_statistic_by_id(self, id: int) -> Statistic:
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_id_string(id)}')
|
||||||
|
result = self._context.select(Statistic.get_select_by_id_string(id))[0]
|
||||||
|
return self._statistic_from_result(result)
|
||||||
|
|
||||||
|
def get_statistic_by_name(self, name: str, server_id: int) -> Statistic:
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}')
|
||||||
|
result = self._context.select(Statistic.get_select_by_name_string(name, server_id))[0]
|
||||||
|
return self._statistic_from_result(result)
|
||||||
|
|
||||||
|
def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]:
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}')
|
||||||
|
result = self._context.select(Statistic.get_select_by_name_string(name, server_id))
|
||||||
|
if result is None or len(result) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = result[0]
|
||||||
|
|
||||||
|
return self._statistic_from_result(result)
|
||||||
|
|
||||||
|
def add_statistic(self, statistic: Statistic):
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {statistic.insert_string}')
|
||||||
|
self._context.cursor.execute(statistic.insert_string)
|
||||||
|
|
||||||
|
def update_statistic(self, statistic: Statistic):
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {statistic.udpate_string}')
|
||||||
|
self._context.cursor.execute(statistic.udpate_string)
|
||||||
|
|
||||||
|
def delete_statistic(self, statistic: Statistic):
|
||||||
|
self._logger.trace(__name__, f'Send SQL command: {statistic.delete_string}')
|
||||||
|
self._context.cursor.execute(statistic.delete_string)
|
@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'modules.base.abc'
|
__title__ = 'modules.base.service'
|
||||||
__author__ = 'Sven Heidemann'
|
__author__ = 'Sven Heidemann'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||||
|
@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'modules.permission.abc'
|
__title__ = 'modules.permission.service'
|
||||||
__author__ = 'Sven Heidemann'
|
__author__ = 'Sven Heidemann'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||||
|
1
kdb-bot/src/modules/stats/__init__.py
Normal file
1
kdb-bot/src/modules/stats/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports:
|
1
kdb-bot/src/modules/stats/command/__init__.py
Normal file
1
kdb-bot/src/modules/stats/command/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports
|
216
kdb-bot/src/modules/stats/command/stats_group.py
Normal file
216
kdb-bot/src/modules/stats/command/stats_group.py
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
from typing import List as TList
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from cpl_core.database.context import DatabaseContextABC
|
||||||
|
from cpl_discord.command import DiscordCommandABC
|
||||||
|
from cpl_translation import TranslatePipe
|
||||||
|
from discord import app_commands
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||||
|
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||||
|
from bot_core.logging.command_logger import CommandLogger
|
||||||
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
|
from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC
|
||||||
|
from modules.permission.abc.permission_service_abc import PermissionServiceABC
|
||||||
|
from modules.stats.service.statistic_service import StatisticService
|
||||||
|
from modules.stats.ui.add_statistic_form import AddStatisticForm
|
||||||
|
|
||||||
|
|
||||||
|
class StatsGroup(DiscordCommandABC):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
logger: CommandLogger,
|
||||||
|
message_service: MessageServiceABC,
|
||||||
|
client_utils: ClientUtilsServiceABC,
|
||||||
|
translate: TranslatePipe,
|
||||||
|
permission_service: PermissionServiceABC,
|
||||||
|
statistic: StatisticService,
|
||||||
|
servers: ServerRepositoryABC,
|
||||||
|
stats: StatisticRepositoryABC,
|
||||||
|
db: DatabaseContextABC,
|
||||||
|
):
|
||||||
|
DiscordCommandABC.__init__(self)
|
||||||
|
|
||||||
|
self._logger = logger
|
||||||
|
self._client_utils = client_utils
|
||||||
|
self._message_service = message_service
|
||||||
|
self._t = translate
|
||||||
|
self._permissions = permission_service
|
||||||
|
self._statistic = statistic
|
||||||
|
self._servers = servers
|
||||||
|
self._stats = stats
|
||||||
|
self._db = db
|
||||||
|
|
||||||
|
@commands.hybrid_group()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def stats(self, ctx: Context):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@stats.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def list(self, ctx: Context, wait: int = None):
|
||||||
|
self._logger.debug(__name__, f'Received command stats list {ctx}')
|
||||||
|
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._permissions.is_member_moderator(ctx.author):
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||||
|
self._logger.trace(__name__, f'Finished command stats list')
|
||||||
|
return
|
||||||
|
|
||||||
|
if ctx.guild is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=self._t.transform('modules.auto_role.list.title'),
|
||||||
|
description=self._t.transform('modules.auto_role.list.description'),
|
||||||
|
color=int('ef9d0d', 16)
|
||||||
|
)
|
||||||
|
|
||||||
|
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
|
||||||
|
if stats.count() == 0:
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.list.nothing_found'))
|
||||||
|
return
|
||||||
|
|
||||||
|
statistics = ''
|
||||||
|
descriptions = ''
|
||||||
|
for statistic in stats:
|
||||||
|
statistics += f'\n{statistic.name}'
|
||||||
|
descriptions += f'\n{statistic.description}'
|
||||||
|
|
||||||
|
embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True)
|
||||||
|
embed.add_field(name=self._t.transform('modules.stats.list.description'), value=descriptions, inline=True)
|
||||||
|
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)
|
||||||
|
self._logger.trace(__name__, f'Finished command stats list')
|
||||||
|
|
||||||
|
@stats.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def view(self, ctx: Context, name: str, wait: int = None):
|
||||||
|
self._logger.debug(__name__, f'Received command stats view {ctx}:{name}')
|
||||||
|
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._permissions.is_member_moderator(ctx.author):
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||||
|
self._logger.trace(__name__, f'Finished command stats view')
|
||||||
|
return
|
||||||
|
|
||||||
|
if ctx.guild is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
statistic = stats.where(lambda s: s.name == name).single()
|
||||||
|
result = await self._statistic.execute(statistic.code, server)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=statistic.name,
|
||||||
|
description=statistic.description,
|
||||||
|
color=int('ef9d0d', 16)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(result.header.count()):
|
||||||
|
header = result.header[i]
|
||||||
|
value = ''
|
||||||
|
for row in result.values:
|
||||||
|
value += f'\n{row[i]}'
|
||||||
|
embed.add_field(name=header, value=value, inline=True)
|
||||||
|
|
||||||
|
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f'Cannot view statistic {name}', e)
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed'))
|
||||||
|
|
||||||
|
self._logger.trace(__name__, f'Finished stats view command')
|
||||||
|
|
||||||
|
@view.autocomplete('name')
|
||||||
|
async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
|
||||||
|
server = self._servers.get_server_by_discord_id(interaction.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats]
|
||||||
|
|
||||||
|
@stats.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def add(self, ctx: Context, name: str):
|
||||||
|
self._logger.debug(__name__, f'Received command stats add {ctx}: {name}')
|
||||||
|
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._permissions.is_member_technician(ctx.author):
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||||
|
self._logger.trace(__name__, f'Finished command stats add')
|
||||||
|
return
|
||||||
|
|
||||||
|
if ctx.guild is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||||
|
form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t)
|
||||||
|
self._logger.trace(__name__, f'Finished stats add command')
|
||||||
|
self._logger.trace(__name__, f'Started stats command form')
|
||||||
|
await ctx.interaction.response.send_modal(form)
|
||||||
|
|
||||||
|
@stats.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def edit(self, ctx: Context, name: str):
|
||||||
|
self._logger.debug(__name__, f'Received command stats edit {ctx}: {name}')
|
||||||
|
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._permissions.is_member_technician(ctx.author):
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||||
|
self._logger.trace(__name__, f'Finished command stats edit')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
statistic = stats.where(lambda s: s.name == name).single()
|
||||||
|
form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description)
|
||||||
|
self._logger.trace(__name__, f'Finished stats edit command')
|
||||||
|
self._logger.trace(__name__, f'Started stats command form')
|
||||||
|
await ctx.interaction.response.send_modal(form)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f'Cannot edit statistic {name}', e)
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed'))
|
||||||
|
|
||||||
|
@edit.autocomplete('name')
|
||||||
|
async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
|
||||||
|
server = self._servers.get_server_by_discord_id(interaction.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats]
|
||||||
|
|
||||||
|
@stats.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def remove(self, ctx: Context, name: str):
|
||||||
|
self._logger.debug(__name__, f'Received command stats remove {ctx}: {name}')
|
||||||
|
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._permissions.is_member_technician(ctx.author):
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||||
|
self._logger.trace(__name__, f'Finished command stats remove')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||||
|
statistic = self._stats.get_statistic_by_name(name, server.server_id)
|
||||||
|
self._stats.delete_statistic(statistic)
|
||||||
|
self._db.save_changes()
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.success'))
|
||||||
|
self._logger.trace(__name__, f'Finished stats remove command')
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f'Cannot remove statistic {name}', e)
|
||||||
|
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.failed'))
|
||||||
|
|
||||||
|
@remove.autocomplete('name')
|
||||||
|
async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
|
||||||
|
server = self._servers.get_server_by_discord_id(interaction.guild.id)
|
||||||
|
stats = self._stats.get_statistics_by_server_id(server.server_id)
|
||||||
|
return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats]
|
1
kdb-bot/src/modules/stats/model/__init__.py
Normal file
1
kdb-bot/src/modules/stats/model/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports
|
24
kdb-bot/src/modules/stats/model/statistic_result.py
Normal file
24
kdb-bot/src/modules/stats/model/statistic_result.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from cpl_query.extension import List
|
||||||
|
|
||||||
|
|
||||||
|
class StatisticResult:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._header = List(str)
|
||||||
|
self._values = List(List)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def header(self) -> List[str]:
|
||||||
|
return self._header
|
||||||
|
|
||||||
|
@header.setter
|
||||||
|
def header(self, value: List[str]):
|
||||||
|
self._header = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[List]:
|
||||||
|
return self._values
|
||||||
|
|
||||||
|
@values.setter
|
||||||
|
def values(self, value: List[List]):
|
||||||
|
self._values = value
|
1
kdb-bot/src/modules/stats/service/__init__.py
Normal file
1
kdb-bot/src/modules/stats/service/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports
|
96
kdb-bot/src/modules/stats/service/statistic_service.py
Normal file
96
kdb-bot/src/modules/stats/service/statistic_service.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from cpl_discord.service import DiscordBotServiceABC
|
||||||
|
from cpl_query.extension import List
|
||||||
|
from discord import Guild
|
||||||
|
|
||||||
|
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
|
||||||
|
from bot_data.abc.client_repository_abc import ClientRepositoryABC
|
||||||
|
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
|
||||||
|
from bot_data.abc.level_repository_abc import LevelRepositoryABC
|
||||||
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
|
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
|
||||||
|
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
|
||||||
|
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
||||||
|
from bot_data.model.auto_role import AutoRole
|
||||||
|
from bot_data.model.client import Client
|
||||||
|
from bot_data.model.known_user import KnownUser
|
||||||
|
from bot_data.model.level import Level
|
||||||
|
from bot_data.model.server import Server
|
||||||
|
from bot_data.model.user import User
|
||||||
|
from bot_data.model.user_joined_server import UserJoinedServer
|
||||||
|
from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel
|
||||||
|
from modules.stats.model.statistic_result import StatisticResult
|
||||||
|
|
||||||
|
|
||||||
|
class StatisticService:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
auto_roles: AutoRoleRepositoryABC,
|
||||||
|
clients: ClientRepositoryABC,
|
||||||
|
known_users: KnownUserRepositoryABC,
|
||||||
|
levels: LevelRepositoryABC,
|
||||||
|
servers: ServerRepositoryABC,
|
||||||
|
user_joined_servers: UserJoinedServerRepositoryABC,
|
||||||
|
user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC,
|
||||||
|
users: UserRepositoryABC,
|
||||||
|
bot: DiscordBotServiceABC,
|
||||||
|
):
|
||||||
|
self._auto_roles = auto_roles
|
||||||
|
self._clients = clients
|
||||||
|
self._known_users = known_users
|
||||||
|
self._levels = levels
|
||||||
|
self._servers = servers
|
||||||
|
self._user_joined_servers = user_joined_servers
|
||||||
|
self._user_joined_voice_channel = user_joined_voice_channel
|
||||||
|
self._users = users
|
||||||
|
self._bot = bot
|
||||||
|
|
||||||
|
async def execute(self, code: str, server: Server) -> StatisticResult:
|
||||||
|
guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single()
|
||||||
|
|
||||||
|
return await self.get_data(
|
||||||
|
code,
|
||||||
|
self._auto_roles
|
||||||
|
.get_auto_roles()
|
||||||
|
.where(lambda x: x.server.server_id == server.server_id),
|
||||||
|
self._clients
|
||||||
|
.get_clients()
|
||||||
|
.where(lambda x: x.server.server_id == server.server_id),
|
||||||
|
self._known_users.get_users(),
|
||||||
|
self._levels
|
||||||
|
.get_levels()
|
||||||
|
.where(lambda x: x.server.server_id == server.server_id),
|
||||||
|
self._servers
|
||||||
|
.get_servers()
|
||||||
|
.where(lambda x: x.server_id == server.server_id),
|
||||||
|
self._user_joined_servers
|
||||||
|
.get_user_joined_servers()
|
||||||
|
.where(lambda x: x.user.server.server_id == server.server_id),
|
||||||
|
self._user_joined_voice_channel
|
||||||
|
.get_user_joined_voice_channels()
|
||||||
|
.where(lambda x: x.user.server.server_id == server.server_id),
|
||||||
|
self._users
|
||||||
|
.get_users()
|
||||||
|
.where(lambda x: x.server.server_id == server.server_id),
|
||||||
|
guild
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_data(
|
||||||
|
code: str,
|
||||||
|
auto_roles: List[AutoRole],
|
||||||
|
clients: List[Client],
|
||||||
|
known_users: List[KnownUser],
|
||||||
|
levels: List[Level],
|
||||||
|
servers: List[Server],
|
||||||
|
user_joined_servers: List[UserJoinedServer],
|
||||||
|
user_joined_voice_channel: List[UserJoinedVoiceChannel],
|
||||||
|
users: List[User],
|
||||||
|
guild: Guild
|
||||||
|
) -> StatisticResult:
|
||||||
|
result = StatisticResult()
|
||||||
|
exec(code)
|
||||||
|
|
||||||
|
return result
|
46
kdb-bot/src/modules/stats/stats.json
Normal file
46
kdb-bot/src/modules/stats/stats.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"ProjectSettings": {
|
||||||
|
"Name": "stats",
|
||||||
|
"Version": {
|
||||||
|
"Major": "0",
|
||||||
|
"Minor": "0",
|
||||||
|
"Micro": "0"
|
||||||
|
},
|
||||||
|
"Author": "",
|
||||||
|
"AuthorEmail": "",
|
||||||
|
"Description": "",
|
||||||
|
"LongDescription": "",
|
||||||
|
"URL": "",
|
||||||
|
"CopyrightDate": "",
|
||||||
|
"CopyrightName": "",
|
||||||
|
"LicenseName": "",
|
||||||
|
"LicenseDescription": "",
|
||||||
|
"Dependencies": [
|
||||||
|
"cpl-core>=2022.10.0.post7"
|
||||||
|
],
|
||||||
|
"DevDependencies": [
|
||||||
|
"cpl-cli>=2022.10.1"
|
||||||
|
],
|
||||||
|
"PythonVersion": ">=3.10.4",
|
||||||
|
"PythonPath": {
|
||||||
|
"linux": ""
|
||||||
|
},
|
||||||
|
"Classifiers": []
|
||||||
|
},
|
||||||
|
"BuildSettings": {
|
||||||
|
"ProjectType": "library",
|
||||||
|
"SourcePath": "",
|
||||||
|
"OutputPath": "../../dist",
|
||||||
|
"Main": "stats.main",
|
||||||
|
"EntryPoint": "stats",
|
||||||
|
"IncludePackageData": false,
|
||||||
|
"Included": [],
|
||||||
|
"Excluded": [
|
||||||
|
"*/__pycache__",
|
||||||
|
"*/logs",
|
||||||
|
"*/tests"
|
||||||
|
],
|
||||||
|
"PackageData": {},
|
||||||
|
"ProjectReferences": []
|
||||||
|
}
|
||||||
|
}
|
24
kdb-bot/src/modules/stats/stats_module.py
Normal file
24
kdb-bot/src/modules/stats/stats_module.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from cpl_core.configuration import ConfigurationABC
|
||||||
|
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||||
|
from cpl_core.environment import ApplicationEnvironmentABC
|
||||||
|
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
||||||
|
|
||||||
|
from bot_core.abc.module_abc import ModuleABC
|
||||||
|
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
|
from modules.stats.command.stats_group import StatsGroup
|
||||||
|
from modules.stats.service.statistic_service import StatisticService
|
||||||
|
|
||||||
|
|
||||||
|
class StatsModule(ModuleABC):
|
||||||
|
|
||||||
|
def __init__(self, dc: DiscordCollectionABC):
|
||||||
|
ModuleABC.__init__(self, dc, FeatureFlagsEnum.stats_module)
|
||||||
|
|
||||||
|
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||||
|
services.add_transient(StatisticService)
|
||||||
|
# commands
|
||||||
|
self._dc.add_command(StatsGroup)
|
||||||
|
# events
|
1
kdb-bot/src/modules/stats/ui/__init__.py
Normal file
1
kdb-bot/src/modules/stats/ui/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports
|
76
kdb-bot/src/modules/stats/ui/add_statistic_form.py
Normal file
76
kdb-bot/src/modules/stats/ui/add_statistic_form.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import discord
|
||||||
|
from cpl_core.database.context import DatabaseContextABC
|
||||||
|
from cpl_query.extension import List
|
||||||
|
from cpl_translation import TranslatePipe
|
||||||
|
from discord import ui, TextStyle
|
||||||
|
|
||||||
|
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||||
|
from bot_core.logging.command_logger import CommandLogger
|
||||||
|
from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC
|
||||||
|
from bot_data.model.server import Server
|
||||||
|
from bot_data.model.statistic import Statistic
|
||||||
|
|
||||||
|
|
||||||
|
class AddStatisticForm(ui.Modal):
|
||||||
|
|
||||||
|
description = ui.TextInput(label='Beschreibung', required=True)
|
||||||
|
code = ui.TextInput(label='Code', required=True, style=TextStyle.long)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
server: Server,
|
||||||
|
stats: StatisticRepositoryABC,
|
||||||
|
db: DatabaseContextABC,
|
||||||
|
name: str,
|
||||||
|
message_service: MessageServiceABC,
|
||||||
|
logger: CommandLogger,
|
||||||
|
t: TranslatePipe,
|
||||||
|
code: str = None,
|
||||||
|
description: str = None,
|
||||||
|
):
|
||||||
|
ui.Modal.__init__(self, title=name)
|
||||||
|
|
||||||
|
self._server = server
|
||||||
|
self._stats = stats
|
||||||
|
self._db = db
|
||||||
|
self._name = name
|
||||||
|
self._message_service = message_service
|
||||||
|
self._logger = logger
|
||||||
|
self._t = t
|
||||||
|
|
||||||
|
if code is not None:
|
||||||
|
self.code.default = code
|
||||||
|
|
||||||
|
if description is not None:
|
||||||
|
self.description.default = description
|
||||||
|
|
||||||
|
async def on_submit(self, interaction: discord.Interaction):
|
||||||
|
statistic = self._stats.get_statistics_by_server_id(self._server.server_id).where(lambda s: s.name == self._name).single_or_default()
|
||||||
|
|
||||||
|
if interaction.guild is None:
|
||||||
|
if statistic is None:
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed'))
|
||||||
|
else:
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed'))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if statistic is None:
|
||||||
|
self._stats.add_statistic(Statistic(self._name, self.description.value, self.code.value, self._server))
|
||||||
|
self._db.save_changes()
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success'))
|
||||||
|
return
|
||||||
|
|
||||||
|
statistic.description = self.description.value
|
||||||
|
statistic.code = self.code.value
|
||||||
|
self._stats.update_statistic(statistic)
|
||||||
|
self._db.save_changes()
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.success'))
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f'Save statistic {self._name} failed', e)
|
||||||
|
if statistic is None:
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed'))
|
||||||
|
else:
|
||||||
|
await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed'))
|
||||||
|
|
||||||
|
self._logger.trace(__name__, f'Finished stats command form')
|
Loading…
Reference in New Issue
Block a user