61 Commits

Author SHA1 Message Date
f8992638d4 Smaller bugfixes 2022-10-09 17:26:22 +02:00
77e4491e90 Merge pull request 'Fehlerhaftes logging und reaction error (#65)' (#66) from #65 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#66
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #65
2022-10-06 16:56:54 +02:00
523eb23f09 Changed logic to get member for reaction #65 2022-10-06 16:51:57 +02:00
6865953433 Updated cpl-core package #65 2022-10-06 16:51:28 +02:00
d3dd825358 Fixed restart in docker compose 2022-10-06 16:07:09 +02:00
abac507e87 Fixed shutdown command 2022-10-05 19:11:34 +02:00
359a614a1a Merge pull request 'Rollen per Reaktion hinzufügen (#54)' (#58) from #54 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#58
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #54
2022-10-05 19:08:22 +02:00
f2e6be530b Renamed some functions & fixed list return context #54 2022-10-05 19:04:58 +02:00
2df4f90802 Removed redundant set from sql queries #54 2022-10-05 18:09:27 +02:00
869439d3b8 Improved translation #54 2022-10-05 17:38:21 +02:00
36a8a5be3c Added project references #54 2022-10-05 16:52:10 +02:00
8966bded81 Added channel param to auto-role add & fixed on reaction add event stuff #54 2022-10-05 16:43:24 +02:00
c7c60edbd7 Build version 0.2.2 #54 2022-10-04 22:30:59 +02:00
17befd9f05 Added auto-role rule remove command #54 2022-10-04 22:24:20 +02:00
eb3bdac162 Added auto-role rule add command #54 2022-10-04 22:04:25 +02:00
b75777a339 Added auto-role remove command & some refactoring #54 2022-10-04 21:04:02 +02:00
5795028fdb Added auto-role add command #54 2022-10-04 20:31:03 +02:00
8afc414d8b Removed old imports #54 2022-10-04 17:23:07 +02:00
a23815998c Improved some commands #54 2022-10-04 13:23:15 +02:00
97e4f8ec29 Added auto-role rule list #54 2022-10-04 13:18:55 +02:00
21c34436b9 Improved auto-role-rule list #54 2022-10-03 22:22:51 +02:00
b35b023b02 [WIP] Added auto-role-rules list #54 2022-10-03 19:47:06 +02:00
9daa0b1c54 Added auto-role list #54 2022-10-03 19:26:48 +02:00
d0ed413150 Added auto role remove #54 2022-10-03 18:27:07 +02:00
fa98f08161 Added auto role add #54 2022-10-03 18:24:29 +02:00
f150df2dfd Added auto role repository #54 2022-10-03 16:36:11 +02:00
901aa4e1a5 Added auto role tables #54 2022-10-03 16:19:54 +02:00
7c0e34e1f8 Fixed docker crazy stuff 2022-10-03 15:27:24 +02:00
bf5003f100 Merge pull request '0.2.1 - Status setzen (#56)' (#57) from #56 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#57
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #56
2022-10-03 13:44:45 +02:00
9f4c6d45c6 Build 2022-10-03 13:44:12 +02:00
04e8e64003 Improved feature-flags & fixed logging 2022-10-03 13:39:17 +02:00
1c3ec3c7e7 Added presence #56 2022-10-03 12:42:42 +02:00
1a8c74d9e0 Fixed docker stuff to work 2022-10-03 04:39:00 +02:00
676d718fc2 Merge pull request 'Sprint 0.2' (#55) from 0.2 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#55
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2022-10-03 02:30:42 +02:00
11791ccf98 Merge pull request '0.2 - Module aufgeräumt (#52)' (#53) from #52 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#53
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #52
2022-10-03 02:25:56 +02:00
05fbe72304 Improved module support #52 2022-10-03 01:05:34 +02:00
b8874a6eab Added module support 2022-10-03 00:28:22 +02:00
03497b6d5a Merge pull request '0.2 - Befehle Migrieren (#42)' (#49) from #42 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#49
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #42
2022-10-02 23:34:38 +02:00
5b1a98ad5c Merge branch '0.2' into #42 2022-10-02 23:32:01 +02:00
63ff8f54fa Merge pull request '0.2 - Besseres Logging (#45)' (#51) from #45 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#51
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #45
2022-10-02 23:30:18 +02:00
cb1ebeeb0c Improved on command logging #45 2022-10-02 23:29:23 +02:00
54db704044 Improved settings #45 2022-10-02 23:23:26 +02:00
d5a1c15db3 Prevent multiple stop_async calls 2022-10-02 13:25:29 +02:00
fa50123d3b Improved logging for migrations #45 2022-10-02 13:22:41 +02:00
e2749f6a81 Improved logging 2022-10-02 13:13:35 +02:00
12cd5c9045 Added custom logging support #45 2022-10-02 12:14:44 +02:00
6e5e5d4550 Merge pull request '0.2 - Feature-Flags (#48)' (#50) from #48 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#50
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #48
2022-10-02 11:18:27 +02:00
890cb6f6b5 Added feature-flags #48 2022-10-02 02:54:08 +02:00
ac238b360a Added feature-flags 2022-10-02 02:34:27 +02:00
8b55db172a Updated info command 2022-10-02 02:07:05 +02:00
7496a17cfa Migrated all other commands 2022-10-02 02:05:47 +02:00
0dbf33a39f Migrated purge command 2022-10-02 02:00:21 +02:00
a939c741cb Added logic to wait before handling command & migrated shutdown command #42 2022-10-02 01:52:32 +02:00
a4dc66396e Migrated restart_command #42 2022-10-02 01:11:54 +02:00
a8b6989d27 Merge pull request '0.2 - Added docker support (#39)' (#41) from #39 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#41
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #39
2022-10-01 23:34:33 +02:00
51fec48cda Added docker support 2022-10-01 23:19:38 +02:00
4b8af2aef4 Merge pull request '#38 - cpl update' (#40) from #38 into 0.2
Reviewed-on: sh-edraft.de/kd_discord_bot#40
Closes #38
2022-09-30 17:29:43 +02:00
b936e569aa Build new version and fixed deps #38 2022-09-30 17:24:02 +02:00
709767c3a0 First fixes #38 2022-09-30 17:03:44 +02:00
40beb3711a Merge pull request '0.1.14 - Anpassung deck zu git' (#17) from 0.1.14 into 0.1
Reviewed-on: sh-edraft.de/kd_discord_bot#17
2022-09-19 20:06:43 +02:00
4cd41c4b26 Set version and build project 2022-07-21 10:09:27 +02:00
128 changed files with 2668 additions and 431 deletions

View File

@@ -6,6 +6,7 @@
"bot-core": "src/bot_core/bot-core.json",
"bot-data": "src/bot_data/bot-data.json",
"admin": "src/modules/admin/admin.json",
"auto-role": "src/modules/auto_role/auto-role.json",
"base": "src/modules/base/base.json",
"boot-log": "src/modules/boot_log/boot-log.json",
"database": "src/modules/database/database.json",
@@ -15,7 +16,11 @@
"Scripts": {
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
"stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;",
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;"
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;",
"build-docker": "cpl b; docker-compose down; docker build -t kdb .",
"compose": "docker-compose up -d",
"docker": "cpl build-docker; cpl compose;"
}
}
}

64
docker-compose.yml Normal file
View File

@@ -0,0 +1,64 @@
version: "3.9"
volumes:
kdb_prod_1:
kdb_staging_1:
kdb_db_1:
kdb_db_2:
services:
kdb_prod_1:
image: kdb/kdb:0.2.1
container_name: kdb_prod_1
depends_on:
- kdb_db_1
volumes:
- kdb_prod_1:/app
environment:
KDB_ENVIRONMENT: "production"
KDB_TOKEN: ""
KDB_PREFIX: "!k "
restart: 'no'
kdb_staging_1:
image: kdb/kdb:0.2.1
container_name: kdb_staging_1
depends_on:
- kdb_db_1
volumes:
- kdb_staging_1:/app
environment:
KDB_ENVIRONMENT: "staging"
KDB_TOKEN: ""
KDB_PREFIX: "!kt "
restart: 'no'
kdb_db_1:
image: mysql:latest
container_name: kdb_db_1
command: mysqld --default-authentication-plugin=mysql_native_password
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "kd_kdb"
MYSQL_USER: "kd_kdb"
MYSQL_PASSWORD: "kd_kdb"
MYSQL_DATABASE: "kd_kdb"
ports:
- "3307:3306"
volumes:
- kdb_db_1:/var/lib/mysql
kdb_db_2:
image: mysql:latest
container_name: kdb_db_2
command: mysqld --default-authentication-plugin=mysql_native_password
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "kd_kdb"
MYSQL_USER: "kd_kdb"
MYSQL_PASSWORD: "kd_kdb"
MYSQL_DATABASE: "kd_kdb"
ports:
- "3308:3306"
volumes:
- kdb_db_2:/var/lib/mysql

18
dockerfile Normal file
View File

@@ -0,0 +1,18 @@
# syntax=docker/dockerfile:1
FROM python:3.10.7-bullseye
WORKDIR /app
COPY ./dist/bot/build/ .
RUN pip install cpl-core --extra-index-url https://pip.sh-edraft.de
RUN pip install cpl-discord --extra-index-url https://pip.sh-edraft.de
RUN pip install cpl-query --extra-index-url https://pip.sh-edraft.de
RUN pip install cpl-translation --extra-index-url https://pip.sh-edraft.de
RUN apt-get update -y
RUN apt-get install nano -y
ENV KDB_TOKEN=""
ENV KDB_PREFIX="!kdb "
ENV KDB_ENVIRONMENT="production"
CMD [ "bash", "/app/bot/bot"]

View File

@@ -15,7 +15,7 @@ __title__ = 'bot'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -1,6 +1,4 @@
from typing import Optional, Type
from cpl_core.configuration import ConfigurationABC, ConfigurationModelABC
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
@@ -9,21 +7,14 @@ from cpl_discord.configuration import DiscordBotSettings
from cpl_discord.service import DiscordBotServiceABC, DiscordBotService
from cpl_translation import TranslatePipe, TranslationServiceABC, TranslationSettings
from bot_core.configuration.bot_settings import BotSettings
from bot_core.configuration.server_settings import ServerSettings
from modules.base.configuration.base_server_settings import BaseServerSettings
from modules.base.configuration.base_settings import BaseSettings
from modules.boot_log.configuration.boot_log_server_settings import BootLogServerSettings
from modules.boot_log.configuration.boot_log_settings import BootLogSettings
from modules.permission.configuration.permission_server_settings import PermissionServerSettings
from modules.permission.configuration.permission_settings import PermissionSettings
class Application(DiscordBotApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
DiscordBotApplicationABC.__init__(self, config, services)
self._services = services
# cpl-core
self._logger: LoggerABC = services.get_service(LoggerABC)
# cpl-discord
@@ -31,33 +22,27 @@ class Application(DiscordBotApplicationABC):
self._bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings)
# cpl-translation
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
self._translate: TranslatePipe = services.get_service(TranslatePipe)
self._t: TranslatePipe = services.get_service(TranslatePipe)
def _configure_settings_with_servers(self, settings: Type, server_settings: Type):
settings: Optional[settings] = self._configuration.get_configuration(settings)
if settings is None:
return
for server in settings.servers:
self._logger.trace(__name__, f'Saved config: {type(server).__name__}_{server.id}')
self._configuration.add_configuration(f'{type(server).__name__}_{server.id}', server)
self._is_stopping = False
async def configure(self):
self._translation.load_by_settings(self._configuration.get_configuration(TranslationSettings))
self._configure_settings_with_servers(BotSettings, ServerSettings)
self._configure_settings_with_servers(BaseSettings, BaseServerSettings)
self._configure_settings_with_servers(BootLogSettings, BootLogServerSettings)
self._configure_settings_with_servers(PermissionSettings, PermissionServerSettings)
async def main(self):
try:
self._logger.debug(__name__, f'Starting...\n')
self._logger.debug(__name__, f'Starting...')
self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}')
await self._bot.start_async()
await self._bot.stop_async()
except Exception as e:
self._logger.error(__name__, 'Start failed', e)
async def stop_async(self):
if self._is_stopping:
return
self._is_stopping = True
try:
self._logger.trace(__name__, f'Try to stop {DiscordBotService.__name__}')
await self._bot.close()
@@ -68,4 +53,4 @@ class Application(DiscordBotApplicationABC):
Console.write_line()
def is_restart(self):
return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False
return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False#

View File

@@ -2,9 +2,9 @@
"ProjectSettings": {
"Name": "bot",
"Version": {
"Major": "1",
"Minor": "0",
"Micro": "0.dev3"
"Major": "0",
"Minor": "2",
"Micro": "3"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
@@ -16,13 +16,13 @@
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"cpl-core>=2022.7.0.post4",
"cpl-translation==2022.7.0.post2",
"cpl-query==2022.7.0",
"cpl-discord==2022.7.0.post4"
"cpl-core==2022.10.0.post6",
"cpl-translation==2022.10.0",
"cpl-query==2022.10.0",
"cpl-discord==2022.10.0.post5"
],
"DevDependencies": [
"cpl-cli>=2022.7.0.post2"
"cpl-cli==2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {
@@ -48,11 +48,13 @@
"../bot_core/bot-core.json",
"../bot_data/bot-data.json",
"../modules/base/base.json",
"../modules/admin/admin.json",
"../modules/auto_role/auto-role.json",
"../modules/base/base.json",
"../modules/boot_log/boot-log.json",
"../modules/database/database.json",
"../modules/permission/permission.json",
"../modules/admin/admin.json",
"../modules/moderator/moderator.json"
"../modules/moderator/moderator.json",
"../modules/permission/permission.json"
]
}
}

View File

@@ -7,10 +7,30 @@
},
"LoggingSettings": {
"Path": "logs/",
"Filename": "log_dev.log",
"Filename": "bot.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "DEBUG"
},
"BotLoggingSettings": {
"Command": {
"Path": "logs/",
"Filename": "commands.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "DEBUG"
},
"Database": {
"Path": "logs/",
"Filename": "database.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "DEBUG"
},
"Message": {
"Path": "logs/",
"Filename": "message.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "DEBUG"
}
},
"Translation": {
"DefaultLanguage": "de",
"Languages": [
@@ -29,7 +49,8 @@
240160344557879316,
236592458664902657
],
"DeployFilesPath": "../../deploy"
"WaitForRestart": 4,
"WaitForShutdown": 4
},
"Base": {
"910199451145076828": {

View File

@@ -0,0 +1,86 @@
{
"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": "bot.log",
"ConsoleLogLevel": "TRACE",
"FileLogLevel": "TRACE"
},
"BotLoggingSettings": {
"Command": {
"Path": "logs/",
"Filename": "commands.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "TRACE"
},
"Database": {
"Path": "logs/",
"Filename": "database.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "TRACE"
},
"Message": {
"Path": "logs/",
"Filename": "message.log",
"ConsoleLogLevel": "DEBUG",
"FileLogLevel": "TRACE"
}
},
"DatabaseSettings": {
"Host": "localhost",
"User": "kd_kdb",
"Password": "VGpZcihrb0N2T2MyZUlURQ==",
"Database": "keksdose_bot_dev",
"Charset": "utf8mb4",
"UseUnicode": "true",
"Buffered": "true",
"AuthPlugin": "mysql_native_password"
},
"DiscordBot": {
"Token": "OTk4MTYwNDI3Njg5MTgxMjM3.GI7h67.BqD6Lu1Tz0MuG8iktYrcLnHi1pNozyMiWFGTKI",
"Prefix": "!ke "
},
"Bot": {
"910199451145076828": {
"MessageDeleteTimer": 2
},
"Technicians": [
240160344557879316
],
"WaitForRestart": 4,
"WaitForShutdown": 4
},
"Base": {
"910199451145076828": {
"MaxVoiceStateHours": 24,
"XpPerMessage": 2,
"XpPerOntimeHour": 4,
"AFKChannelIds": [
910199452915093593,
910199452915093594
],
"AFKCommandChannelId": 910199452915093594,
"HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle"
}
},
"BootLog": {
"910199451145076828": {
"LoginMessageChannelId": "910199452915093588"
}
},
"Permission": {
"910199451145076828": {
"AdminRoleIds": [
925072155203477584
],
"ModeratorRoleIds": [
925072209884635167
]
}
}
}

View File

@@ -6,9 +6,29 @@
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
},
"LoggingSettings": {
"Path": "logs/",
"Filename": "log_$start_time.log",
"Path": "logs/$date_now/",
"Filename": "bot.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "WARN"
},
"BotLoggingSettings": {
"Command": {
"Path": "logs/$date_now/",
"Filename": "commands.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "WARN"
},
"Database": {
"Path": "logs/$date_now/",
"Filename": "database.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "WARN"
},
"Message": {
"Path": "logs/$date_now/",
"Filename": "message.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "WARN"
}
}
}

View File

@@ -6,17 +6,48 @@
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
},
"LoggingSettings": {
"Path": "logs/",
"Filename": "log_$start_time.log",
"Path": "logs/$date_now/",
"Filename": "bot.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "INFO"
},
"BotLoggingSettings": {
"Command": {
"Path": "logs/$date_now/",
"Filename": "commands.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "INFO"
},
"Database": {
"Path": "logs/$date_now/",
"Filename": "database.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "INFO"
},
"Message": {
"Path": "logs/$date_now/",
"Filename": "message.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "INFO"
}
},
"Translation": {
"DefaultLanguage": "de",
"Languages": [
"de"
]
},
"DatabaseSettings": {
"Host": "kdb_db_1",
"User": "kd_kdb",
"Password": "a2Rfa2Ri",
"Database": "kd_kdb",
"Port": "3306",
"Charset": "utf8mb4",
"UseUnicode": "true",
"Buffered": "true",
"AuthPlugin": "mysql_native_password"
},
"Bot": {
"650366049023295514": {
"MessageDeleteTimer": 2
@@ -28,7 +59,8 @@
240160344557879316,
236592458664902657
],
"DeployFilesPath": "../../"
"WaitForRestart": 4,
"WaitForShutdown": 4
},
"Base": {
"650366049023295514": {

View File

@@ -6,17 +6,48 @@
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
},
"LoggingSettings": {
"Path": "logs/",
"Filename": "log_staging_$start_time.log",
"Path": "logs/$date_now/",
"Filename": "bot.log",
"ConsoleLogLevel": "INFO",
"FileLogLevel": "DEBUG"
},
"BotLoggingSettings": {
"Command": {
"Path": "logs/$date_now/",
"Filename": "commands.log",
"ConsoleLogLevel": "INFO",
"FileLogLevel": "DEBUG"
},
"Database": {
"Path": "logs/$date_now/",
"Filename": "database.log",
"ConsoleLogLevel": "INFO",
"FileLogLevel": "DEBUG"
},
"Message": {
"Path": "logs/$date_now/",
"Filename": "message.log",
"ConsoleLogLevel": "INFO",
"FileLogLevel": "DEBUG"
}
},
"Translation": {
"DefaultLanguage": "de",
"Languages": [
"de"
]
},
"DatabaseSettings": {
"Host": "kdb_db_2",
"User": "kd_kdb",
"Password": "a2Rfa2Ri",
"Database": "kd_kdb",
"Port": "3306",
"Charset": "utf8mb4",
"UseUnicode": "true",
"Buffered": "true",
"AuthPlugin": "mysql_native_password"
},
"Bot": {
"910199451145076828": {
"MessageDeleteTimer": 4
@@ -25,7 +56,8 @@
240160344557879316,
236592458664902657
],
"DeployFilesPath": "../../"
"WaitForRestart": 4,
"WaitForShutdown": 4
},
"Base": {
"910199451145076828": {

View File

@@ -0,0 +1,14 @@
{
"FeatureFlags": {
"AdminModule": true,
"AutoRoleModule": true,
"BaseModule": true,
"BootLogModule": true,
"CoreModule": true,
"CoreExtensionModule": true,
"DatabaseModule": true,
"ModeratorModule": true,
"PermissionModule": true,
"PresenceModule": true
}
}

View File

@@ -1,4 +1,5 @@
import asyncio
import traceback
from typing import Optional
from cpl_core.application import ApplicationBuilder
@@ -8,6 +9,8 @@ from bot.application import Application
from bot.startup import Startup
from bot.startup_discord_extension import StartupDiscordExtension
from bot.startup_migration_extension import StartupMigrationExtension
from bot.startup_module_extension import StartupModuleExtension
from bot.startup_settings_extension import StartupSettingsExtension
from modules.boot_log.boot_log_extension import BootLogExtension
from modules.database.database_extension import DatabaseExtension
@@ -18,16 +21,21 @@ class Program:
self.app: Optional[Application] = None
async def start(self):
app_builder = ApplicationBuilder(Application)
app_builder.use_extension(StartupDiscordExtension)
app_builder.use_extension(StartupMigrationExtension)
app_builder.use_extension(BootLogExtension)
app_builder.use_extension(DatabaseExtension)
app_builder.use_startup(Startup)
# discord extension has to be loaded before modules (modules depends on discord stuff)
app_builder = ApplicationBuilder(Application) \
.use_extension(StartupSettingsExtension) \
.use_extension(StartupDiscordExtension) \
.use_extension(StartupModuleExtension) \
.use_extension(StartupMigrationExtension) \
.use_extension(BootLogExtension) \
.use_extension(DatabaseExtension) \
.use_startup(Startup)
self.app: Application = await app_builder.build_async()
await self.app.run_async()
async def stop(self):
if self.app is None:
return
await self.app.stop_async()
@@ -38,12 +46,12 @@ def main():
except KeyboardInterrupt:
asyncio.run(program.stop())
except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot start the bot', str(e))
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot start the bot', f'{e} -> {traceback.format_exc()}')
finally:
try:
asyncio.run(program.stop())
except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot stop the bot', str(e))
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot stop the bot', f'{e} -> {traceback.format_exc()}')
if program.app is not None and program.app.is_restart():
del program

32
src/bot/module_list.py Normal file
View File

@@ -0,0 +1,32 @@
from cpl_query.extension import List
from bot_core.core_extension.core_extension_module import CoreExtensionModule
from bot_core.core_module import CoreModule
from bot_data.data_module import DataModule
from modules.admin.admin_module import AdminModule
from modules.auto_role.auto_role_module import AutoRoleModule
from modules.base.base_module import BaseModule
from modules.boot_log.boot_log_module import BootLogModule
from modules.database.database_module import DatabaseModule
from modules.moderator.moderator_module import ModeratorModule
from modules.permission.permission_module import PermissionModule
class ModuleList:
@staticmethod
def get_modules():
# core modules (modules out of modules folder) should be loaded first!
return List(type, [
CoreModule, # has to be first!
DataModule,
AdminModule,
AutoRoleModule,
BaseModule,
DatabaseModule,
ModeratorModule,
PermissionModule,
# has to be last!
BootLogModule,
CoreExtensionModule,
])

View File

@@ -1,35 +1,21 @@
import os
from datetime import datetime
from typing import Optional
from cpl_core.application import StartupABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.database import DatabaseSettings
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_core.environment import ApplicationEnvironment
from cpl_core.logging import LoggerABC
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe
from bot_core.service.client_utils_service import ClientUtilsService
from bot_core.service.message_service import MessageService
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
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_core.abc.custom_file_logger_abc import CustomFileLoggerABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_core.logging.command_logger import CommandLogger
from bot_core.logging.database_logger import DatabaseLogger
from bot_core.logging.message_logger import MessageLogger
from bot_data.db_context import DBContext
from bot_data.service.client_repository_service import ClientRepositoryService
from bot_data.service.known_user_repository_service import KnownUserRepositoryService
from bot_data.service.server_repository_service import ServerRepositoryService
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_repository_service import UserRepositoryService
from modules.base.abc.base_helper_abc import BaseHelperABC
from modules.base.service.base_helper_service import BaseHelperService
from modules.permission.abc.permission_service_abc import PermissionServiceABC
from modules.permission.service.permission_service import PermissionService
class Startup(StartupABC):
@@ -39,44 +25,32 @@ class Startup(StartupABC):
self._start_time = datetime.now()
self._config: Optional[ConfigurationABC] = None
self._feature_flags: Optional[FeatureFlagsSettings] = None
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC:
environment.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
configuration.add_environment_variables('KDB_')
configuration.add_environment_variables('DISCORD_')
configuration.add_json_file(f'config/appsettings.json', optional=False)
configuration.add_json_file(f'config/appsettings.{environment.environment_name}.json', optional=True)
configuration.add_json_file(f'config/appsettings.{environment.host_name}.json', optional=True)
configuration.add_configuration('Startup_StartTime', str(self._start_time))
self._config = configuration
self._feature_flags = configuration.get_configuration(FeatureFlagsSettings)
return configuration
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
services.add_logging()
services.add_translation()
if self._feature_flags.get_flag(FeatureFlagsEnum.core_module):
# custom logging
services.add_singleton(CustomFileLoggerABC, CommandLogger)
services.add_singleton(CustomFileLoggerABC, DatabaseLogger)
services.add_singleton(CustomFileLoggerABC, MessageLogger)
services.add_translation()
services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings))
# general services
services.add_transient(BaseHelperABC, BaseHelperService)
services.add_transient(MessageServiceABC, MessageService)
services.add_transient(ClientUtilsServiceABC, ClientUtilsService)
provider = services.build_service_provider()
# instantiate custom logger
for c in CustomFileLoggerABC.__subclasses__():
i: LoggerABC = provider.get_service(c)
# pipes
services.add_transient(DateTimeOffsetPipe)
# module services
services.add_singleton(PermissionServiceABC, PermissionService)
logger: LoggerABC = provider.get_service(LoggerABC)
for flag in [f for f in FeatureFlagsEnum]:
logger.debug(__name__, f'Loaded feature-flag: {flag} = {self._feature_flags.get_flag(flag)}')
# 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)
return services.build_service_provider()
return provider

View File

@@ -3,26 +3,6 @@ from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord import get_discord_collection
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
from modules.admin.command.deploy_command import DeployCommand
from modules.admin.command.restart_command import RestartCommand
from modules.admin.command.shutdown_command import ShutdownCommand
from modules.base.command.afk_command import AFKCommand
from modules.base.command.help_command import HelpCommand
from modules.base.command.info_command import InfoCommand
from modules.base.command.ping_command import PingCommand
from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent
from modules.moderator.command.purge_command import PurgeCommand
from modules.base.command.user_info_command import UserInfoCommand
from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent
from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent
from modules.base.events.base_on_message_event import BaseOnMessageEvent
from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent
from modules.boot_log.boot_log_on_ready_event import BootLogOnReadyEvent
from modules.database.database_on_ready_event import DatabaseOnReadyEvent
from modules.permission.events.permission_on_member_update_event import PermissionOnMemberUpdateEvent
from modules.permission.events.permission_on_ready_event import PermissionOnReadyEvent
class StartupDiscordExtension(StartupExtensionABC):
@@ -35,34 +15,4 @@ class StartupDiscordExtension(StartupExtensionABC):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_discord()
dc = get_discord_collection(services)
""" commands """
# admin
dc.add_command(RestartCommand)
dc.add_command(ShutdownCommand)
dc.add_command(DeployCommand)
# moderator
dc.add_command(PurgeCommand)
# simple
dc.add_command(AFKCommand)
dc.add_command(HelpCommand)
dc.add_command(InfoCommand)
dc.add_command(PingCommand)
dc.add_command(UserInfoCommand)
""" events """
# on_command_error
dc.add_event(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent)
# on_member_join
dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent)
# on_member_remove
dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberRemoveEvent)
# on_member_update
dc.add_event(DiscordEventTypesEnum.on_member_update.value, PermissionOnMemberUpdateEvent)
# on_message
dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent)
# on_voice_state_update
dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent)
# on_ready
dc.add_event(DiscordEventTypesEnum.on_ready.value, DatabaseOnReadyEvent)
dc.add_event(DiscordEventTypesEnum.on_ready.value, PermissionOnReadyEvent)
dc.add_event(DiscordEventTypesEnum.on_ready.value, BootLogOnReadyEvent) # has to be last
dcc = get_discord_collection(services)

View File

@@ -4,6 +4,7 @@ from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from bot_data.abc.migration_abc import MigrationABC
from bot_data.migration.auto_role_migration import AutoRoleMigration
from bot_data.migration.initial_migration import InitialMigration
from bot_data.service.migration_service import MigrationService
@@ -19,3 +20,4 @@ class StartupMigrationExtension(StartupExtensionABC):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(MigrationService)
services.add_transient(MigrationABC, InitialMigration)
services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2

View File

@@ -0,0 +1,39 @@
from typing import Optional
from cpl_core.application import StartupExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console, ForegroundColorEnum
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot.module_list import ModuleList
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
class StartupModuleExtension(StartupExtensionABC):
def __init__(self):
self._config: Optional[ConfigurationABC] = None
self._feature_flags: Optional[FeatureFlagsSettings] = None
self._modules = ModuleList.get_modules()
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
self._config = config
self._feature_flags = config.get_configuration(FeatureFlagsSettings)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
provider = services.build_service_provider()
dc_collection: DiscordCollectionABC = provider.get_service(DiscordCollectionABC)
for module_type in self._modules:
module = module_type(dc_collection)
if not self._feature_flags.get_flag(module.feature_flag):
continue
Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line(f'[{__name__}] Loaded module: {module_type}')
Console.color_reset()
module.configure_configuration(self._config, env)
module.configure_services(services, env)

View File

@@ -0,0 +1,51 @@
import os
from datetime import datetime
from typing import Callable, Type, Optional
from cpl_core.application import StartupExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from bot_core.configuration.bot_logging_settings import BotLoggingSettings
from bot_core.configuration.bot_settings import BotSettings
from modules.base.configuration.base_settings import BaseSettings
from modules.boot_log.configuration.boot_log_settings import BootLogSettings
from modules.permission.configuration.permission_settings import PermissionSettings
class StartupSettingsExtension(StartupExtensionABC):
def __init__(self):
self._start_time = datetime.now()
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC):
# this shit has to be done here because we need settings in subsequent startup extensions
environment.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
configuration.add_environment_variables('KDB_')
configuration.add_environment_variables('DISCORD_')
configuration.add_json_file(f'config/appsettings.json', optional=False)
configuration.add_json_file(f'config/appsettings.{environment.environment_name}.json', optional=True)
configuration.add_json_file(f'config/appsettings.{environment.host_name}.json', optional=True)
# load feature-flags
configuration.add_json_file(f'config/feature-flags.json', optional=False)
configuration.add_configuration('Startup_StartTime', str(self._start_time))
self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, BaseSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, BootLogSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
pass
@staticmethod
def _configure_settings_with_sub_settings(config: ConfigurationABC, settings: Type, list_atr: Callable, atr: Callable):
settings: Optional[settings] = config.get_configuration(settings)
if settings is None:
return
for sub_settings in list_atr(settings):
config.add_configuration(f'{type(sub_settings).__name__}_{atr(sub_settings)}', sub_settings)

View File

@@ -4,6 +4,12 @@
"bot_has_no_permission_message": "Ey!!!\nWas soll das?\nIch habe keine Berechtigungen :(\nScheiß System...",
"no_permission_message": "Nein!\nIch höre nicht auf dich ¯\\_(ツ)_/¯",
"not_implemented_yet": "Ey Alter, das kann ich noch nicht...",
"presence": {
"booting": "{} Ich fahre gerade hoch...",
"running": "{} Behalte Ruhe und iss Kekse :D",
"restart": "{} Muss neue Kekse holen...",
"shutdown": "{} Ich werde bestimmt wieder kommen..."
},
"errors": {
"error": "Es gab einen Fehler. Meld dich bitte bei einem Admin.",
"command_error": "Es gab einen Fehler beim bearbeiten des Befehls. Meld dich bitte bei einem Admin.",
@@ -37,7 +43,8 @@
"extension_already_loaded": "Fehler: Erweiterung wurde bereits geladen!",
"extension_not_loaded": "Fehler: Erweiterung wurde nicht geladen!",
"no_entry_point_error": "Fehler: Kein Eintrittspunkt!",
"extension_failed": "Fehler: Erweiterung ist fehlgeschlagen!"
"extension_failed": "Fehler: Erweiterung ist fehlgeschlagen!",
"bot_not_ready_yet": "Ey Alter! Gedulde dich doch mal! ..."
}
},
"modules": {
@@ -46,6 +53,57 @@
"shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)",
"deploy_message": "Der neue Stand wurde hochgeladen."
},
"auto_role": {
"list": {
"title": "Beobachtete Nachrichten:",
"description": "Von auto-role beobachtete Nachrichten:",
"auto_role_id": "auto-role Id",
"message_id": "Nachricht-Id"
},
"add": {
"success": "auto-role für die Nachricht {} wurde hinzugefügt :D",
"error": {
"not_found": "Nachricht {} in {} nicht gefunden!",
"already_exists": "auto-role für die Nachricht {} existiert bereits!"
}
},
"remove": {
"success": "auto-role {} wurde entfernt :D",
"error": {
"not_found": "auto-role {} nicht gefunden!"
}
},
"error": {
"nothing_found": "Keine auto-role Einträge gefunden."
},
"rule": {
"list": {
"title": "auto-role Regeln:",
"description": "Von auto-role angewendete Regeln:",
"auto_role_rule_id": "auto-role Regel Id",
"emoji": "Emoji",
"role": "Rolle"
},
"add": {
"success": "Regel {} -> {} für auto-role {} wurde hinzugefügt :D",
"error": {
"not_found": "Regel für auto-role {} nicht gefunden!",
"emoji_not_found": "Emoji {} für auto-role Regel {} nicht gefunden!",
"rule_not_found": "Rolle {} für auto-role Regel {} nicht gefunden!",
"already_exists": "Regel für auto-role {} existiert bereits!"
}
},
"remove": {
"success": "Regel für auto-role {} wurde entfernt :D",
"error": {
"not_found": "Regel für auto-role {} nicht gefunden!"
}
},
"error": {
"id_not_found": "Kein auto-role Eintrag mit der Id gefunden!"
}
}
},
"moderator": {
"purge_message": "Na gut..., ich lösche alle Nachrichten wenns sein muss."
},

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_core'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_core.abc'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -1,5 +1,7 @@
from abc import ABC, abstractmethod
from discord.ext.commands import Context
class ClientUtilsServiceABC(ABC):
@@ -14,3 +16,9 @@ class ClientUtilsServiceABC(ABC):
@abstractmethod
def get_client(self, dc_ic: int, guild_id: int): pass
@abstractmethod
async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: pass
@abstractmethod
async def presence_game(self, t_key: str): pass

View File

@@ -0,0 +1,60 @@
from abc import ABC, abstractmethod
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.logging import LoggingSettings, Logger, LoggingLevelEnum, LoggerABC
from cpl_core.time import TimeFormatSettings
from bot_core.configuration.file_logging_settings import FileLoggingSettings
class CustomFileLoggerABC(Logger, ABC):
@abstractmethod
def __init__(self, key: str, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
self._key = key
settings: LoggingSettings = config.get_configuration(f'{FileLoggingSettings.__name__}_{key}')
Logger.__init__(self, settings, time_format, env)
self._begin_log()
def _begin_log(self):
console_level = self._console.value
self._console = LoggingLevelEnum.OFF
self.info(__name__, f'Starting...')
self._console = LoggingLevelEnum(console_level)
def _get_string(self, name_list_as_str: str, level: LoggingLevelEnum, message: str) -> str:
names = name_list_as_str.split(' ')
log_level = level.name
string = f'<{self._get_datetime_now()}> [ {log_level} ]'
for name in names:
string += f' [ {name} ]'
string += f': {message}'
return string
def header(self, string: str):
super().header(string)
def trace(self, name: str, message: str):
name = f'{name} {self._key}'
super().trace(name, message)
def debug(self, name: str, message: str):
name = f'{name} {self._key}'
super().debug(name, message)
def info(self, name: str, message: str):
name = f'{name} {self._key}'
super().info(name, message)
def warn(self, name: str, message: str):
name = f'{name} {self._key}'
super().warn(name, message)
def error(self, name: str, message: str, ex: Exception = None):
name = f'{name} {self._key}'
super().error(name, message, ex)
def fatal(self, name: str, message: str, ex: Exception = None):
name = f'{name} {self._key}'
super().fatal(name, message, ex)

View File

@@ -0,0 +1,20 @@
from abc import abstractmethod
from cpl_core.application import StartupExtensionABC
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
class ModuleABC(StartupExtensionABC):
@abstractmethod
def __init__(self, dc: DiscordCollectionABC, feature_flag: FeatureFlagsEnum):
StartupExtensionABC.__init__(self)
self._dc = dc
self._feature_flag = feature_flag
@property
def feature_flag(self) -> FeatureFlagsEnum:
return self._feature_flag

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "0",
"Micro": "0.dev1"
"Micro": "0"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
@@ -16,10 +16,10 @@
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"cpl-core>=2022.7.0"
"cpl-core>=2022.10.0"
],
"DevDependencies": [
"cpl-cli>=2022.7.0"
"cpl-cli>=2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_core.configuration'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,33 @@
import traceback
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console, ForegroundColorEnum
from cpl_query.extension import List
from bot_core.configuration.file_logging_settings import FileLoggingSettings
class BotLoggingSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._files: List[FileLoggingSettings] = List(FileLoggingSettings)
@property
def files(self) -> List[FileLoggingSettings]:
return self._files
def from_dict(self, settings: dict):
try:
files = List(FileLoggingSettings)
for s in settings:
st = FileLoggingSettings()
settings[s]['Key'] = s
st.from_dict(settings[s])
files.append(st)
self._files = files
except Exception as e:
Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
Console.set_foreground_color(ForegroundColorEnum.default)

View File

@@ -12,28 +12,35 @@ class BotSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._servers: List[ServerSettings] = List()
self._technicians: list[int] = []
self._deploy_file_path = './'
self._servers: List[ServerSettings] = List(ServerSettings)
self._technicians: List[int] = List(int)
self._wait_for_restart = 2
self._wait_for_shutdown = 2
@property
def servers(self) -> List[ServerSettings]:
return self._servers
@property
def technicians(self) -> list[int]:
def technicians(self) -> List[int]:
return self._technicians
@property
def deploy_file_path(self) -> str:
return self._deploy_file_path
def wait_for_restart(self) -> int:
return self._wait_for_restart
@property
def wait_for_shutdown(self) -> int:
return self._wait_for_shutdown
def from_dict(self, settings: dict):
try:
self._technicians = settings["Technicians"]
self._deploy_file_path = settings["DeployFilesPath"]
self._wait_for_restart = settings["WaitForRestart"]
self._wait_for_shutdown = settings["WaitForShutdown"]
settings.pop("Technicians")
settings.pop("DeployFilesPath")
settings.pop("WaitForRestart")
settings.pop("WaitForShutdown")
servers = List(ServerSettings)
for s in settings:
st = ServerSettings()

View File

@@ -0,0 +1,18 @@
from enum import Enum
class FeatureFlagsEnum(Enum):
# modules
admin_module = 'AdminModule'
auto_role_module = 'AutoRoleModule'
base_module = 'BaseModule'
boot_log_module = 'BootLogModule'
core_module = 'CoreModule'
core_extension_module = 'CoreExtensionModule'
data_module = 'DataModule',
database_module = 'DatabaseModule',
moderator_module = 'ModeratorModule'
permission_module = 'PermissionModule'
# features
presence = 'Presence'

View File

@@ -0,0 +1,48 @@
import traceback
from typing import Optional, Callable
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
class FeatureFlagsSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._flags = {
# modules
FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48
FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54
FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48
FeatureFlagsEnum.boot_log_module.value: True, # 02.10.2022 #48
FeatureFlagsEnum.core_module.value: True, # 03.10.2022 #56
FeatureFlagsEnum.core_extension_module.value: True, # 03.10.2022 #56
FeatureFlagsEnum.data_module.value: True, # 03.10.2022 #56
FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48
FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48
FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48
# features
FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56
}
def get_flag(self, key: FeatureFlagsEnum) -> bool:
if key.value not in self._flags:
return False
return self._flags[key.value]
def _load_flag(self, settings: dict, key: FeatureFlagsEnum):
if key.value not in settings:
return
self._flags[key.value] = bool(settings[key.value])
def from_dict(self, settings: dict):
try:
for flag in [f.value for f in FeatureFlagsEnum]:
self._load_flag(settings, FeatureFlagsEnum(flag))
except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')

View File

@@ -0,0 +1,24 @@
import traceback
from cpl_core.console import Console
from cpl_core.logging import LoggingSettings
class FileLoggingSettings(LoggingSettings):
def __init__(self):
LoggingSettings.__init__(self)
self._key = ''
@property
def key(self) -> str:
return self._key
def from_dict(self, settings: dict):
try:
self._key = settings['Key']
super().from_dict(settings)
except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'bot_core.core_extension'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,21 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
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 bot_core.core_extension.core_extension_on_ready_event import CoreExtensionOnReadyEvent
class CoreExtensionModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_extension_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
self._dc.add_event(DiscordEventTypesEnum.on_ready.value, CoreExtensionOnReadyEvent)

View File

@@ -0,0 +1,32 @@
import asyncio
from cpl_core.logging import LoggerABC
from cpl_discord.events import OnReadyABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
class CoreExtensionOnReadyEvent(OnReadyABC):
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
t: TranslatePipe
):
OnReadyABC.__init__(self)
self._logger = logger
self._bot = bot
self._client_utils = client_utils
self._t = t
self._logger.info(__name__, f'Module {type(self)} loaded')
async def on_ready(self):
self._logger.debug(__name__, f'Module {type(self)} started')
await self._client_utils.presence_game('common.presence.running')
self._logger.trace(__name__, f'Module {type(self)} stopped')

View File

@@ -0,0 +1,32 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.events.core_on_ready_event import CoreOnReadyEvent
from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe
from bot_core.service.client_utils_service import ClientUtilsService
from bot_core.service.message_service import MessageService
class CoreModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(MessageServiceABC, MessageService)
services.add_transient(ClientUtilsServiceABC, ClientUtilsService)
# pipes
services.add_transient(DateTimeOffsetPipe)
self._dc.add_event(DiscordEventTypesEnum.on_ready.value, CoreOnReadyEvent)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'bot_core.events'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,30 @@
from cpl_core.logging import LoggerABC
from cpl_discord.events import OnReadyABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
class CoreOnReadyEvent(OnReadyABC):
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
t: TranslatePipe,
):
OnReadyABC.__init__(self)
self._logger = logger
self._bot = bot
self._client_utils = client_utils
self._t = t
self._logger.info(__name__, f'Module {type(self)} loaded')
async def on_ready(self):
self._logger.debug(__name__, f'Module {type(self)} started')
await self._client_utils.presence_game('common.presence.booting')
self._logger.trace(__name__, f'Module {type(self)} stopped')

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'bot_core.helper'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,9 @@
import discord
class LogMessageHelper:
@staticmethod
def get_log_string(message: discord.Message):
content = message.content.replace("\n", "\n\t")
return f'{message.author} @ {message.channel} -> \n\t{content}'

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'bot_core.logging'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,11 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.time import TimeFormatSettings
from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
class CommandLogger(CustomFileLoggerABC):
def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
CustomFileLoggerABC.__init__(self, 'Command', config, time_format, env)

View File

@@ -0,0 +1,11 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.time import TimeFormatSettings
from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
class DatabaseLogger(CustomFileLoggerABC):
def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
CustomFileLoggerABC.__init__(self, 'Database', config, time_format, env)

View File

@@ -0,0 +1,11 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.time import TimeFormatSettings
from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
class MessageLogger(CustomFileLoggerABC):
def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
CustomFileLoggerABC.__init__(self, 'Message', config, time_format, env)

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_core.pipes'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_core.service'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -1,8 +1,16 @@
import discord
from cpl_core.configuration import ConfigurationABC
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
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.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
@@ -11,16 +19,26 @@ class ClientUtilsService(ClientUtilsServiceABC):
def __init__(
self,
config: ConfigurationABC,
logger: LoggerABC,
bot: DiscordBotServiceABC,
servers: ServerRepositoryABC,
clients: ClientRepositoryABC,
db: DatabaseContextABC
message_service: MessageServiceABC,
db: DatabaseContextABC,
t: TranslatePipe,
feature_flags: FeatureFlagsSettings
):
ClientUtilsServiceABC.__init__(self)
self._config = config
self._logger = logger
self._bot = bot
self._servers = servers
self._clients = clients
self._message_service = message_service
self._db = db
self._t = t
self._feature_flags = feature_flags
def received_command(self, guild_id: int):
server = self._servers.get_server_by_discord_id(guild_id)
@@ -40,3 +58,20 @@ class ClientUtilsService(ClientUtilsServiceABC):
server = self._servers.get_server_by_discord_id(guild_id)
client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.server_id)
return client
async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool:
if self._config.get_configuration('IS_READY') == 'true':
return True
self._logger.debug(__name__, f'Bot is not ready yet {self._t.transform("common.errors.bot_not_ready_yet")}')
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), without_tracking=True)
return False
async def presence_game(self, t_key: str):
if not self._feature_flags.get_flag(FeatureFlagsEnum.presence):
return
import bot
name = self._t.transform(t_key).format(bot.__version__)
await self._bot.change_presence(activity=discord.Game(name=name))
self._logger.info(__name__, f'Set presence {name}')

View File

@@ -4,19 +4,20 @@ 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_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from discord.ext.commands import Context
from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.configuration.server_settings import ServerSettings
from bot_core.helper.log_message_helper import LogMessageHelper
from bot_core.logging.message_logger import MessageLogger
from bot_data.abc.client_repository_abc import ClientRepositoryABC
class MessageService(MessageServiceABC):
def __init__(self, config: ConfigurationABC, logger: LoggerABC, bot: DiscordBotServiceABC, clients: ClientRepositoryABC, db: DatabaseContextABC):
def __init__(self, config: ConfigurationABC, logger: MessageLogger, bot: DiscordBotServiceABC, clients: ClientRepositoryABC, db: DatabaseContextABC):
self._config = config
self._logger = logger
self._bot = bot
@@ -35,10 +36,11 @@ class MessageService(MessageServiceABC):
server_st: ServerSettings = self._config.get_configuration(f'ServerSettings_{message.guild.id}')
if not mass_delete:
await asyncio.sleep(server_st.message_delete_timer)
self._logger.debug(__name__, f'Try to delete message:\n\t{message}\n\t{message.content}')
self._logger.debug(__name__, f'Try to delete message: {LogMessageHelper.get_log_string(message)}')
guild_id = message.guild.id
try:
await message.delete()
await asyncio.sleep(server_st.message_delete_timer)
except Exception as e:
self._logger.error(__name__, f'Deleting message failed', e)
else:
@@ -104,8 +106,7 @@ class MessageService(MessageServiceABC):
await asyncio.sleep(wait_before_delete)
if is_persistent:
await self.delete_message(ctx.message, without_tracking)
return
if ctx.guild is not None:
await self.delete_messages(List(discord.Message, [msg, ctx.message]), ctx.guild.id)
await self.delete_message(msg, without_tracking)

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_data'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_data.abc'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,58 @@
from abc import ABC, abstractmethod
from typing import Optional
from cpl_query.extension import List
from bot_data.model.auto_role import AutoRole
from bot_data.model.auto_role_rule import AutoRoleRule
class AutoRoleRepositoryABC(ABC):
@abstractmethod
def __init__(self): pass
@abstractmethod
def get_auto_roles(self) -> List[AutoRole]: pass
@abstractmethod
def get_auto_role_by_id(self, id: int) -> AutoRole: pass
@abstractmethod
def find_auto_role_by_id(self, id: int) -> Optional[AutoRole]: pass
@abstractmethod
def get_auto_roles_by_server_id(self, id: int) -> AutoRole: pass
@abstractmethod
def get_auto_role_by_message_id(self, id: int) -> AutoRole: pass
@abstractmethod
def find_auto_role_by_message_id(self, id: int) -> Optional[AutoRole]: pass
@abstractmethod
def add_auto_role(self, auto_role: AutoRole): pass
@abstractmethod
def update_auto_role(self, auto_role: AutoRole): pass
@abstractmethod
def delete_auto_role(self, auto_role: AutoRole): pass
@abstractmethod
def get_auto_role_rules(self) -> List[AutoRoleRule]: pass
@abstractmethod
def get_auto_role_rule_by_id(self, id: int) -> AutoRoleRule: pass
@abstractmethod
def get_auto_role_rules_by_auto_role_id(self, id: int) -> List[AutoRoleRule]: pass
@abstractmethod
def add_auto_role_rule(self, auto_role: AutoRoleRule): pass
@abstractmethod
def update_auto_role_rule(self, auto_role: AutoRoleRule): pass
@abstractmethod
def delete_auto_role_rule(self, auto_role: AutoRoleRule): pass

View File

@@ -2,6 +2,7 @@ from abc import ABC, abstractmethod
class MigrationABC(ABC):
name = None
@abstractmethod
def __init__(self): pass

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "0",
"Micro": "0.dev1"
"Micro": "0"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
@@ -16,10 +16,10 @@
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"cpl-core>=2022.7.0"
"cpl-core>=2022.10.0"
],
"DevDependencies": [
"cpl-cli>=2022.7.0.post1"
"cpl-cli>=2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {

View File

@@ -0,0 +1,39 @@
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 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.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.service.auto_role_repository_service import AutoRoleRepositoryService
from bot_data.service.client_repository_service import ClientRepositoryService
from bot_data.service.known_user_repository_service import KnownUserRepositoryService
from bot_data.service.server_repository_service import ServerRepositoryService
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_repository_service import UserRepositoryService
class DataModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.data_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
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)
services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService)

View File

@@ -1,11 +1,12 @@
from cpl_core.database import DatabaseSettings
from cpl_core.database.context import DatabaseContext
from cpl_core.logging import LoggerABC
from bot_core.logging.database_logger import DatabaseLogger
class DBContext(DatabaseContext):
def __init__(self, logger: LoggerABC):
def __init__(self, logger: DatabaseLogger):
self._logger = logger

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_data.migration'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,47 @@
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.migration_abc import MigrationABC
from bot_data.db_context import DBContext
class AutoRoleMigration(MigrationABC):
name = '0.2.1_AutoRoleMigration'
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 `AutoRoles` (
`AutoRoleId` BIGINT NOT NULL AUTO_INCREMENT,
`ServerId` BIGINT,
`DiscordMessageId` BIGINT NOT NULL,
`CreatedAt` DATETIME(6),
`LastModifiedAt` DATETIME(6),
PRIMARY KEY(`AutoRoleId`)
);
""")
)
self._cursor.execute(
str(f"""
CREATE TABLE IF NOT EXISTS `AutoRoleRules` (
`AutoRoleRuleId` BIGINT NOT NULL AUTO_INCREMENT,
`AutoRoleId` BIGINT,
`DiscordEmojiName` VARCHAR(64),
`DiscordRoleId` BIGINT NOT NULL,
`CreatedAt` DATETIME(6),
`LastModifiedAt` DATETIME(6),
PRIMARY KEY(`AutoRoleRuleId`)
);
""")
)
def downgrade(self):
self._cursor.execute('DROP TABLE `AutoRole`;')
self._cursor.execute('DROP TABLE `AutoRoleRules`;')

View File

@@ -1,12 +1,13 @@
from cpl_core.logging import LoggerABC
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.migration_abc import MigrationABC
from bot_data.db_context import DBContext
class InitialMigration(MigrationABC):
name = '0.1_InitialMigration'
def __init__(self, logger: LoggerABC, db: DBContext):
def __init__(self, logger: DatabaseLogger, db: DBContext):
MigrationABC.__init__(self)
self._logger = logger
self._db = db
self._cursor = db.cursor

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_data.model'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,85 @@
from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC
class AutoRole(TableABC):
def __init__(self, server_id: int, dc_message_id: int, created_at: datetime=None, modified_at: datetime=None, id=0):
self._auto_role_id = id
self._server_id = server_id
self._discord_message_id = dc_message_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 auto_role_id(self) -> int:
return self._auto_role_id
@property
def server_id(self) -> int:
return self._server_id
@property
def discord_message_id(self) -> int:
return self._discord_message_id
@staticmethod
def get_select_all_string() -> str:
return str(f"""
SELECT * FROM `AutoRoles`;
""")
@staticmethod
def get_select_by_id_string(id: int) -> str:
return str(f"""
SELECT * FROM `AutoRoles`
WHERE `AutoRoleId` = {id};
""")
@staticmethod
def get_select_by_server_id_string(id: int) -> str:
return str(f"""
SELECT * FROM `AutoRoles`
WHERE `ServerId` = {id};
""")
@staticmethod
def get_select_by_message_id_string(id: int) -> str:
return str(f"""
SELECT * FROM `AutoRoles`
WHERE `DiscordMessageId` = {id};
""")
@property
def insert_string(self) -> str:
return str(f"""
INSERT INTO `AutoRoles` (
`ServerId`, `DiscordMessageId`, `CreatedAt`, `LastModifiedAt`
) VALUES (
{self._server_id},
{self._discord_message_id},
'{self._created_at}',
'{self._modified_at}'
);
""")
@property
def udpate_string(self) -> str:
return str(f"""
UPDATE `AutoRoles`
SET `ServerId` = {self._server_id},
`DiscordMessageId` = {self._discord_message_id},
`LastModifiedAt` = '{self._modified_at}'
WHERE `AutoRoleId` = {self._auto_role_id};
""")
@property
def delete_string(self) -> str:
return str(f"""
DELETE FROM `AutoRoles`
WHERE `AutoRoleId` = {self._auto_role_id};
""")

View File

@@ -0,0 +1,85 @@
from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC
class AutoRoleRule(TableABC):
def __init__(self, auto_role_id: int, discord_emoji_name: str, discord_role_id: int, created_at: datetime=None, modified_at: datetime=None, id=0):
self._auto_role_rule_id = id
self._auto_role_id = auto_role_id
self._discord_emoji_name = discord_emoji_name
self._discord_role_id = discord_role_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 auto_role_rule_id(self) -> int:
return self._auto_role_rule_id
@property
def auto_role_id(self) -> int:
return self._auto_role_id
@property
def emoji_name(self) -> str:
return self._discord_emoji_name
@property
def role_id(self) -> int:
return self._discord_role_id
@staticmethod
def get_select_all_string() -> str:
return str(f"""
SELECT * FROM `AutoRoleRules`;
""")
@staticmethod
def get_select_by_id_string(id: int) -> str:
return str(f"""
SELECT * FROM `AutoRoleRules`
WHERE `AutoRoleRuleId` = {id};
""")
@staticmethod
def get_select_by_auto_role_id_string(id: int) -> str:
return str(f"""
SELECT * FROM `AutoRoleRules`
WHERE `AutoRoleId` = {id};
""")
@property
def insert_string(self) -> str:
return str(f"""
INSERT INTO `AutoRoleRules` (
`AutoRoleId`, `DiscordEmojiName`, `DiscordRoleId`, `CreatedAt`, `LastModifiedAt`
) VALUES (
{self._auto_role_id},
'{self._discord_emoji_name}',
{self._discord_role_id},
'{self._created_at}',
'{self._modified_at}'
);
""")
@property
def udpate_string(self) -> str:
return str(f"""
UPDATE `AutoRoleRules`
SET `AutoRoleId` = {self._auto_role_id},
`DiscordEmojiName` = {self._discord_emoji_name},
`DiscordRoleId` = {self._discord_role_id},
`LastModifiedAt` = '{self._modified_at}'
WHERE `AutoRoleRuleId` = {self._auto_role_rule_id};
""")
@property
def delete_string(self) -> str:
return str(f"""
DELETE FROM `AutoRoleRules`
WHERE `AutoRoleRuleId` = {self._auto_role_rule_id};
""")

View File

@@ -60,12 +60,12 @@ class Server(TableABC):
UPDATE `Servers`
SET `DiscordServerId` = {self._discord_server_id},
`LastModifiedAt` = '{self._modified_at}'
WHERE `Id` = {self._server_id};
WHERE `ServerId` = {self._server_id};
""")
@property
def delete_string(self) -> str:
return str(f"""
DELETE FROM `Servers`
WHERE `Id` = {self._server_id};
WHERE `ServerId` = {self._server_id};
""")

View File

@@ -15,7 +15,7 @@ __title__ = 'bot_data.service'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,170 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_data.model.auto_role_rule import AutoRoleRule
class AutoRoleRepositoryService(AutoRoleRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC):
self._logger = logger
self._context = db_context
AutoRoleRepositoryABC.__init__(self)
def get_auto_roles(self) -> List[AutoRole]:
auto_roles = List(AutoRole)
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_all_string()}')
results = self._context.select(AutoRole.get_select_all_string())
for result in results:
auto_roles.append(AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
))
return auto_roles
def get_auto_role_by_id(self, id: int) -> AutoRole:
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_by_id_string(id)}')
result = self._context.select(AutoRole.get_select_by_id_string(id))[0]
return AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
)
def find_auto_role_by_id(self, id: int) -> Optional[AutoRole]:
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_by_id_string(id)}')
result = self._context.select(AutoRole.get_select_by_id_string(id))
if result is None or len(result) == 0:
return None
result = result[0]
return AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
)
def get_auto_roles_by_server_id(self, id: int) -> List[AutoRole]:
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_by_server_id_string(id)}')
auto_roles = List(AutoRole)
results = self._context.select(AutoRole.get_select_by_server_id_string(id))
for result in results:
auto_roles.append(AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
))
return auto_roles
def get_auto_role_by_message_id(self, id: int) -> AutoRole:
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_by_message_id_string(id)}')
result = self._context.select(AutoRole.get_select_by_message_id_string(id))[0]
return AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
)
def find_auto_role_by_message_id(self, id: int) -> Optional[AutoRole]:
self._logger.trace(__name__, f'Send SQL command: {AutoRole.get_select_by_message_id_string(id)}')
result = self._context.select(AutoRole.get_select_by_message_id_string(id))
if result is None or len(result) == 0:
return None
result = result[0]
return AutoRole(
result[1],
result[2],
result[3],
result[4],
id=result[0]
)
def add_auto_role(self, auto_role: AutoRole):
self._logger.trace(__name__, f'Send SQL command: {auto_role.insert_string}')
self._context.cursor.execute(auto_role.insert_string)
def update_auto_role(self, auto_role: AutoRole):
self._logger.trace(__name__, f'Send SQL command: {auto_role.udpate_string}')
self._context.cursor.execute(auto_role.udpate_string)
def delete_auto_role(self, auto_role: AutoRole):
self._logger.trace(__name__, f'Send SQL command: {auto_role.delete_string}')
self._context.cursor.execute(auto_role.delete_string)
def get_auto_role_rules(self) -> List[AutoRoleRule]:
auto_role_rules = List(AutoRoleRule)
self._logger.trace(__name__, f'Send SQL command: {AutoRoleRule.get_select_all_string()}')
results = self._context.select(AutoRoleRule.get_select_all_string())
for result in results:
auto_role_rules.append(AutoRoleRule(
result[1],
result[2],
result[3],
result[4],
result[5],
id=result[0]
))
return auto_role_rules
def get_auto_role_rule_by_id(self, id: int) -> AutoRoleRule:
self._logger.trace(__name__, f'Send SQL command: {AutoRoleRule.get_select_by_id_string(id)}')
result = self._context.select(AutoRoleRule.get_select_by_id_string(id))[0]
return AutoRoleRule(
result[1],
result[2],
result[3],
result[4],
result[5],
id=result[0]
)
def get_auto_role_rules_by_auto_role_id(self, id: int) -> List[AutoRoleRule]:
auto_role_rules = List(AutoRoleRule)
self._logger.trace(__name__, f'Send SQL command: {AutoRoleRule.get_select_by_auto_role_id_string(id)}')
results = self._context.select(AutoRoleRule.get_select_by_auto_role_id_string(id))
for result in results:
auto_role_rules.append(AutoRoleRule(
result[1],
result[2],
result[3],
result[4],
result[5],
id=result[0]
))
return auto_role_rules
def add_auto_role_rule(self, auto_role_rule: AutoRoleRule):
self._logger.trace(__name__, f'Send SQL command: {auto_role_rule.insert_string}')
self._context.cursor.execute(auto_role_rule.insert_string)
def update_auto_role_rule(self, auto_role_rule: AutoRoleRule):
self._logger.trace(__name__, f'Send SQL command: {auto_role_rule.udpate_string}')
self._context.cursor.execute(auto_role_rule.udpate_string)
def delete_auto_role_rule(self, auto_role_rule: AutoRoleRule):
self._logger.trace(__name__, f'Send SQL command: {auto_role_rule.delete_string}')
self._context.cursor.execute(auto_role_rule.delete_string)

View File

@@ -1,8 +1,9 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.client import Client
@@ -10,7 +11,7 @@ from bot_data.model.client import Client
class ClientRepositoryService(ClientRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
self._logger = logger
self._context = db_context

View File

@@ -1,16 +1,17 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_query.extension import List
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
from cpl_core.database.context import DatabaseContextABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.known_user import KnownUser
class KnownUserRepositoryService(KnownUserRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
self._logger = logger
self._context = db_context

View File

@@ -2,22 +2,23 @@ from typing import Type
from cpl_core.database.context import DatabaseContextABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_core.logging import LoggerABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.migration_abc import MigrationABC
from bot_data.model.migration_history import MigrationHistory
class MigrationService:
def __init__(self, logger: LoggerABC, services: ServiceProviderABC, db: DatabaseContextABC):
def __init__(self, logger: DatabaseLogger, services: ServiceProviderABC, db: DatabaseContextABC):
self._logger = logger
self._services = services
self._db = db
self._cursor = db.cursor
self._migrations: list[Type[MigrationABC]] = MigrationABC.__subclasses__()
self._migrations = List(type, MigrationABC.__subclasses__()).order_by(lambda x: x.name)
def migrate(self):
self._logger.info(__name__, f"Running Migrations")
@@ -31,11 +32,10 @@ class MigrationService:
# 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 = self._db.select(MigrationHistory.get_select_by_id_string(migration_id))
self._logger.trace(__name__, str(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}")
self._logger.debug(__name__, f"Running Migration {migration_id}")
migration_as_service: MigrationABC = self._services.get_service(migration)
migration_as_service.upgrade()
self._cursor.execute(MigrationHistory(migration_id).insert_string)

View File

@@ -1,15 +1,16 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
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.model.server import Server
class ServerRepositoryService(ServerRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC):
self._logger = logger
self._context = db_context

View File

@@ -1,18 +1,18 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.user_joined_server_repository_abc import \
UserJoinedServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.user import User
from bot_data.model.user_joined_server import UserJoinedServer
class UserJoinedServerRepositoryService(UserJoinedServerRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, users: UserRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, users: UserRepositoryABC):
self._logger = logger
self._context = db_context

View File

@@ -1,17 +1,17 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_query.extension import List, IterableABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
class UserJoinedVoiceChannelRepositoryService(UserJoinedVoiceChannelRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, users: UserRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, users: UserRepositoryABC):
self._logger = logger
self._context = db_context

View File

@@ -1,8 +1,9 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
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.user_repository_abc import UserRepositoryABC
from bot_data.model.user import User
@@ -10,7 +11,7 @@ from bot_data.model.user import User
class UserRepositoryService(UserRepositoryABC):
def __init__(self, logger: LoggerABC, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
self._logger = logger
self._context = db_context

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.admin'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -16,10 +16,10 @@
"LicenseName": "",
"LicenseDescription": "",
"Dependencies": [
"cpl-core>=2022.7.0.post5"
"cpl-core>=2022.10.0.post5"
],
"DevDependencies": [
"cpl-cli>=2022.7.0.post2"
"cpl-cli>=2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {

View 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.admin.command.restart_command import RestartCommand
from modules.admin.command.shutdown_command import ShutdownCommand
class AdminModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.admin_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
# commands
self._dc.add_command(RestartCommand)
self._dc.add_command(ShutdownCommand)
# events

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.admin.command'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -1,106 +0,0 @@
import os
import shutil
import zipfile
from io import BytesIO
import requests
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
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.configuration.bot_settings import BotSettings
class DeployCommand(DiscordCommandABC):
def __init__(
self,
logger: LoggerABC,
config: ConfigurationABC,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
translate: TranslatePipe,
bot_settings: BotSettings,
env: ApplicationEnvironmentABC
):
DiscordCommandABC.__init__(self)
self._logger = logger
self._config = config
self._message_service = message_service
self._bot = bot
self._client_utils = client_utils
self._t = translate
self._bot_settings = bot_settings
self._env = env
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.dm_only()
async def deploy(self, ctx: Context, old_version: str):
self._logger.debug(__name__, f'Received command deploy {ctx}')
if ctx.author.id not in self._bot_settings.technicians:
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
self._logger.trace(__name__, f'Finished deploy command')
return
blacklist = ['venv', 'start', 'latest']
if old_version in blacklist:
raise Exception(self._t.transform('common.errors.bad_argument'))
if len(ctx.message.attachments) > 1:
raise IndexError(self._t.transform('common.errors.too_many_arguments'))
if len(ctx.message.attachments) < 1:
raise IndexError(self._t.transform('common.errors.missing_required_argument'))
try:
attachment_url = ctx.message.attachments[0].url
file_request = requests.get(attachment_url)
except Exception as e:
self._logger.error(__name__, f'An error occurred downloading the zip file', e)
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.command_error'))
return
try:
self._logger.debug(__name__, f'Deploying files to {self._bot_settings.deploy_file_path}/latest')
deploy_path = f'{self._bot_settings.deploy_file_path}/latest'
deploy_old_path = f'{self._bot_settings.deploy_file_path}/{old_version}'
if os.path.exists(deploy_path):
self._logger.debug(__name__, f'Moving files of {self._bot_settings.deploy_file_path}/latest to {self._bot_settings.deploy_file_path}/{old_version}')
shutil.move(deploy_path, deploy_old_path)
os.makedirs(deploy_path)
file = zipfile.ZipFile(BytesIO(file_request.content))
file.extractall(deploy_path)
except Exception as e:
self._logger.error(__name__, f'An error occurred extracting the zip file', e)
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.command_error'), without_tracking=True)
return
try:
shutil.copy(f'{deploy_old_path}/bot/config/appsettings.{self._env.host_name}.json', f'{deploy_path}/bot/config/appsettings.{self._env.host_name}.json')
except Exception as e:
self._logger.error(__name__, f'An error occurred copying config file', e)
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.command_error'), without_tracking=True)
return
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.deploy_message'), without_tracking=True)
self._config.add_configuration('IS_RESTART', 'true')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.restart_message'), without_tracking=True)
await self._bot.stop_async()
self._logger.trace(__name__, f'Finished deploy command')

View File

@@ -1,5 +1,7 @@
import asyncio
import discord
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
@@ -8,6 +10,8 @@ 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.configuration.bot_settings import BotSettings
from bot_core.logging.command_logger import CommandLogger
from modules.permission.abc.permission_service_abc import PermissionServiceABC
@@ -15,13 +19,14 @@ class RestartCommand(DiscordCommandABC):
def __init__(
self,
logger: LoggerABC,
logger: CommandLogger,
config: ConfigurationABC,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
translate: TranslatePipe,
permissions: PermissionServiceABC,
settings: BotSettings
):
DiscordCommandABC.__init__(self)
@@ -32,13 +37,17 @@ class RestartCommand(DiscordCommandABC):
self._client_utils = client_utils
self._t = translate
self._permissions = permissions
self._settings = settings
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def restart(self, ctx: Context):
self._logger.debug(__name__, f'Received command restart {ctx}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
if not self._permissions.is_member_moderator(ctx.author):
@@ -47,6 +56,8 @@ class RestartCommand(DiscordCommandABC):
return
self._config.add_configuration('IS_RESTART', 'true')
await self._client_utils.presence_game('common.presence.restart')
await asyncio.sleep(self._settings.wait_for_restart)
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.restart_message'))
await self._bot.stop_async()

View File

@@ -1,5 +1,7 @@
import asyncio
import discord
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
@@ -8,6 +10,8 @@ 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.configuration.bot_settings import BotSettings
from bot_core.logging.command_logger import CommandLogger
from modules.permission.abc.permission_service_abc import PermissionServiceABC
@@ -15,13 +19,14 @@ class ShutdownCommand(DiscordCommandABC):
def __init__(
self,
logger: LoggerABC,
logger: CommandLogger,
config: ConfigurationABC,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
translate: TranslatePipe,
permissions: PermissionServiceABC,
settings: BotSettings
):
DiscordCommandABC.__init__(self)
@@ -32,13 +37,17 @@ class ShutdownCommand(DiscordCommandABC):
self._client_utils = client_utils
self._t = translate
self._permissions = permissions
self._settings = settings
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def shutdown(self, ctx: Context):
self._logger.debug(__name__, f'Received command shutdown {ctx}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
if not self._permissions.is_member_moderator(ctx.author):
@@ -46,7 +55,9 @@ class ShutdownCommand(DiscordCommandABC):
self._logger.trace(__name__, f'Finished shutdown command')
return
await self._client_utils.presence_game('common.presence.shutdown')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.shutdown_message'))
await asyncio.sleep(self._settings.wait_for_shutdown)
await self._bot.stop_async()
self._logger.trace(__name__, f'Finished shutdown command')

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'modules.auto_role'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,46 @@
{
"ProjectSettings": {
"Name": "auto-role",
"Version": {
"Major": "0",
"Minor": "0",
"Micro": "0"
},
"Author": "",
"AuthorEmail": "",
"Description": "",
"LongDescription": "",
"URL": "",
"CopyrightDate": "",
"CopyrightName": "",
"LicenseName": "",
"LicenseDescription": "",
"Dependencies": [
"cpl-core>=2022.10.0.post5"
],
"DevDependencies": [
"cpl-cli>=2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {
"linux": ""
},
"Classifiers": []
},
"BuildSettings": {
"ProjectType": "library",
"SourcePath": "",
"OutputPath": "../../dist",
"Main": "admin.main",
"EntryPoint": "admin",
"IncludePackageData": false,
"Included": [],
"Excluded": [
"*/__pycache__",
"*/logs",
"*/tests"
],
"PackageData": {},
"ProjectReferences": []
}
}

View File

@@ -0,0 +1,29 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
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.auto_role.command.auto_role_group import AutoRoleGroup
from modules.auto_role.events.auto_role_on_raw_reaction_add import AutoRoleOnRawReactionAddEvent
from modules.auto_role.events.auto_role_on_raw_reaction_remove import AutoRoleOnRawReactionRemoveEvent
from modules.auto_role.helper.reaction_handler import ReactionHandler
class AutoRoleModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.auto_role_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(ReactionHandler)
# commands
self._dc.add_command(AutoRoleGroup)
# events
self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_add.value, AutoRoleOnRawReactionAddEvent)
self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_remove.value, AutoRoleOnRawReactionRemoveEvent)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'modules.auto_role.command'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,304 @@
from typing import List as TList, Optional
import discord
from cpl_core.database.context import DatabaseContextABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.container import TextChannel
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
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.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_data.model.auto_role_rule import AutoRoleRule
from modules.permission.abc.permission_service_abc import PermissionServiceABC
class AutoRoleGroup(DiscordCommandABC):
def __init__(
self,
logger: CommandLogger,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
translate: TranslatePipe,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
db_context: DatabaseContextABC,
permission_service: PermissionServiceABC,
):
DiscordCommandABC.__init__(self)
self._logger = logger
self._message_service = message_service
self._bot = bot
self._client_utils = client_utils
self._t = translate
self._servers = servers
self._auto_roles = auto_roles
self._db_context = db_context
self._permissions = permission_service
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.hybrid_group(name="auto-role")
@commands.guild_only()
async def auto_role(self, ctx: Context):
pass
@auto_role.command(alias='auto-roles')
@commands.guild_only()
async def list(self, ctx: Context, wait: int = None):
self._logger.debug(__name__, f'Received command auto-role 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 auto-role list')
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)
)
auto_roles = self._auto_roles.get_auto_roles()
if auto_roles.count() < 1:
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.error.nothing_found'))
self._logger.trace(__name__, f'Finished command auto-role list')
return
auto_role_id = ''
message_id = ''
for auto_role in auto_roles:
auto_role_id += f'\n{auto_role.auto_role_id}'
message_id += f'\n{auto_role.discord_message_id}'
embed.add_field(name=self._t.transform('modules.auto_role.list.auto_role_id'), value=auto_role_id, inline=True)
embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=message_id, inline=True)
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)
self._logger.trace(__name__, f'Finished command auto-role list')
@auto_role.command()
@commands.guild_only()
async def add(self, ctx: Context, channel: discord.TextChannel, message_id: str):
self._logger.debug(__name__, f'Received command auto-role add {ctx} {message_id}')
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 auto-role add')
return
message = List(discord.Message, [message async for message in channel.history(limit=50)]).where(lambda m: m.id == int(message_id)).single_or_default()
if message is None:
self._logger.debug(__name__, f'Message with id {message_id} not found in {channel.name}')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.not_found').format(message_id, channel.name))
self._logger.trace(__name__, f'Finished command auto-role add')
return
if self._auto_roles.find_auto_role_by_message_id(int(message_id)) is not None:
self._logger.debug(__name__, f'auto-role for message {message_id} already exists')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.already_exists').format(message_id))
self._logger.trace(__name__, f'Finished command auto-role add')
return
server_id = self._servers.get_server_by_discord_id(ctx.guild.id).server_id
self._auto_roles.add_auto_role(AutoRole(server_id, int(message_id)))
self._db_context.save_changes()
self._logger.info(__name__, f'Saved auto-role for message {message_id} at server {server_id}')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.success').format(message_id))
self._logger.trace(__name__, f'Finished command auto-role add')
@add.autocomplete('message_id')
async def add_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
channel = discord.utils.get(interaction.guild.text_channels, id=interaction.channel_id)
try:
channel_from_data = interaction.data['options'][0]['options'][0]['value']
found_channel = discord.utils.get(interaction.guild.text_channels, id=int(channel_from_data))
if found_channel is not None:
channel = found_channel
finally:
pass
messages = [message async for message in channel.history(limit=10)]
return [app_commands.Choice(name=f'{message.author}@{message.created_at}', value=str(message.id)) for message in messages if current in str(message.id)]
@auto_role.command()
@commands.guild_only()
async def remove(self, ctx: Context, auto_role: int):
self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role}')
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 auto-role remove')
return
auto_role_from_db = self._auto_roles.find_auto_role_by_id(auto_role)
if auto_role_from_db is None:
self._logger.debug(__name__, f'auto-role {auto_role} not found')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.remove.error.not_found').format(auto_role))
self._logger.trace(__name__, f'Finished command auto-role remove')
return
for rule in self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role_from_db.auto_role_id):
self._auto_roles.delete_auto_role_rule(rule)
self._logger.info(__name__, f'Removed auto-role rule {rule.role_id}')
self._auto_roles.delete_auto_role(auto_role_from_db)
self._db_context.save_changes()
self._logger.info(__name__, f'Removed auto-role {auto_role}')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.remove.success').format(auto_role))
self._logger.trace(__name__, f'Finished command auto-role remove')
@remove.autocomplete('auto_role')
async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id)
return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles]
@auto_role.group()
@commands.guild_only()
async def rule(self, ctx: Context):
pass
@rule.command(alias='rules')
@commands.guild_only()
async def list(self, ctx: Context, auto_role: int, wait: int = None):
self._logger.debug(__name__, f'Received command auto-role rule 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 auto-role rule list')
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)
)
rules = self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role)
if rules.count() < 1:
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.error.id_not_found'))
self._logger.trace(__name__, f'Finished command auto-role rule list')
return
auto_role_rule_id = ''
emoji = ''
role = ''
for rule in rules:
auto_role_rule_id += f'\n{rule.role_id}'
emoji += f'\n{rule.emoji_name}'
role += f'\n{ctx.guild.get_role(rule.role_id)}'
embed.add_field(name=self._t.transform('modules.auto_role.rule.list.auto_role_rule_id'), value=auto_role_rule_id, inline=True)
embed.add_field(name=self._t.transform('modules.auto_role.rule.list.emoji'), value=emoji, inline=True)
embed.add_field(name=self._t.transform('modules.auto_role.rule.list.role'), value=role, inline=True)
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)
self._logger.trace(__name__, f'Finished command auto-role rule list')
@list.autocomplete('auto_role')
async def list_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id)
return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles]
@rule.command()
@commands.guild_only()
async def add(self, ctx: Context, auto_role: int, emoji_name: str, role_id: str):
self._logger.debug(__name__, f'Received command auto-role add {ctx} {auto_role}')
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 auto-role rule add')
return
emoji = discord.utils.get(self._bot.emojis, name=emoji_name)
if emoji is None:
self._logger.debug(__name__, f'auto-role rule add emoji {emoji_name} not found')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.error.emoji_not_found').format(emoji_name, auto_role))
self._logger.trace(__name__, f'Finished command auto-role add')
return
role = ctx.guild.get_role(int(role_id))
if role is None:
self._logger.debug(__name__, f'auto-role rule add role {role_id} not found')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.error.role_not_found').format(role_id, auto_role))
self._logger.trace(__name__, f'Finished command auto-role add')
return
auto_role_from_db = self._auto_roles.get_auto_role_by_id(auto_role)
if auto_role_from_db is None:
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.remove.error.not_found').format(auto_role))
self._logger.trace(__name__, f'Finished command auto-role rule add')
return
if self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role).where(lambda r: r.emoji_name == emoji.name and int(role_id) == role.id).count() > 0:
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.already_exists').format(auto_role))
self._logger.trace(__name__, f'Finished command auto-role rule add')
return
self._auto_roles.add_auto_role_rule(AutoRoleRule(auto_role, emoji_name, int(role_id)))
self._db_context.save_changes()
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.success').format(emoji, role.name, auto_role))
self._logger.trace(__name__, f'Finished command auto-role rule add')
@add.autocomplete('auto_role')
async def add_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id)
return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles]
@add.autocomplete('emoji_name')
async def add_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
emojis = interaction.guild.emojis
return [app_commands.Choice(name=emoji.name, value=emoji.name) for emoji in emojis]
@add.autocomplete('role_id')
async def rule_add_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
roles = interaction.guild.roles
return [app_commands.Choice(name=role.name, value=str(role.id)) for role in roles]
@rule.command()
@commands.guild_only()
async def remove(self, ctx: Context, auto_role_rule: int):
self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role_rule}')
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 auto-role remove')
return
auto_role_from_db = self._auto_roles.get_auto_role_rule_by_id(auto_role_rule)
if auto_role_from_db is None:
self._logger.debug(__name__, f'auto-role rule {auto_role_rule} not found')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.remove.error.not_found').format(auto_role_rule))
self._logger.trace(__name__, f'Finished command auto-role rule remove')
return
self._auto_roles.delete_auto_role_rule(auto_role_from_db)
self._db_context.save_changes()
self._logger.info(__name__, f'Removed auto-role rule {auto_role_rule}')
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.remove.success').format(auto_role_rule))
self._logger.trace(__name__, f'Finished command auto-role remove')
@remove.autocomplete('auto_role_rule')
async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
rules = self._auto_roles.get_auto_role_rules()
return [app_commands.Choice(name=f'{rule.auto_role_rule_id} {rule.emoji_name} {interaction.guild.get_role(int(rule.role_id))}', value=rule.auto_role_rule_id) for rule in
rules]

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'modules.auto_role.events'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,34 @@
from cpl_core.logging import LoggerABC
from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC
from cpl_discord.service import DiscordBotServiceABC
from discord import RawReactionActionEvent
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from modules.auto_role.helper.reaction_handler import ReactionHandler
class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC):
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
reaction_handler: ReactionHandler
):
OnRawReactionAddABC.__init__(self)
self._logger = logger
self._bot = bot
self._servers = servers
self._auto_roles = auto_roles
self._reaction_handler = reaction_handler
async def on_raw_reaction_add(self, payload: RawReactionActionEvent):
self._logger.debug(__name__, f'Module {type(self)} started')
await self._reaction_handler.handle(payload, 'add')
self._logger.debug(__name__, f'Module {type(self)} stopped')

View File

@@ -0,0 +1,34 @@
from cpl_core.logging import LoggerABC
from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC
from cpl_discord.service import DiscordBotServiceABC
from discord import RawReactionActionEvent
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from modules.auto_role.helper.reaction_handler import ReactionHandler
class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC):
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
reaction_handler: ReactionHandler
):
OnRawReactionRemoveABC.__init__(self)
self._logger = logger
self._bot = bot
self._servers = servers
self._auto_roles = auto_roles
self._reaction_handler = reaction_handler
async def on_raw_reaction_remove(self, payload: RawReactionActionEvent):
self._logger.debug(__name__, f'Module {type(self)} started')
await self._reaction_handler.handle(payload, 'remove')
self._logger.debug(__name__, f'Module {type(self)} stopped')

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
bot Keksdose bot
~~~~~~~~~~~~~~~~~~~
Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'modules.auto_role.helper'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '0.2.3'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -0,0 +1,70 @@
import discord
from cpl_core.logging import LoggerABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from discord import RawReactionActionEvent
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_data.model.auto_role_rule import AutoRoleRule
class ReactionHandler:
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC
):
self._logger = logger
self._bot = bot
self._servers = servers
self._auto_roles = auto_roles
self._message_ids = self._auto_roles.get_auto_roles().select(lambda x: x.discord_message_id)
self._roles = self._auto_roles.get_auto_roles()
async def handle(self, payload: RawReactionActionEvent, r_type=None) -> None:
self._logger.trace(__name__, f'Handle reaction {payload} {r_type}')
if payload.message_id not in self._message_ids:
self._logger.debug(__name__, f'Message not in auto-roles - skipping')
return
guild = self._bot.get_guild(payload.guild_id)
user = guild.get_member(payload.user_id)
if user is None:
self._logger.warn(__name__, f'User {payload.user_id} in {guild.name} not found - skipping')
return
if user.bot:
self._logger.debug(__name__, f'User is bot - skipping')
return
emoji = payload.emoji.name
auto_role: AutoRole = self._roles.where(lambda x: x.discord_message_id == payload.message_id).single_or_default()
if auto_role is None:
self._logger.debug(__name__, f'auto-role for message not found - skipping')
return
rules: List[AutoRoleRule] = self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role.auto_role_id)
if rules.count() == 0:
self._logger.debug(__name__, f'auto-role rules not found - skipping')
return
for rule in rules:
if emoji != rule.emoji_name:
continue
if r_type == 'add':
role = guild.get_role(rule.role_id)
self._logger.debug(__name__, f'Assign role {role.name} to {user.name}')
await user.add_roles(role)
elif r_type == 'remove':
role = guild.get_role(rule.role_id)
self._logger.debug(__name__, f'Remove role {role.name} to {user.name}')
await user.remove_roles(role)
else:
self._logger.warn(__name__, f'Invalid reaction type {r_type}')

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.base'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.base.abc'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -16,10 +16,10 @@
"LicenseName": "",
"LicenseDescription": "",
"Dependencies": [
"cpl-core>=2022.7.0.post2"
"cpl-core>=2022.10.0.post2"
],
"DevDependencies": [
"cpl-cli>=2022.7.0.post2"
"cpl-cli>=2022.10.0"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {

View File

@@ -0,0 +1,44 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
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.base.abc.base_helper_abc import BaseHelperABC
from modules.base.command.afk_command import AFKCommand
from modules.base.command.help_command import HelpCommand
from modules.base.command.info_command import InfoCommand
from modules.base.command.ping_command import PingCommand
from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent
from modules.base.events.base_on_command_event import BaseOnCommandEvent
from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent
from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent
from modules.base.events.base_on_message_event import BaseOnMessageEvent
from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent
from modules.base.service.base_helper_service import BaseHelperService
class BaseModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.base_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(BaseHelperABC, BaseHelperService)
# commands
self._dc.add_command(AFKCommand)
self._dc.add_command(HelpCommand)
self._dc.add_command(InfoCommand)
self._dc.add_command(PingCommand)
# events
self._dc.add_event(DiscordEventTypesEnum.on_command.value, BaseOnCommandEvent)
self._dc.add_event(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent)
self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent)
self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent)
self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent)
self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent)

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.base.command'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -1,5 +1,4 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
@@ -9,6 +8,7 @@ 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 modules.base.configuration.base_server_settings import BaseServerSettings
@@ -16,7 +16,7 @@ class AFKCommand(DiscordCommandABC):
def __init__(
self,
logger: LoggerABC,
logger: CommandLogger,
config: ConfigurationABC,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
@@ -34,10 +34,12 @@ class AFKCommand(DiscordCommandABC):
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def afk(self, ctx: Context):
self._logger.debug(__name__, f'Received command afk {ctx}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
settings: BaseServerSettings = self._config.get_configuration(f'BaseServerSettings_{ctx.guild.id}')

View File

@@ -1,12 +1,16 @@
from typing import List
import discord
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
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 modules.base.configuration.base_server_settings import BaseServerSettings
@@ -15,7 +19,7 @@ class HelpCommand(DiscordCommandABC):
def __init__(
self,
config: ConfigurationABC,
logger: LoggerABC,
logger: CommandLogger,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC
@@ -30,12 +34,19 @@ class HelpCommand(DiscordCommandABC):
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def help(self, ctx: Context, persistent_flag: str = None):
self._logger.debug(__name__, f'Received command help {ctx}:{persistent_flag}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
settings: BaseServerSettings = self._config.get_configuration(f'BaseServerSettings_{ctx.guild.id}')
is_persistent = persistent_flag == '--stay'
await self._message_service.send_ctx_msg(ctx, settings.help_command_reference_url, is_persistent=is_persistent)
self._logger.trace(__name__, f'Finished help command')
@help.autocomplete('persistent_flag')
async def help_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]:
flags = ['--stay']
return [app_commands.Choice(name=key, value=key) for key in flags]

View File

@@ -2,7 +2,6 @@ from datetime import datetime
import discord
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
@@ -12,6 +11,7 @@ from discord.ext.commands import Context
import bot
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
class InfoCommand(DiscordCommandABC):
@@ -19,7 +19,7 @@ class InfoCommand(DiscordCommandABC):
def __init__(
self,
config: ConfigurationABC,
logger: LoggerABC,
logger: CommandLogger,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
@@ -36,10 +36,12 @@ class InfoCommand(DiscordCommandABC):
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def info(self, ctx: Context, *, wait: int = None):
self._logger.debug(__name__, f'Received command info {ctx},{wait}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
client = self._client_utils.get_client(self._bot.user.id, ctx.guild.id)
@@ -58,7 +60,9 @@ class InfoCommand(DiscordCommandABC):
embed.add_field(name=self._t.transform('modules.base.info.fields.deleted_message_count'), value=client.deleted_message_count, inline=False)
embed.add_field(name=self._t.transform('modules.base.info.fields.received_command_count'), value=client.received_command_count)
embed.add_field(name=self._t.transform('modules.base.info.fields.moved_users_count'), value=client.moved_users_count)
modules = ['Base', 'BootLog', 'Database', 'Permission']
from bot.module_list import ModuleList
modules = ModuleList.get_modules()
modules = modules.select(lambda x: x.__name__.replace('Module', ''))
embed.add_field(name=self._t.transform('modules.base.info.fields.modules'), value='\n'.join(modules), inline=False)
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)

View File

@@ -1,4 +1,3 @@
from cpl_core.logging import LoggerABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
@@ -7,13 +6,14 @@ 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
class PingCommand(DiscordCommandABC):
def __init__(
self,
logger: LoggerABC,
logger: CommandLogger,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC,
@@ -29,10 +29,12 @@ class PingCommand(DiscordCommandABC):
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
@commands.command()
@commands.hybrid_command()
@commands.guild_only()
async def ping(self, ctx: Context):
self._logger.debug(__name__, f'Received command ping {ctx}')
if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx):
return
self._client_utils.received_command(ctx.guild.id)
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong'))
self._logger.trace(__name__, f'Finished ping command')

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.base.configuration'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

View File

@@ -2,6 +2,7 @@ import traceback
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
from cpl_query.extension import List
class BaseServerSettings(ConfigurationModelABC):
@@ -13,7 +14,9 @@ class BaseServerSettings(ConfigurationModelABC):
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._afk_channel_ids: List[int] = List(int)
self._afk_command_channel_id: int = 0
self._help_command_reference_url: str = ''
@property
def id(self) -> int:
@@ -32,7 +35,7 @@ class BaseServerSettings(ConfigurationModelABC):
return self._xp_per_ontime_hour
@property
def afk_channel_ids(self) -> list[int]:
def afk_channel_ids(self) -> List[int]:
return self._afk_channel_ids
@property

View File

@@ -15,7 +15,7 @@ __title__ = 'modules.base.events'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
__version__ = '1.0.0.dev3'
__version__ = '0.2.3'
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='1', minor='0', micro='0.dev3')
version_info = VersionInfo(major='0', minor='2', micro='3')

Some files were not shown because too many files have changed in this diff Show More