Moved bot to kdb-bot #70
This commit is contained in:
26
kdb-bot/cpl-workspace.json
Normal file
26
kdb-bot/cpl-workspace.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"WorkspaceSettings": {
|
||||
"DefaultProject": "bot",
|
||||
"Projects": {
|
||||
"bot": "src/bot/bot.json",
|
||||
"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",
|
||||
"moderator": "src/modules/moderator/moderator.json",
|
||||
"permission": "src/modules/permission/permission.json",
|
||||
"bot-api": "src/bot_api/bot-api.json"
|
||||
},
|
||||
"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;",
|
||||
"build-docker": "cpl b; docker-compose down; docker build -t kdb .",
|
||||
"compose": "docker-compose up -d",
|
||||
"docker": "cpl build-docker; cpl compose;"
|
||||
}
|
||||
}
|
||||
}
|
64
kdb-bot/docker-compose.yml
Normal file
64
kdb-bot/docker-compose.yml
Normal 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
kdb-bot/dockerfile
Normal file
18
kdb-bot/dockerfile
Normal 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"]
|
26
kdb-bot/src/bot/__init__.py
Normal file
26
kdb-bot/src/bot/__init__.py
Normal 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'
|
||||
__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')
|
74
kdb-bot/src/bot/application.py
Normal file
74
kdb-bot/src/bot/application.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.console import Console
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_core.logging import LoggerABC
|
||||
from cpl_discord.application import DiscordBotApplicationABC
|
||||
from cpl_discord.configuration import DiscordBotSettings
|
||||
from cpl_discord.service import DiscordBotServiceABC, DiscordBotService
|
||||
from cpl_translation import TranslatePipe, TranslationServiceABC, TranslationSettings
|
||||
|
||||
from bot_api.api_thread import ApiThread
|
||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
|
||||
|
||||
|
||||
class Application(DiscordBotApplicationABC):
|
||||
|
||||
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
|
||||
DiscordBotApplicationABC.__init__(self, config, services)
|
||||
|
||||
self._services = services
|
||||
self._config = config
|
||||
|
||||
# cpl-core
|
||||
self._logger: LoggerABC = services.get_service(LoggerABC)
|
||||
# cpl-discord
|
||||
self._bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC)
|
||||
self._bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings)
|
||||
# cpl-translation
|
||||
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
|
||||
self._t: TranslatePipe = services.get_service(TranslatePipe)
|
||||
|
||||
self._feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings)
|
||||
|
||||
# api
|
||||
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module):
|
||||
self._api: ApiThread = services.get_service(ApiThread)
|
||||
|
||||
self._is_stopping = False
|
||||
|
||||
async def configure(self):
|
||||
self._translation.load_by_settings(self._configuration.get_configuration(TranslationSettings))
|
||||
|
||||
async def main(self):
|
||||
try:
|
||||
self._logger.debug(__name__, f'Starting...')
|
||||
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module):
|
||||
self._api.start()
|
||||
|
||||
if self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development':
|
||||
self._api.join()
|
||||
return
|
||||
|
||||
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()
|
||||
self._logger.trace(__name__, f'Stopped {DiscordBotService.__name__}')
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, 'stop failed', e)
|
||||
|
||||
Console.write_line()
|
||||
|
||||
def is_restart(self):
|
||||
return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False #
|
24
kdb-bot/src/bot/bot
Normal file
24
kdb-bot/src/bot/bot
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
path="`dirname \"$0\"`" # relative
|
||||
path="`( cd \"$path\" && pwd)`" # absolutized and normalized
|
||||
|
||||
cd "$path/../"
|
||||
|
||||
if [[ $1 == "-dev" ]]; then
|
||||
export KDB_ENVIRONMENT=development
|
||||
export KDB_NAME=KDB-dev
|
||||
elif [[ $1 == "-stage" ]]; then
|
||||
export KDB_ENVIRONMENT=staging
|
||||
export KDB_NAME=KDB-test
|
||||
elif [[ $1 == "-prod" ]]; then
|
||||
export KDB_ENVIRONMENT=production
|
||||
export KDB_NAME=KDB
|
||||
else
|
||||
export KDB_ENVIRONMENT=production
|
||||
export KDB_NAME=KDB-prod
|
||||
fi
|
||||
|
||||
export PYTHONPATH=./:$PYTHONPATH
|
||||
|
||||
python3.10 bot/main.py
|
61
kdb-bot/src/bot/bot.json
Normal file
61
kdb-bot/src/bot/bot.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "bot",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "2",
|
||||
"Micro": "3"
|
||||
},
|
||||
"Author": "Sven Heidemann",
|
||||
"AuthorEmail": "sven.heidemann@sh-edraft.de",
|
||||
"Description": "Keksdose bot",
|
||||
"LongDescription": "Discord bot for the Keksdose discord Server",
|
||||
"URL": "https://www.sh-edraft.de",
|
||||
"CopyrightDate": "2022",
|
||||
"CopyrightName": "sh-edraft.de",
|
||||
"LicenseName": "MIT",
|
||||
"LicenseDescription": "MIT, see LICENSE for more details.",
|
||||
"Dependencies": [
|
||||
"cpl-core==2022.10.0.post6",
|
||||
"cpl-translation==2022.10.0.post1",
|
||||
"cpl-query==2022.10.0.post2",
|
||||
"cpl-discord==2022.10.0.post5"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli==2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "bot.main",
|
||||
"EntryPoint": "bot",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": [
|
||||
"../bot_api/bot-api.json",
|
||||
"../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/moderator/moderator.json",
|
||||
"../modules/permission/permission.json"
|
||||
]
|
||||
}
|
||||
}
|
86
kdb-bot/src/bot/config/appsettings.PC-Nick.json
Normal file
86
kdb-bot/src/bot/config/appsettings.PC-Nick.json
Normal 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": "root",
|
||||
"Password": "MTAwNjE5OTdOaWNrLko=",
|
||||
"Database": "kd_kdb",
|
||||
"Charset": "utf8mb4",
|
||||
"UseUnicode": "true",
|
||||
"Buffered": "true",
|
||||
"AuthPlugin": "mysql_native_password"
|
||||
},
|
||||
"DiscordBot": {
|
||||
"Token": "MTAyOTgxMjE0Mjk4NTE5MTYxNA.G4ArAJ.sZh6pE-mwO2qDAr1mfHEoo7EwbJb-TZT8h6nGg",
|
||||
"Prefix": "!kn "
|
||||
},
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
83
kdb-bot/src/bot/config/appsettings.development.json
Normal file
83
kdb-bot/src/bot/config/appsettings.development.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"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": "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": [
|
||||
"de"
|
||||
]
|
||||
},
|
||||
"DiscordBot": {
|
||||
"Token": "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4",
|
||||
"Prefix": "!kd "
|
||||
},
|
||||
"Bot": {
|
||||
"910199451145076828": {
|
||||
"MessageDeleteTimer": 6
|
||||
},
|
||||
"Technicians": [
|
||||
240160344557879316,
|
||||
236592458664902657
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
92
kdb-bot/src/bot/config/appsettings.edrafts-lapi.json
Normal file
92
kdb-bot/src/bot/config/appsettings.edrafts-lapi.json
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"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": {
|
||||
"Api": {
|
||||
"Path": "logs/",
|
||||
"Filename": "api.log",
|
||||
"ConsoleLogLevel": "TRACE",
|
||||
"FileLogLevel": "TRACE"
|
||||
},
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
86
kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json
Normal file
86
kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json
Normal 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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
18
kdb-bot/src/bot/config/appsettings.example.json
Normal file
18
kdb-bot/src/bot/config/appsettings.example.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"TimeFormatSettings": {
|
||||
"DateFormat": "%Y-%m-%d",
|
||||
"TimeFormat": "%H:%M:%S",
|
||||
"DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
|
||||
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
|
||||
},
|
||||
"LoggingSettings": {
|
||||
"Path": "logs/",
|
||||
"Filename": "log_$start_time.log",
|
||||
"ConsoleLogLevel": "ERROR",
|
||||
"FileLogLevel": "WARN"
|
||||
},
|
||||
"DiscordBot": {
|
||||
"Token": "",
|
||||
"Prefix": "! "
|
||||
}
|
||||
}
|
34
kdb-bot/src/bot/config/appsettings.json
Normal file
34
kdb-bot/src/bot/config/appsettings.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"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/$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"
|
||||
}
|
||||
}
|
||||
}
|
114
kdb-bot/src/bot/config/appsettings.production.json
Normal file
114
kdb-bot/src/bot/config/appsettings.production.json
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"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/$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
|
||||
},
|
||||
"910199451145076828": {
|
||||
"MessageDeleteTimer": 2
|
||||
},
|
||||
"Technicians": [
|
||||
240160344557879316,
|
||||
236592458664902657
|
||||
],
|
||||
"WaitForRestart": 4,
|
||||
"WaitForShutdown": 4
|
||||
},
|
||||
"Base": {
|
||||
"650366049023295514": {
|
||||
"MaxVoiceStateHours": 24,
|
||||
"XpPerMessage": 2,
|
||||
"XpPerOntimeHour": 4,
|
||||
"AFKChannelIds": [
|
||||
784530469290246145
|
||||
],
|
||||
"AFKCommandChannelId": 784530469290246145,
|
||||
"HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle"
|
||||
},
|
||||
"910199451145076828": {
|
||||
"MaxVoiceStateHours": 24,
|
||||
"XpPerMessage": 2,
|
||||
"XpPerOntimeHour": 4,
|
||||
"AFKChannelIds": [
|
||||
910199452915093593,
|
||||
910199452915093594
|
||||
],
|
||||
"AFKCommandChannelId": 910199452915093594,
|
||||
"HelpCommandReferenceUrl": "https://docs.sh-edraft.de/kdb/"
|
||||
}
|
||||
},
|
||||
"BootLog": {
|
||||
"650366049023295514": {
|
||||
"LoginMessageChannelId": "998544927094997093"
|
||||
},
|
||||
"910199451145076828": {
|
||||
"LoginMessageChannelId": "910199452915093588"
|
||||
}
|
||||
},
|
||||
"Permission": {
|
||||
"650366049023295514": {
|
||||
"AdminRoleIds": [
|
||||
666129320481128479
|
||||
],
|
||||
"ModeratorRoleIds": [
|
||||
998161580669804565
|
||||
]
|
||||
},
|
||||
"910199451145076828": {
|
||||
"AdminRoleIds": [
|
||||
925072155203477584
|
||||
],
|
||||
"ModeratorRoleIds": [
|
||||
925072209884635167
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
90
kdb-bot/src/bot/config/appsettings.staging.json
Normal file
90
kdb-bot/src/bot/config/appsettings.staging.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"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/$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
|
||||
},
|
||||
"Technicians": [
|
||||
240160344557879316,
|
||||
236592458664902657
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
16
kdb-bot/src/bot/config/feature-flags.json
Normal file
16
kdb-bot/src/bot/config/feature-flags.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"FeatureFlags": {
|
||||
"ApiModule": true,
|
||||
"AdminModule": true,
|
||||
"AutoRoleModule": true,
|
||||
"BaseModule": true,
|
||||
"BootLogModule": true,
|
||||
"CoreModule": true,
|
||||
"CoreExtensionModule": true,
|
||||
"DatabaseModule": true,
|
||||
"ModeratorModule": true,
|
||||
"PermissionModule": true,
|
||||
"PresenceModule": true,
|
||||
"ApiOnly": true
|
||||
}
|
||||
}
|
72
kdb-bot/src/bot/main.py
Normal file
72
kdb-bot/src/bot/main.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import asyncio
|
||||
import traceback
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.application import ApplicationBuilder
|
||||
from cpl_core.console import Console
|
||||
|
||||
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
|
||||
|
||||
|
||||
class Program:
|
||||
|
||||
def __init__(self):
|
||||
self.app: Optional[Application] = None
|
||||
|
||||
async def start(self):
|
||||
# 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()
|
||||
|
||||
|
||||
def main():
|
||||
program = Program()
|
||||
try:
|
||||
asyncio.run(program.start())
|
||||
except KeyboardInterrupt:
|
||||
asyncio.run(program.stop())
|
||||
except Exception as 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', f'{e} -> {traceback.format_exc()}')
|
||||
|
||||
if program.app is not None and program.app.is_restart():
|
||||
del program
|
||||
main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
# ((
|
||||
# ( `)
|
||||
# ; / ,
|
||||
# / \/
|
||||
# / |
|
||||
# / ~/
|
||||
# / ) ) ~ edraft
|
||||
# ___// | /
|
||||
# `--' \_~-,
|
34
kdb-bot/src/bot/module_list.py
Normal file
34
kdb-bot/src/bot/module_list.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from cpl_query.extension import List
|
||||
|
||||
from bot_api.api_module import ApiModule
|
||||
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,
|
||||
ApiModule,
|
||||
AdminModule,
|
||||
AutoRoleModule,
|
||||
BaseModule,
|
||||
DatabaseModule,
|
||||
ModeratorModule,
|
||||
PermissionModule,
|
||||
# has to be last!
|
||||
BootLogModule,
|
||||
CoreExtensionModule,
|
||||
])
|
60
kdb-bot/src/bot/startup.py
Normal file
60
kdb-bot/src/bot/startup.py
Normal file
@@ -0,0 +1,60 @@
|
||||
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 ServiceCollectionABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_core.environment import ApplicationEnvironment
|
||||
from cpl_core.logging import LoggerABC
|
||||
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
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
|
||||
|
||||
|
||||
class Startup(StartupABC):
|
||||
|
||||
def __init__(self):
|
||||
StartupABC.__init__(self)
|
||||
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:
|
||||
self._config = configuration
|
||||
self._feature_flags = configuration.get_configuration(FeatureFlagsSettings)
|
||||
return configuration
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
|
||||
services.add_logging()
|
||||
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)
|
||||
|
||||
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module):
|
||||
services.add_singleton(CustomFileLoggerABC, ApiLogger)
|
||||
|
||||
services.add_translation()
|
||||
services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings))
|
||||
|
||||
provider = services.build_service_provider()
|
||||
# instantiate custom logger
|
||||
for c in CustomFileLoggerABC.__subclasses__():
|
||||
i: LoggerABC = provider.get_service(c)
|
||||
|
||||
|
||||
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)}')
|
||||
|
||||
return provider
|
18
kdb-bot/src/bot/startup_discord_extension.py
Normal file
18
kdb-bot/src/bot/startup_discord_extension.py
Normal file
@@ -0,0 +1,18 @@
|
||||
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 cpl_discord import get_discord_collection
|
||||
|
||||
|
||||
class StartupDiscordExtension(StartupExtensionABC):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
|
||||
pass
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
services.add_discord()
|
||||
dcc = get_discord_collection(services)
|
25
kdb-bot/src/bot/startup_migration_extension.py
Normal file
25
kdb-bot/src/bot/startup_migration_extension.py
Normal file
@@ -0,0 +1,25 @@
|
||||
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_data.abc.migration_abc import MigrationABC
|
||||
from bot_data.migration.api_migration import ApiMigration
|
||||
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
|
||||
|
||||
|
||||
class StartupMigrationExtension(StartupExtensionABC):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
|
||||
pass
|
||||
|
||||
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
|
||||
services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0
|
39
kdb-bot/src/bot/startup_module_extension.py
Normal file
39
kdb-bot/src/bot/startup_module_extension.py
Normal 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)
|
51
kdb-bot/src/bot/startup_settings_extension.py
Normal file
51
kdb-bot/src/bot/startup_settings_extension.py
Normal 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)
|
174
kdb-bot/src/bot/translation/de.json
Normal file
174
kdb-bot/src/bot/translation/de.json
Normal file
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"common": {
|
||||
"hello_world": "Hallo Welt",
|
||||
"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.",
|
||||
"missing_required_argument": "Fehler: Ein benötigter Parameter fehlt!",
|
||||
"argument_parsing_error": "Fehler: Parameter konnte nicht gelesen werden!",
|
||||
"unexpected_quote_error": "Fehler: Unerwarteter Zitat Fehler!",
|
||||
"invalid_end_of_quoted_string_error": "Fehler: Ungültiges Zitatende!",
|
||||
"expected_closing_quote_error": "Fehler: Erwarte Zitatende!",
|
||||
"bad_argument": "Fehler: Ungültiger Parameter!",
|
||||
"bad_union_argument": "Fehler: Ungültiger Union Parameter!",
|
||||
"private_message_only": "Fehler: Nur private Nachrichten sind erlaubt!",
|
||||
"no_private_message": "Fehler: Private Nachrichten sind nicht erlaubt!",
|
||||
"check_failure": "Fehler: Du hast nicht die benötigte Berechtigung!",
|
||||
"check_any_failure": "Fehler: Alle checks sind Fehlgeschlagen!",
|
||||
"command_not_found": "Fehler: Befehl konnte nicht gefunden werden!",
|
||||
"disabled_command": "Fehler: Befehl wurde deaktiviert!",
|
||||
"command_invoke_error": "Fehler: Befehl konnte nicht aufgerufen werden!",
|
||||
"too_many_arguments": "Fehler: Zu viele Parameter!",
|
||||
"user_input_error": "Fehler: Eingabefehler!",
|
||||
"command_on_cooldown": "Fehler: Befehl befindet sich im cooldown!",
|
||||
"max_concurrency_reached": "Fehler: Maximale Parallelität erreicht!",
|
||||
"not_owner": "Fehler: Du bist nicht mein besitzer!",
|
||||
"missing_permissions": "Fehler: Berechtigungen fehlen!",
|
||||
"bot_missing_permissions": "Fehler: Mir fehlen Berechtigungen!",
|
||||
"missing_role": "Fehler: Benötigte Rolle fehlt!",
|
||||
"bot_missing_role": "Fehler: Mir fehlt eine benötigte Rolle!",
|
||||
"missing_any_role": "Fehler: Alle benötigten Rollen fehlen!",
|
||||
"bot_missing_any_role": "Fehler: Mir fehlen alle benötigten Rollen!",
|
||||
"nsfw_channel_required": "Fehler: NSFW Kanal benötigt!",
|
||||
"extension_error": "Fehler: Erweiterungsfehler!",
|
||||
"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!",
|
||||
"bot_not_ready_yet": "Ey Alter! Gedulde dich doch mal! ..."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"admin": {
|
||||
"restart_message": "Bin gleich wieder da :D",
|
||||
"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."
|
||||
},
|
||||
"base": {
|
||||
"technician_error_message": "Es gab ein Fehler mit dem Event: {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.UUID: {}",
|
||||
"technician_command_error_message": "Es gab ein Fehler mit dem Befehl: {} ausgelöst von {} -> {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.UUID: {}",
|
||||
"welcome_message": "Hello There!\nIch heiße dich bei {} herzlichst willkommen!",
|
||||
"welcome_message_for_team": "{} hat gerade das Irrenhaus betreten.",
|
||||
"goodbye_message": "Schade das du uns so schnell verlässt :(",
|
||||
"afk_command_channel_missing_message": "Zu unfähig einem Sprachkanal beizutreten?",
|
||||
"afk_command_move_message": "Ich verschiebe dich ja schon... (◔_◔)",
|
||||
"pong": "Pong",
|
||||
"info": {
|
||||
"title": "Gismo",
|
||||
"description": "Informationen über mich",
|
||||
"fields": {
|
||||
"version": "Version",
|
||||
"ontime": "Ontime",
|
||||
"sent_message_count": "Gesendete Nachrichten",
|
||||
"received_message_count": "Empfangene Nachrichten",
|
||||
"deleted_message_count": "Gelöschte Nachrichten",
|
||||
"received_command_count": "Empfangene Befehle",
|
||||
"moved_users_count": "Verschobene Benutzer",
|
||||
"modules": "Module"
|
||||
},
|
||||
"footer": ""
|
||||
},
|
||||
"user_info": {
|
||||
"fields": {
|
||||
"id": "Id",
|
||||
"name": "Name",
|
||||
"discord_join": "Discord beigetreten am",
|
||||
"last_join": "Server beigetreten am",
|
||||
"xp": "XP",
|
||||
"roles": "Rollen",
|
||||
"joins": "Beitritte",
|
||||
"lefts": "Abgänge",
|
||||
"warnings": "Verwarnungen"
|
||||
},
|
||||
"footer": ""
|
||||
}
|
||||
},
|
||||
"boot_log": {
|
||||
"login_message": "Ich bin on the line :D\nDer Scheiß hat {} Sekunden gedauert"
|
||||
},
|
||||
"database": {},
|
||||
"permission": {
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"api": {
|
||||
"test_mail": {
|
||||
"subject": "Krümmelmonster Web Interface Test-Mail",
|
||||
"message": "Dies ist eine Test-Mail vom Krümmelmonster Web Interface\nGesendet von {}-{}"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"confirmation": {
|
||||
"subject": "E-Mail für {} {} bestätigen",
|
||||
"message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/register/{}"
|
||||
},
|
||||
"forgot_password": {
|
||||
"subject": "Passwort für {} {} zurücksetzen",
|
||||
"message": "Öffne den Link um das Passwort zu ändern:\n{}auth/forgot-password/{}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
kdb-bot/src/bot_api/__init__.py
Normal file
26
kdb-bot/src/bot_api/__init__.py
Normal 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_api'
|
||||
__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')
|
26
kdb-bot/src/bot_api/abc/__init__.py
Normal file
26
kdb-bot/src/bot_api/abc/__init__.py
Normal 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_api.abc'
|
||||
__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')
|
65
kdb-bot/src/bot_api/abc/auth_service_abc.py
Normal file
65
kdb-bot/src/bot_api/abc/auth_service_abc.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from cpl_query.extension import List
|
||||
|
||||
from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria
|
||||
from bot_api.model.auth_user_dto import AuthUserDTO
|
||||
from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO
|
||||
from bot_api.model.email_string_dto import EMailStringDTO
|
||||
from bot_api.model.reset_password_dto import ResetPasswordDTO
|
||||
from bot_api.model.token_dto import TokenDTO
|
||||
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
|
||||
|
||||
|
||||
class AuthServiceABC(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_all_auth_users_async(self) -> List[AuthUserDTO]: pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def add_auth_user_async(self, user_dto: AuthUserDTO) -> int: pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_auth_user_by_email_async(self, email: str): pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass
|
||||
|
||||
@abstractmethod
|
||||
async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def revoke_async(self, token_dto: TokenDTO): pass
|
||||
|
||||
@abstractmethod
|
||||
async def confirm_email_async(self, id: str) -> bool: pass
|
||||
|
||||
@abstractmethod
|
||||
async def forgot_password_async(self, email: str): pass
|
||||
|
||||
@abstractmethod
|
||||
async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def reset_password_async(self, rp_dto: ResetPasswordDTO): pass
|
16
kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py
Normal file
16
kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from abc import abstractmethod
|
||||
|
||||
from cpl_core.database import TableABC
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
|
||||
|
||||
class AuthUserTransformerABC:
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def to_db(dto: DtoABC) -> TableABC: pass
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def to_dto(db: TableABC) -> DtoABC: pass
|
13
kdb-bot/src/bot_api/abc/dto_abc.py
Normal file
13
kdb-bot/src/bot_api/abc/dto_abc.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class DtoABC(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
def from_dict(self, values: dict): pass
|
||||
|
||||
@abstractmethod
|
||||
def to_dict(self) -> dict: pass
|
17
kdb-bot/src/bot_api/abc/select_criteria_abc.py
Normal file
17
kdb-bot/src/bot_api/abc/select_criteria_abc.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class SelectCriteriaABC(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(
|
||||
self,
|
||||
page_index: int,
|
||||
page_size: int,
|
||||
sort_direction: str,
|
||||
sort_column: str
|
||||
):
|
||||
self.page_index = page_index
|
||||
self.page_size = page_size
|
||||
self.sort_direction = sort_direction
|
||||
self.sort_column = sort_column
|
81
kdb-bot/src/bot_api/api.py
Normal file
81
kdb-bot/src/bot_api/api.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
from functools import partial
|
||||
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from flask import Flask, request, jsonify, Response, make_response
|
||||
from flask_cors import CORS
|
||||
|
||||
from bot_api.configuration.api_settings import ApiSettings
|
||||
from bot_api.exception.service_exception import ServiceException
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_api.model.error_dto import ErrorDTO
|
||||
from bot_api.route.route import Route
|
||||
|
||||
|
||||
class Api(Flask):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: ApiLogger,
|
||||
services: ServiceProviderABC,
|
||||
api_settings: ApiSettings,
|
||||
*args, **kwargs
|
||||
):
|
||||
if not args:
|
||||
kwargs.setdefault('import_name', __name__)
|
||||
|
||||
Flask.__init__(self, *args, **kwargs)
|
||||
|
||||
self._logger = logger
|
||||
self._services = services
|
||||
self._apt_settings = api_settings
|
||||
|
||||
self._cors = CORS(self, support_credentials=True)
|
||||
|
||||
# register before request
|
||||
self.before_request_funcs.setdefault(None, []).append(self.before_request)
|
||||
exc_class, code = self._get_exc_class_and_code(Exception)
|
||||
self.error_handler_spec[None][code][exc_class] = self.handle_exception
|
||||
|
||||
def _register_routes(self):
|
||||
for path, f in Route.registered_routes.items():
|
||||
route = f[0]
|
||||
kwargs = f[1]
|
||||
cls = None
|
||||
qual_name_split = route.__qualname__.split('.')
|
||||
if len(qual_name_split) > 0:
|
||||
cls_type = vars(sys.modules[route.__module__])[qual_name_split[0]]
|
||||
cls = self._services.get_service(cls_type)
|
||||
|
||||
partial_f = partial(route, self if cls is None else cls)
|
||||
partial_f.__name__ = route.__name__
|
||||
self.route(path, **kwargs)(partial_f)
|
||||
|
||||
def handle_exception(self, e: Exception):
|
||||
self._logger.error(__name__, f'Caught error', e)
|
||||
|
||||
if isinstance(e, ServiceException):
|
||||
ex: ServiceException = e
|
||||
self._logger.error(__name__, ex.get_detailed_message())
|
||||
error = ErrorDTO(ex.error_code, ex.message)
|
||||
return jsonify(error.to_dict()), 500
|
||||
else:
|
||||
tracking_id = uuid.uuid4()
|
||||
user_message = f'Tracking Id: {tracking_id}'
|
||||
self._logger.error(__name__, user_message, e)
|
||||
error = ErrorDTO(None, user_message)
|
||||
return jsonify(error.to_dict()), 400
|
||||
|
||||
def before_request(self, *args, **kwargs):
|
||||
self._logger.debug(__name__, f'Received GET @{request.url}')
|
||||
headers = str(request.headers).replace("\n", "\n\t")
|
||||
self._logger.trace(__name__, f'Headers: \n\t{headers}')
|
||||
|
||||
def start(self):
|
||||
self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}')
|
||||
self._register_routes()
|
||||
from waitress import serve
|
||||
# https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html
|
||||
serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10)
|
41
kdb-bot/src/bot_api/api_module.py
Normal file
41
kdb-bot/src/bot_api/api_module.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
from cpl_core.mailing import EMailClientABC, EMailClient
|
||||
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
||||
from flask import Flask
|
||||
|
||||
from bot_api.abc.auth_service_abc import AuthServiceABC
|
||||
from bot_api.api import Api
|
||||
from bot_api.api_thread import ApiThread
|
||||
from bot_api.controller.gui_controller import GuiController
|
||||
from bot_api.controller.auth_controller import AuthController
|
||||
from bot_api.service.auth_service import AuthService
|
||||
from bot_core.abc.module_abc import ModuleABC
|
||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
|
||||
|
||||
class ApiModule(ModuleABC):
|
||||
|
||||
def __init__(self, dc: DiscordCollectionABC):
|
||||
ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module)
|
||||
|
||||
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
|
||||
cwd = env.working_directory
|
||||
env.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
|
||||
config.add_json_file(f'config/apisettings.json', optional=False)
|
||||
config.add_json_file(f'config/apisettings.{env.environment_name}.json', optional=True)
|
||||
config.add_json_file(f'config/apisettings.{env.host_name}.json', optional=True)
|
||||
env.set_working_directory(cwd)
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
services.add_singleton(EMailClientABC, EMailClient)
|
||||
|
||||
services.add_singleton(ApiThread)
|
||||
services.add_singleton(Flask, Api)
|
||||
|
||||
services.add_transient(AuthServiceABC, AuthService)
|
||||
services.add_transient(AuthController)
|
||||
services.add_transient(GuiController)
|
27
kdb-bot/src/bot_api/api_thread.py
Normal file
27
kdb-bot/src/bot_api/api_thread.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import threading
|
||||
|
||||
from bot_api.api import Api
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
|
||||
|
||||
|
||||
class ApiThread(threading.Thread):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: ApiLogger,
|
||||
api: Api,
|
||||
feature_flags: FeatureFlagsSettings
|
||||
):
|
||||
threading.Thread.__init__(self, daemon=True)
|
||||
|
||||
self._logger = logger
|
||||
self._api = api
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
self._logger.trace(__name__, f'Try to start {type(self._api).__name__}')
|
||||
self._api.start()
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, 'Start failed', e)
|
52
kdb-bot/src/bot_api/bot-api.json
Normal file
52
kdb-bot/src/bot_api/bot-api.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "bot-api",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core==2022.10.0.post6",
|
||||
"Flask==2.2.2",
|
||||
"Flask[async]==2.2.2",
|
||||
"Flask-Classful==0.14.2",
|
||||
"Flask-Cors==3.0.10",
|
||||
"PyJWT[crypto]==2.5.0",
|
||||
"PyJWT==2.5.0"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli==2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "library",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "bot_api.main",
|
||||
"EntryPoint": "bot-api",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
8
kdb-bot/src/bot_api/config/apisettings.development.json
Normal file
8
kdb-bot/src/bot_api/config/apisettings.development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"EMailClientSettings": {
|
||||
"Host": "mail.sh-edraft.de",
|
||||
"Port": "587",
|
||||
"UserName": "dev-srv@sh-edraft.de",
|
||||
"Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA=="
|
||||
}
|
||||
}
|
17
kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json
Normal file
17
kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Api": {
|
||||
"Port": 5000,
|
||||
"Host": "0.0.0.0",
|
||||
"RedirectToHTTPS": false
|
||||
},
|
||||
"Authentication": {
|
||||
"SecretKey": "F3b5LDz+#Jvzg=W!@gsa%xsF",
|
||||
"Issuer": "http://localhost:5000",
|
||||
"Audience": "http://localhost:4200",
|
||||
"TokenExpireTime": 1,
|
||||
"RefreshTokenExpireTime": 7
|
||||
},
|
||||
"Frontend": {
|
||||
"URL": "http://localhost:4200/"
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
{}
|
1
kdb-bot/src/bot_api/config/apisettings.json
Normal file
1
kdb-bot/src/bot_api/config/apisettings.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
1
kdb-bot/src/bot_api/config/apisettings.production.json
Normal file
1
kdb-bot/src/bot_api/config/apisettings.production.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
1
kdb-bot/src/bot_api/config/apisettings.staging.json
Normal file
1
kdb-bot/src/bot_api/config/apisettings.staging.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
1
kdb-bot/src/bot_api/config/appsettings.PC-Nick.json
Normal file
1
kdb-bot/src/bot_api/config/appsettings.PC-Nick.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
26
kdb-bot/src/bot_api/configuration/__init__.py
Normal file
26
kdb-bot/src/bot_api/configuration/__init__.py
Normal 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_api.configuration'
|
||||
__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')
|
35
kdb-bot/src/bot_api/configuration/api_settings.py
Normal file
35
kdb-bot/src/bot_api/configuration/api_settings.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class ApiSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._port = 80
|
||||
self._host = ''
|
||||
self._redirect_to_https = False
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
return self._port
|
||||
|
||||
@property
|
||||
def host(self) -> str:
|
||||
return self._host
|
||||
|
||||
@property
|
||||
def redirect_to_https(self) -> bool:
|
||||
return self._redirect_to_https
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._port = int(settings['Port'])
|
||||
self._host = settings['Host']
|
||||
self._redirect_to_https = bool(settings['RedirectToHTTPS'])
|
||||
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()}')
|
48
kdb-bot/src/bot_api/configuration/authentication_settings.py
Normal file
48
kdb-bot/src/bot_api/configuration/authentication_settings.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class AuthenticationSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._secret_key = ''
|
||||
self._issuer = ''
|
||||
self._audience = ''
|
||||
self._token_expire_time = 0
|
||||
self._refresh_token_expire_time = 0
|
||||
|
||||
@property
|
||||
def secret_key(self) -> str:
|
||||
return self._secret_key
|
||||
|
||||
@property
|
||||
def issuer(self) -> str:
|
||||
return self._issuer
|
||||
|
||||
@property
|
||||
def audience(self) -> str:
|
||||
return self._audience
|
||||
|
||||
@property
|
||||
def token_expire_time(self) -> int:
|
||||
return self._token_expire_time
|
||||
|
||||
@property
|
||||
def refresh_token_expire_time(self) -> int:
|
||||
return self._refresh_token_expire_time
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._secret_key = settings['SecretKey']
|
||||
self._issuer = settings['Issuer']
|
||||
self._audience = settings['Audience']
|
||||
self._token_expire_time = int(settings['TokenExpireTime'])
|
||||
self._refresh_token_expire_time = int(settings['RefreshTokenExpireTime'])
|
||||
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()}')
|
23
kdb-bot/src/bot_api/configuration/frontend_settings.py
Normal file
23
kdb-bot/src/bot_api/configuration/frontend_settings.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class FrontendSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._url = ''
|
||||
|
||||
@property
|
||||
def url(self) -> str:
|
||||
return self._url
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._url = settings['URL']
|
||||
except Exception as e:
|
||||
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
|
||||
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
|
55
kdb-bot/src/bot_api/configuration/version_settings.py
Normal file
55
kdb-bot/src/bot_api/configuration/version_settings.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum
|
||||
|
||||
|
||||
class VersionSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
major: str = None,
|
||||
minor: str = None,
|
||||
micro: str = None
|
||||
):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._major: Optional[str] = major
|
||||
self._minor: Optional[str] = minor
|
||||
self._micro: Optional[str] = micro
|
||||
|
||||
@property
|
||||
def major(self) -> str:
|
||||
return self._major
|
||||
|
||||
@property
|
||||
def minor(self) -> str:
|
||||
return self._minor
|
||||
|
||||
@property
|
||||
def micro(self) -> str:
|
||||
return self._micro
|
||||
|
||||
def to_str(self) -> str:
|
||||
if self._micro is None:
|
||||
return f'{self._major}.{self._minor}'
|
||||
else:
|
||||
return f'{self._major}.{self._minor}.{self._micro}'
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
self._major = settings[VersionSettingsNameEnum.major.value]
|
||||
self._minor = settings[VersionSettingsNameEnum.minor.value]
|
||||
micro = settings[VersionSettingsNameEnum.micro.value]
|
||||
if micro != '':
|
||||
self._micro = micro
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
version = {
|
||||
VersionSettingsNameEnum.major.value: self._major,
|
||||
VersionSettingsNameEnum.minor.value: self._minor,
|
||||
}
|
||||
|
||||
if self._micro is not None:
|
||||
version[VersionSettingsNameEnum.micro.value] = self._micro
|
||||
|
||||
return version
|
26
kdb-bot/src/bot_api/controller/__init__.py
Normal file
26
kdb-bot/src/bot_api/controller/__init__.py
Normal 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_api.controller'
|
||||
__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')
|
118
kdb-bot/src/bot_api/controller/auth_controller.py
Normal file
118
kdb-bot/src/bot_api/controller/auth_controller.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
from cpl_core.mailing import EMailClientABC, EMailClientSettings
|
||||
from cpl_translation import TranslatePipe
|
||||
from flask import request, jsonify, Response
|
||||
|
||||
from bot_api.abc.auth_service_abc import AuthServiceABC
|
||||
from bot_api.api import Api
|
||||
from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria
|
||||
from bot_api.json_processor import JSONProcessor
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_api.model.auth_user_dto import AuthUserDTO
|
||||
from bot_api.model.token_dto import TokenDTO
|
||||
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
|
||||
from bot_api.route.route import Route
|
||||
|
||||
|
||||
class AuthController:
|
||||
BasePath = '/api/auth'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: ConfigurationABC,
|
||||
env: ApplicationEnvironmentABC,
|
||||
logger: ApiLogger,
|
||||
t: TranslatePipe,
|
||||
api: Api,
|
||||
mail_settings: EMailClientSettings,
|
||||
mailer: EMailClientABC,
|
||||
auth_service: AuthServiceABC
|
||||
):
|
||||
self._config = config
|
||||
self._env = env
|
||||
self._logger = logger
|
||||
self._t = t
|
||||
self._api = api
|
||||
self._mail_settings = mail_settings
|
||||
self._mailer = mailer
|
||||
self._auth_service = auth_service
|
||||
|
||||
@Route.get(f'{BasePath}/users')
|
||||
async def get_all_users(self) -> Response:
|
||||
result = await self._auth_service.get_all_auth_users_async()
|
||||
return jsonify(result.select(lambda x: x.to_dict()))
|
||||
|
||||
@Route.post(f'{BasePath}/users/get/filtered')
|
||||
async def get_filtered_users(self) -> Response:
|
||||
dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True))
|
||||
result = await self._auth_service.get_filtered_auth_users_async(dto)
|
||||
result.result = result.result.select(lambda x: x.to_dict())
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.get(f'{BasePath}/users/get/<email>')
|
||||
async def get_user_from_email(self, email: str) -> Response:
|
||||
result = await self._auth_service.get_auth_user_by_email_async(email)
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.get(f'{BasePath}/users/find/<email>')
|
||||
async def find_user_from_email(self, email: str) -> Response:
|
||||
result = await self._auth_service.find_auth_user_by_email_async(email)
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.post(f'{BasePath}/register')
|
||||
async def register(self):
|
||||
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.add_auth_user_async(dto)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/login')
|
||||
async def login(self) -> Response:
|
||||
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
|
||||
result = await self._auth_service.login_async(dto)
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.post(f'{BasePath}/forgot-password/<email>')
|
||||
async def forgot_password(self, email: str):
|
||||
await self._auth_service.forgot_password_async(email)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/confirm-forgot-password/<id>')
|
||||
async def confirm_forgot_password(self, id: str):
|
||||
await self._auth_service.confirm_forgot_password_async(id)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/update-user')
|
||||
async def update_user(self):
|
||||
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.update_user_async(dto)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/update-user-as-admin')
|
||||
async def update_user_as_admin(self):
|
||||
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.update_user_async(dto)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/refresh')
|
||||
async def refresh(self) -> Response:
|
||||
dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True))
|
||||
result = await self._auth_service.refresh_async(dto)
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.post(f'{BasePath}/revoke')
|
||||
async def revoke(self):
|
||||
dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.revoke_async(dto)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/delete-user')
|
||||
async def delete_user(self):
|
||||
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.delete_auth_user_async(dto)
|
||||
return '', 200
|
||||
|
||||
@Route.post(f'{BasePath}/delete-user-by-mail/<email>')
|
||||
async def delete_user_by_mail(self, email: str):
|
||||
await self._auth_service.delete_auth_user_by_email_async(email)
|
||||
return '', 200
|
73
kdb-bot/src/bot_api/controller/gui_controller.py
Normal file
73
kdb-bot/src/bot_api/controller/gui_controller.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
from cpl_core.mailing import EMail, EMailClientABC, EMailClientSettings
|
||||
from cpl_translation import TranslatePipe
|
||||
|
||||
from bot_api.api import Api
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_api.model.settings_dto import SettingsDTO
|
||||
from bot_api.model.version_dto import VersionDTO
|
||||
from bot_api.route.route import Route
|
||||
|
||||
|
||||
class GuiController:
|
||||
BasePath = f'/api/gui'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: ConfigurationABC,
|
||||
env: ApplicationEnvironmentABC,
|
||||
logger: ApiLogger,
|
||||
t: TranslatePipe,
|
||||
api: Api,
|
||||
mail_settings: EMailClientSettings,
|
||||
mailer: EMailClientABC
|
||||
):
|
||||
self._config = config
|
||||
self._env = env
|
||||
self._logger = logger
|
||||
self._t = t
|
||||
self._api = api
|
||||
self._mail_settings = mail_settings
|
||||
self._mailer = mailer
|
||||
|
||||
@Route.get(f'{BasePath}/api-version')
|
||||
async def api_version(self):
|
||||
import bot_api
|
||||
version = bot_api.version_info
|
||||
return VersionDTO(version.major, version.minor, version.micro).to_dict()
|
||||
|
||||
@Route.get(f'{BasePath}/settings')
|
||||
async def settings(self):
|
||||
# TODO: Authentication
|
||||
import bot_api
|
||||
version = bot_api.version_info
|
||||
|
||||
return SettingsDTO(
|
||||
'',
|
||||
VersionDTO(version.major, version.minor, version.micro),
|
||||
os.path.abspath(os.path.join(self._env.working_directory, 'config')),
|
||||
'',
|
||||
'/',
|
||||
0,
|
||||
0,
|
||||
self._mail_settings.user_name,
|
||||
self._mail_settings.port,
|
||||
self._mail_settings.host,
|
||||
self._mail_settings.user_name,
|
||||
self._mail_settings.user_name,
|
||||
).to_dict()
|
||||
|
||||
@Route.get(f'{BasePath}/send-test-mail/<email>')
|
||||
async def send_test_mail(self, email: str):
|
||||
# TODO: Authentication
|
||||
mail = EMail()
|
||||
mail.add_header('Mime-Version: 1.0')
|
||||
mail.add_header('Content-Type: text/plain; charset=utf-8')
|
||||
mail.add_header('Content-Transfer-Encoding: quoted-printable')
|
||||
mail.add_receiver(email)
|
||||
mail.subject = self._t.transform('api.api.test_mail.subject')
|
||||
mail.body = self._t.transform('api.api.test_mail.message').format(self._env.host_name, self._env.environment_name)
|
||||
self._mailer.send_mail(mail)
|
26
kdb-bot/src/bot_api/exception/__init__.py
Normal file
26
kdb-bot/src/bot_api/exception/__init__.py
Normal 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_api.exception'
|
||||
__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')
|
19
kdb-bot/src/bot_api/exception/service_error_code_enum.py
Normal file
19
kdb-bot/src/bot_api/exception/service_error_code_enum.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ServiceErrorCode(Enum):
|
||||
|
||||
Unknown = 0
|
||||
|
||||
InvalidDependencies = 1
|
||||
InvalidData = 2
|
||||
NotFound = 3
|
||||
DataAlreadyExists = 4
|
||||
UnableToAdd = 5
|
||||
UnableToDelete = 6
|
||||
|
||||
InvalidUser = 7
|
||||
|
||||
ConnectionFailed = 8
|
||||
Timeout = 9
|
||||
MailError = 10
|
13
kdb-bot/src/bot_api/exception/service_exception.py
Normal file
13
kdb-bot/src/bot_api/exception/service_exception.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from bot_api.exception.service_error_code_enum import ServiceErrorCode
|
||||
|
||||
|
||||
class ServiceException(Exception):
|
||||
|
||||
def __init__(self, error_code: ServiceErrorCode, message: str, *args):
|
||||
Exception.__init__(self, *args)
|
||||
|
||||
self.error_code = error_code
|
||||
self.message = message
|
||||
|
||||
def get_detailed_message(self) -> str:
|
||||
return f'ServiceException - ErrorCode: {self.error_code} - ErrorMessage: {self.message}'
|
26
kdb-bot/src/bot_api/filter/__init__.py
Normal file
26
kdb-bot/src/bot_api/filter/__init__.py
Normal 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_api.filter'
|
||||
__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')
|
23
kdb-bot/src/bot_api/filter/auth_user_select_criteria.py
Normal file
23
kdb-bot/src/bot_api/filter/auth_user_select_criteria.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from bot_api.abc.select_criteria_abc import SelectCriteriaABC
|
||||
|
||||
|
||||
class AuthUserSelectCriteria(SelectCriteriaABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
page_index: int,
|
||||
page_size: int,
|
||||
sort_direction: str,
|
||||
sort_column: str,
|
||||
|
||||
first_name: str,
|
||||
last_name: str,
|
||||
email: str,
|
||||
auth_role: int
|
||||
):
|
||||
SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column)
|
||||
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.email = email
|
||||
self.auth_role = auth_role
|
39
kdb-bot/src/bot_api/json_processor.py
Normal file
39
kdb-bot/src/bot_api/json_processor.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from inspect import signature, Parameter
|
||||
|
||||
from cpl_core.utils import String
|
||||
|
||||
|
||||
class JSONProcessor:
|
||||
|
||||
@staticmethod
|
||||
def process(_t: type, values: dict) -> object:
|
||||
args = []
|
||||
|
||||
sig = signature(_t.__init__)
|
||||
for param in sig.parameters.items():
|
||||
parameter = param[1]
|
||||
if parameter.name == 'self' or parameter.annotation == Parameter.empty:
|
||||
continue
|
||||
|
||||
name = String.convert_to_camel_case(parameter.name)
|
||||
name = name.replace('Dto', 'DTO')
|
||||
name_first_lower = String.first_to_lower(name)
|
||||
if name in values or name_first_lower in values:
|
||||
value = ''
|
||||
if name in values:
|
||||
value = values[name]
|
||||
else:
|
||||
value = values[name_first_lower]
|
||||
|
||||
if isinstance(value, dict):
|
||||
value = JSONProcessor.process(parameter.annotation, value)
|
||||
|
||||
args.append(value)
|
||||
|
||||
elif parameter.default != Parameter.empty:
|
||||
args.append(parameter.default)
|
||||
|
||||
else:
|
||||
args.append(None)
|
||||
|
||||
return _t(*args)
|
26
kdb-bot/src/bot_api/logging/__init__.py
Normal file
26
kdb-bot/src/bot_api/logging/__init__.py
Normal 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_api.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')
|
11
kdb-bot/src/bot_api/logging/api_logger.py
Normal file
11
kdb-bot/src/bot_api/logging/api_logger.py
Normal 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 ApiLogger(CustomFileLoggerABC):
|
||||
|
||||
def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
|
||||
CustomFileLoggerABC.__init__(self, 'Api', config, time_format, env)
|
26
kdb-bot/src/bot_api/model/__init__.py
Normal file
26
kdb-bot/src/bot_api/model/__init__.py
Normal 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_api.model'
|
||||
__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')
|
99
kdb-bot/src/bot_api/model/auth_user_dto.py
Normal file
99
kdb-bot/src/bot_api/model/auth_user_dto.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from typing import Optional
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
from bot_data.model.auth_role_enum import AuthRoleEnum
|
||||
|
||||
|
||||
class AuthUserDTO(DtoABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: int,
|
||||
first_name: str,
|
||||
last_name: str,
|
||||
email: str,
|
||||
password: str,
|
||||
confirmation_id: Optional[str],
|
||||
auth_role: AuthRoleEnum,
|
||||
):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._id = id
|
||||
self._first_name = first_name
|
||||
self._last_name = last_name
|
||||
self._email = email
|
||||
self._password = password
|
||||
self._is_confirmed = confirmation_id is None
|
||||
self._auth_role = auth_role
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def first_name(self) -> str:
|
||||
return self._first_name
|
||||
|
||||
@first_name.setter
|
||||
def first_name(self, value: str):
|
||||
self._first_name = value
|
||||
|
||||
@property
|
||||
def last_name(self) -> str:
|
||||
return self._last_name
|
||||
|
||||
@last_name.setter
|
||||
def last_name(self, value: str):
|
||||
self._last_name = value
|
||||
|
||||
@property
|
||||
def email(self) -> str:
|
||||
return self._email
|
||||
|
||||
@email.setter
|
||||
def email(self, value: str):
|
||||
self._email = value
|
||||
|
||||
@property
|
||||
def password(self) -> str:
|
||||
return self._password
|
||||
|
||||
@password.setter
|
||||
def password(self, value: str):
|
||||
self._password = value
|
||||
|
||||
@property
|
||||
def is_confirmed(self) -> Optional[str]:
|
||||
return self._is_confirmed
|
||||
|
||||
@is_confirmed.setter
|
||||
def is_confirmed(self, value: Optional[str]):
|
||||
self._is_confirmed = value
|
||||
|
||||
@property
|
||||
def auth_role(self) -> AuthRoleEnum:
|
||||
return self._auth_role
|
||||
|
||||
@auth_role.setter
|
||||
def auth_role(self, value: AuthRoleEnum):
|
||||
self._auth_role = value
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._id = values['id']
|
||||
self._first_name = values['firstName']
|
||||
self._last_name = values['lastName']
|
||||
self._email = values['email']
|
||||
self._password = values['password']
|
||||
self._is_confirmed = values['isConfirmed']
|
||||
self._auth_role = values['authRole']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'id': self._id,
|
||||
'firstName': self._first_name,
|
||||
'lastName': self._last_name,
|
||||
'email': self._email,
|
||||
'password': self._password,
|
||||
'isConfirmed': self._is_confirmed,
|
||||
'authRole': self._auth_role.value,
|
||||
}
|
21
kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py
Normal file
21
kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from cpl_query.extension import List
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
from bot_data.filtered_result import FilteredResult
|
||||
|
||||
|
||||
class AuthUserFilteredResultDTO(DtoABC, FilteredResult):
|
||||
|
||||
def __init__(self, result: List = None, total_count: int = 0):
|
||||
DtoABC.__init__(self)
|
||||
FilteredResult.__init__(self, result, total_count)
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._result = values['users']
|
||||
self._total_count = values['totalCount']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'users': self.result,
|
||||
'totalCount': self.total_count
|
||||
}
|
21
kdb-bot/src/bot_api/model/email_string_dto.py
Normal file
21
kdb-bot/src/bot_api/model/email_string_dto.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
|
||||
|
||||
class EMailStringDTO(DtoABC):
|
||||
|
||||
def __init__(self, email: str):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._email = email
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._email = values['email']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'email': self._email
|
||||
}
|
34
kdb-bot/src/bot_api/model/error_dto.py
Normal file
34
kdb-bot/src/bot_api/model/error_dto.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import traceback
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
from bot_api.exception.service_error_code_enum import ServiceErrorCode
|
||||
|
||||
|
||||
class ErrorDTO(DtoABC):
|
||||
|
||||
def __init__(self, error_code: Optional[ServiceErrorCode], message: str):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._error_code = ServiceErrorCode.Unknown if error_code is None else error_code
|
||||
self._message = message
|
||||
|
||||
@property
|
||||
def error_code(self) -> ServiceErrorCode:
|
||||
return self._error_code
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
return self._message
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._error_code = values['ErrorCode']
|
||||
self._message = values['Message']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'errorCode': int(self._error_code.value),
|
||||
'message': self._message
|
||||
}
|
32
kdb-bot/src/bot_api/model/reset_password_dto.py
Normal file
32
kdb-bot/src/bot_api/model/reset_password_dto.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
|
||||
|
||||
class ResetPasswordDTO(DtoABC):
|
||||
|
||||
def __init__(self, id: str, password: str):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._id = id
|
||||
self._password = password
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def password(self) -> str:
|
||||
return self._password
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._id = values['id']
|
||||
self._password = values['password']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'id': self._id,
|
||||
'password': self._password
|
||||
}
|
67
kdb-bot/src/bot_api/model/settings_dto.py
Normal file
67
kdb-bot/src/bot_api/model/settings_dto.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
from bot_api.model.version_dto import VersionDTO
|
||||
|
||||
|
||||
class SettingsDTO(DtoABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
web_version: str,
|
||||
api_version: VersionDTO,
|
||||
config_path: str,
|
||||
web_base_url: str,
|
||||
api_base_url: str,
|
||||
token_expire_time: int,
|
||||
refresh_token_expire_time: int,
|
||||
mail_user: str,
|
||||
mail_port: int,
|
||||
mail_host: str,
|
||||
mail_transceiver: str,
|
||||
mail_transceiver_address: str,
|
||||
):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._web_version = web_version
|
||||
self._api_version = api_version
|
||||
self._config_path = config_path
|
||||
self._web_base_url = web_base_url
|
||||
self._api_base_url = api_base_url
|
||||
|
||||
self._token_expire_time = token_expire_time
|
||||
self._refresh_token_expire_time = refresh_token_expire_time
|
||||
|
||||
self._mail_user = mail_user
|
||||
self._mail_port = mail_port
|
||||
self._mail_host = mail_host
|
||||
self._mail_transceiver = mail_transceiver
|
||||
self._mail_transceiver_address = mail_transceiver_address
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._web_version = values['webVersion']
|
||||
self._api_version.from_dict(values['apiVersion'])
|
||||
self._config_path = values['configPath']
|
||||
self._web_base_url = values['webBaseURL']
|
||||
self._api_base_url = values['apiBaseURL']
|
||||
self._token_expire_time = values['tokenExpireTime']
|
||||
self._refresh_token_expire_time = values['refreshTokenExpireTime']
|
||||
self._mail_user = values['mailUser']
|
||||
self._mail_port = values['mailPort']
|
||||
self._mail_host = values['mailHost']
|
||||
self._mail_transceiver = values['mailTransceiver']
|
||||
self._mail_transceiver_address = values['mailTransceiverAddress']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'webVersion': self._web_version,
|
||||
'apiVersion': self._api_version.to_dict(),
|
||||
'configPath': self._config_path,
|
||||
'webBaseURL': self._web_base_url,
|
||||
'apiBaseURL': self._api_base_url,
|
||||
'tokenExpireTime': self._token_expire_time,
|
||||
'refreshTokenExpireTime': self._refresh_token_expire_time,
|
||||
'mailUser': self._mail_user,
|
||||
'mailPort': self._mail_port,
|
||||
'mailHost': self._mail_host,
|
||||
'mailTransceiver': self._mail_transceiver,
|
||||
'mailTransceiverAddress': self._mail_transceiver_address,
|
||||
}
|
32
kdb-bot/src/bot_api/model/token_dto.py
Normal file
32
kdb-bot/src/bot_api/model/token_dto.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
|
||||
|
||||
class TokenDTO(DtoABC):
|
||||
|
||||
def __init__(self, token: str, refresh_token: str):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._token = token
|
||||
self._refresh_token = refresh_token
|
||||
|
||||
@property
|
||||
def token(self) -> str:
|
||||
return self._token
|
||||
|
||||
@property
|
||||
def refresh_token(self) -> str:
|
||||
return self._refresh_token
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._token = values['token']
|
||||
self._refresh_token = values['refreshToken']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'token': self._token,
|
||||
'refreshToken': self._refresh_token
|
||||
}
|
45
kdb-bot/src/bot_api/model/update_auth_user_dto.py
Normal file
45
kdb-bot/src/bot_api/model/update_auth_user_dto.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
from bot_api.model.auth_user_dto import AuthUserDTO
|
||||
|
||||
|
||||
class UpdateAuthUserDTO(DtoABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
auth_user_dto: AuthUserDTO,
|
||||
new_auth_user_dto: AuthUserDTO,
|
||||
change_password=False
|
||||
):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._auth_user = auth_user_dto
|
||||
self._new_auth_user = new_auth_user_dto
|
||||
self._change_password = change_password
|
||||
|
||||
@property
|
||||
def auth_user(self) -> AuthUserDTO:
|
||||
return self._auth_user
|
||||
|
||||
@property
|
||||
def new_auth_user(self) -> AuthUserDTO:
|
||||
return self._new_auth_user
|
||||
|
||||
@property
|
||||
def change_password(self) -> bool:
|
||||
return self._change_password
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._auth_user = values['authUser']
|
||||
self._new_auth_user = values['newAuthUser']
|
||||
self._change_password = False if 'changePassword' not in values else values['changePassword']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'authUser': self._auth_user,
|
||||
'newAuthUser': self._new_auth_user,
|
||||
'changePassword': self._change_password
|
||||
}
|
27
kdb-bot/src/bot_api/model/version_dto.py
Normal file
27
kdb-bot/src/bot_api/model/version_dto.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.console import Console
|
||||
|
||||
from bot_api.abc.dto_abc import DtoABC
|
||||
|
||||
|
||||
class VersionDTO(DtoABC):
|
||||
|
||||
def __init__(self, major: str = None, minor: str = None, micro: str = None):
|
||||
DtoABC.__init__(self)
|
||||
|
||||
self._major = major
|
||||
self._minor = minor
|
||||
self._micro = micro
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._major = values['major']
|
||||
self._minor = values['minor']
|
||||
self._micro = values['micro']
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'major': self._major,
|
||||
'minor': self._minor,
|
||||
'micro': self._micro,
|
||||
}
|
26
kdb-bot/src/bot_api/route/__init__.py
Normal file
26
kdb-bot/src/bot_api/route/__init__.py
Normal 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_api.route'
|
||||
__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')
|
35
kdb-bot/src/bot_api/route/route.py
Normal file
35
kdb-bot/src/bot_api/route/route.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from flask_cors import cross_origin
|
||||
|
||||
|
||||
class Route:
|
||||
registered_routes = {}
|
||||
|
||||
@classmethod
|
||||
def route(cls, path=None, **kwargs):
|
||||
# simple decorator for class based views
|
||||
def inner(fn):
|
||||
cross_origin(fn)
|
||||
cls.registered_routes[path] = (fn, kwargs)
|
||||
return fn
|
||||
|
||||
return inner
|
||||
|
||||
@classmethod
|
||||
def get(cls, path=None, **kwargs):
|
||||
return cls.route(path, methods=['GET'], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def post(cls, path=None, **kwargs):
|
||||
return cls.route(path, methods=['POST'], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def head(cls, path=None, **kwargs):
|
||||
return cls.route(path, methods=['HEAD'], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def put(cls, path=None, **kwargs):
|
||||
return cls.route(path, methods=['PUT'], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, path=None, **kwargs):
|
||||
return cls.route(path, methods=['DELETE'], **kwargs)
|
26
kdb-bot/src/bot_api/service/__init__.py
Normal file
26
kdb-bot/src/bot_api/service/__init__.py
Normal 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_api.service'
|
||||
__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')
|
385
kdb-bot/src/bot_api/service/auth_service.py
Normal file
385
kdb-bot/src/bot_api/service/auth_service.py
Normal file
@@ -0,0 +1,385 @@
|
||||
import hashlib
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional
|
||||
|
||||
import jwt
|
||||
from cpl_core.database.context import DatabaseContextABC
|
||||
from cpl_core.mailing import EMailClientABC, EMail
|
||||
from cpl_query.extension import List
|
||||
from cpl_translation import TranslatePipe
|
||||
|
||||
from bot_api.abc.auth_service_abc import AuthServiceABC
|
||||
from bot_api.configuration.authentication_settings import AuthenticationSettings
|
||||
from bot_api.configuration.frontend_settings import FrontendSettings
|
||||
from bot_api.exception.service_error_code_enum import ServiceErrorCode
|
||||
from bot_api.exception.service_exception import ServiceException
|
||||
from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_api.model.auth_user_dto import AuthUserDTO
|
||||
from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO
|
||||
from bot_api.model.email_string_dto import EMailStringDTO
|
||||
from bot_api.model.reset_password_dto import ResetPasswordDTO
|
||||
from bot_api.model.token_dto import TokenDTO
|
||||
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
|
||||
from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT
|
||||
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
|
||||
from bot_data.model.auth_user import AuthUser
|
||||
|
||||
|
||||
class AuthService(AuthServiceABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: ApiLogger,
|
||||
auth_users: AuthUserRepositoryABC,
|
||||
db: DatabaseContextABC,
|
||||
mailer: EMailClientABC,
|
||||
t: TranslatePipe,
|
||||
auth_settings: AuthenticationSettings,
|
||||
frontend_settings: FrontendSettings,
|
||||
|
||||
):
|
||||
AuthServiceABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._auth_users = auth_users
|
||||
self._db = db
|
||||
self._mailer = mailer
|
||||
self._t = t
|
||||
self._auth_settings = auth_settings
|
||||
self._frontend_settings = frontend_settings
|
||||
|
||||
@staticmethod
|
||||
def _get_mail_to_send() -> EMail:
|
||||
mail = EMail()
|
||||
mail.add_header('Mime-Version: 1.0')
|
||||
mail.add_header('Content-Type: text/plain charset=utf-8')
|
||||
mail.add_header('Content-Transfer-Encoding: quoted-printable')
|
||||
return mail
|
||||
|
||||
@staticmethod
|
||||
def _hash_sha256(password: str) -> str:
|
||||
return hashlib.sha256(password.encode('utf-8')).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def _is_email_valid(email: str) -> bool:
|
||||
if re.match(re.compile(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'), email) is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _generate_token(self, user: AuthUser) -> str:
|
||||
token = jwt.encode(
|
||||
payload={
|
||||
'user_id': user.id,
|
||||
'email': user.email,
|
||||
'role': user.auth_role.value,
|
||||
'exp': datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time),
|
||||
'iss': self._auth_settings.issuer,
|
||||
'aud': self._auth_settings.audience
|
||||
},
|
||||
key=self._auth_settings.secret_key
|
||||
)
|
||||
|
||||
return token
|
||||
|
||||
def _decode_token(self, token: str) -> dict:
|
||||
return jwt.decode(
|
||||
token,
|
||||
key=self._auth_settings.secret_key,
|
||||
issuer=self._auth_settings.issuer,
|
||||
audience=self._auth_settings.audience,
|
||||
algorithms=['HS256']
|
||||
)
|
||||
|
||||
def _create_and_save_refresh_token(self, user: AuthUser) -> str:
|
||||
token = str(uuid.uuid4())
|
||||
user.refresh_token = token
|
||||
user.refresh_token_expire_time = datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.refresh_token_expire_time)
|
||||
self._auth_users.update_auth_user(user)
|
||||
self._db.save_changes()
|
||||
return token
|
||||
|
||||
def _send_confirmation_id_to_user(self, user: AuthUser):
|
||||
url = self._frontend_settings.url
|
||||
if not url.endswith('/'):
|
||||
url = f'{url}/'
|
||||
|
||||
mail = self._get_mail_to_send()
|
||||
mail.add_receiver(user.email)
|
||||
mail.subject = self._t.transform('api.auth.confirmation.subject').format(user.first_name, user.last_name)
|
||||
mail.body = self._t.transform('api.auth.confirmation.message').format(url, user.confirmation_id)
|
||||
self._mailer.send_mail(mail)
|
||||
|
||||
def _send_forgot_password_id_to_user(self, user: AuthUser):
|
||||
url = self._frontend_settings.url
|
||||
if not url.endswith('/'):
|
||||
url = f'{url}/'
|
||||
|
||||
mail = self._get_mail_to_send()
|
||||
mail.add_receiver(user.email)
|
||||
mail.subject = self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name)
|
||||
mail.body = self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id)
|
||||
self._mailer.send_mail(mail)
|
||||
|
||||
async def get_all_auth_users_async(self) -> List[AuthUserDTO]:
|
||||
result = self._auth_users.get_all_auth_users() \
|
||||
.select(lambda x: AUT.to_dto(x))
|
||||
return List(AuthUserDTO, result)
|
||||
|
||||
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO:
|
||||
users = self._auth_users.get_filtered_auth_users(criteria)
|
||||
result = users.result.select(lambda x: AUT.to_dto(x))
|
||||
|
||||
return AuthUserFilteredResultDTO(
|
||||
List(AuthUserDTO, result),
|
||||
users.total_count
|
||||
)
|
||||
|
||||
async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO:
|
||||
try:
|
||||
return AUT.to_dto(self._auth_users.get_auth_user_by_email(email))
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'AuthUser not found', e)
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'User not found {email}')
|
||||
|
||||
async def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]:
|
||||
user = self._auth_users.find_auth_user_by_email(email)
|
||||
return AUT.to_dto(user) if user is not None else None
|
||||
|
||||
async def add_auth_user_async(self, user_dto: AuthUser):
|
||||
db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
|
||||
if db_user is not None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists')
|
||||
|
||||
user_dto.password = self._hash_sha256(user_dto.password)
|
||||
user = AUT.to_db(user_dto)
|
||||
if not self._is_email_valid(user.email):
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address')
|
||||
|
||||
try:
|
||||
user.confirmation_id = uuid.uuid4()
|
||||
self._auth_users.add_auth_user(user)
|
||||
self._send_confirmation_id_to_user(user)
|
||||
self._db.save_changes()
|
||||
self._logger.info(__name__, f'Added auth user with E-Mail: {user_dto.email}')
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot add user with E-Mal {user_dto.email}', e)
|
||||
raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail")
|
||||
|
||||
async def update_user_async(self, update_user_dto: UpdateAuthUserDTO):
|
||||
if update_user_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty')
|
||||
|
||||
if update_user_dto.auth_user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty')
|
||||
|
||||
if update_user_dto.new_auth_user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty')
|
||||
|
||||
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email):
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail')
|
||||
|
||||
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
|
||||
if user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found')
|
||||
|
||||
if user.confirmation_id is not None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed')
|
||||
|
||||
# update first name
|
||||
if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name:
|
||||
user.first_name = update_user_dto.new_auth_user.first_name
|
||||
|
||||
# update last name
|
||||
if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and \
|
||||
update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name:
|
||||
user.last_name = update_user_dto.new_auth_user.last_name
|
||||
|
||||
# update E-Mail
|
||||
if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email:
|
||||
user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email)
|
||||
if user_by_new_e_mail is not None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists')
|
||||
user.email = update_user_dto.new_auth_user.email
|
||||
|
||||
is_existing_password_set = False
|
||||
is_new_password_set = False
|
||||
# hash passwords in DTOs
|
||||
if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '':
|
||||
is_existing_password_set = True
|
||||
update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password)
|
||||
|
||||
if update_user_dto.auth_user.password != user.password:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password')
|
||||
|
||||
if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '':
|
||||
is_new_password_set = True
|
||||
update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password)
|
||||
|
||||
# update password
|
||||
if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password:
|
||||
user.password = update_user_dto.new_auth_user.password
|
||||
|
||||
self._auth_users.update_auth_user(user)
|
||||
self._db.save_changes()
|
||||
|
||||
async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO):
|
||||
if update_user_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty')
|
||||
|
||||
if update_user_dto.auth_user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty')
|
||||
|
||||
if update_user_dto.new_auth_user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty')
|
||||
|
||||
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email):
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail')
|
||||
|
||||
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
|
||||
if user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found')
|
||||
|
||||
if user.confirmation_id is not None and update_user_dto.new_auth_user.is_confirmed:
|
||||
user.confirmation_id = None
|
||||
elif user.confirmation_id is None and not update_user_dto.new_auth_user.is_confirmed:
|
||||
user.confirmation_id = uuid.uuid4()
|
||||
# else
|
||||
# raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed')
|
||||
|
||||
# update first name
|
||||
if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name:
|
||||
user.first_name = update_user_dto.new_auth_user.first_name
|
||||
|
||||
# update last name
|
||||
if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name:
|
||||
user.last_name = update_user_dto.new_auth_user.last_name
|
||||
|
||||
# update E-Mail
|
||||
if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email:
|
||||
user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email)
|
||||
if user_by_new_e_mail is not None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists')
|
||||
user.email = update_user_dto.new_auth_user.email
|
||||
|
||||
# update password
|
||||
if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password:
|
||||
user.password = self._hash_sha256(update_user_dto.new_auth_user.password)
|
||||
|
||||
# update role
|
||||
if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role:
|
||||
user.auth_role = update_user_dto.new_auth_user.auth_role
|
||||
|
||||
self._db.save_changes()
|
||||
|
||||
async def delete_auth_user_by_email_async(self, email: str):
|
||||
try:
|
||||
user = self._auth_users.get_auth_user_by_email(email)
|
||||
self._auth_users.delete_auth_user(user)
|
||||
self._db.save_changes()
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot delete user', e)
|
||||
raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {email}')
|
||||
|
||||
async def delete_auth_user_async(self, user_dto: AuthUser):
|
||||
try:
|
||||
self._auth_users.delete_auth_user(AUT.to_db(user_dto))
|
||||
self._db.save_changes()
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot delete user', e)
|
||||
raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {user_dto.email}')
|
||||
|
||||
async def login_async(self, user_dto: AuthUser) -> TokenDTO:
|
||||
if user_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'User not set')
|
||||
|
||||
db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
|
||||
if db_user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found')
|
||||
|
||||
user_dto.password = self._hash_sha256(user_dto.password)
|
||||
if db_user.password != user_dto.password:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password')
|
||||
|
||||
token = self._generate_token(db_user)
|
||||
refresh_token = self._create_and_save_refresh_token(db_user)
|
||||
if db_user.forgot_password_id is not None:
|
||||
db_user.forgot_password_id = None
|
||||
|
||||
self._db.save_changes()
|
||||
return TokenDTO(token, refresh_token)
|
||||
|
||||
async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO:
|
||||
if token_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set')
|
||||
|
||||
token = self._decode_token(token_dto.token)
|
||||
if token is None or 'email' not in token:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid')
|
||||
|
||||
try:
|
||||
user = self._auth_users.get_auth_user_by_email(token['email'])
|
||||
if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now():
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired')
|
||||
|
||||
return TokenDTO(self._generate_token(user), self._create_and_save_refresh_token(user))
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Refreshing token failed', e)
|
||||
return TokenDTO('', '')
|
||||
|
||||
async def revoke_async(self, token_dto: TokenDTO):
|
||||
if token_dto is None or token_dto.token is None or token_dto.refresh_token is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'Token not set')
|
||||
|
||||
token = self._decode_token(token_dto.token)
|
||||
try:
|
||||
user = self._auth_users.get_auth_user_by_email(token['email'])
|
||||
if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now():
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired')
|
||||
|
||||
user.refresh_token = None
|
||||
self._auth_users.update_auth_user(user)
|
||||
self._db.save_changes()
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Refreshing token failed', e)
|
||||
|
||||
async def confirm_email_async(self, id: str) -> bool:
|
||||
user = self._auth_users.find_auth_user_by_confirmation_id(id)
|
||||
if user is None:
|
||||
return False
|
||||
|
||||
user.confirmation_id = None
|
||||
self._auth_users.update_auth_user(user)
|
||||
self._db.save_changes()
|
||||
return True
|
||||
|
||||
async def forgot_password_async(self, email: str):
|
||||
user = self._auth_users.find_auth_user_by_email(email)
|
||||
if user is None:
|
||||
return
|
||||
|
||||
user.forgot_password_id = uuid.uuid4()
|
||||
self._auth_users.update_auth_user(user)
|
||||
self._send_forgot_password_id_to_user(user)
|
||||
self._db.save_changes()
|
||||
|
||||
async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO:
|
||||
user = self._auth_users.find_auth_user_by_forgot_password_id(id)
|
||||
return EMailStringDTO(user.email)
|
||||
|
||||
async def reset_password_async(self, rp_dto: ResetPasswordDTO):
|
||||
user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id)
|
||||
if user is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, f'User by forgot password id {rp_dto.id} not found')
|
||||
|
||||
if user.confirmation_id is not None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidUser, f'E-Mail not confirmed')
|
||||
|
||||
if user.password is None or rp_dto.password == '':
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set')
|
||||
|
||||
user.password = self._hash_sha256(rp_dto.password)
|
||||
self._db.save_changes()
|
26
kdb-bot/src/bot_api/transformer/__init__.py
Normal file
26
kdb-bot/src/bot_api/transformer/__init__.py
Normal 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_api.transformer'
|
||||
__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')
|
36
kdb-bot/src/bot_api/transformer/auth_user_transformer.py
Normal file
36
kdb-bot/src/bot_api/transformer/auth_user_transformer.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from bot_api.abc.auth_user_transformer_abc import AuthUserTransformerABC
|
||||
from bot_api.model.auth_user_dto import AuthUserDTO
|
||||
from bot_data.model.auth_role_enum import AuthRoleEnum
|
||||
from bot_data.model.auth_user import AuthUser
|
||||
|
||||
|
||||
class AuthUserTransformer(AuthUserTransformerABC):
|
||||
|
||||
@staticmethod
|
||||
def to_db(dto: AuthUser) -> AuthUser:
|
||||
return AuthUser(
|
||||
dto.first_name,
|
||||
dto.last_name,
|
||||
dto.email,
|
||||
dto.password,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
datetime.now(tz=timezone.utc),
|
||||
AuthRoleEnum.normal if dto.auth_role is None else dto.auth_role,
|
||||
id=0 if dto.id is None else dto.id
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def to_dto(db: AuthUser) -> AuthUserDTO:
|
||||
return AuthUserDTO(
|
||||
db.id,
|
||||
db.first_name,
|
||||
db.last_name,
|
||||
db.email,
|
||||
db.password,
|
||||
db.confirmation_id,
|
||||
db.auth_role
|
||||
)
|
26
kdb-bot/src/bot_core/__init__.py
Normal file
26
kdb-bot/src/bot_core/__init__.py
Normal 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'
|
||||
__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')
|
26
kdb-bot/src/bot_core/abc/__init__.py
Normal file
26
kdb-bot/src/bot_core/abc/__init__.py
Normal 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.abc'
|
||||
__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')
|
24
kdb-bot/src/bot_core/abc/client_utils_service_abc.py
Normal file
24
kdb-bot/src/bot_core/abc/client_utils_service_abc.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from discord.ext.commands import Context
|
||||
|
||||
|
||||
class ClientUtilsServiceABC(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
def received_command(self, guild_id: int): pass
|
||||
|
||||
@abstractmethod
|
||||
def moved_user(self, guild_id: int): pass
|
||||
|
||||
@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
|
60
kdb-bot/src/bot_core/abc/custom_file_logger_abc.py
Normal file
60
kdb-bot/src/bot_core/abc/custom_file_logger_abc.py
Normal 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)
|
27
kdb-bot/src/bot_core/abc/message_service_abc.py
Normal file
27
kdb-bot/src/bot_core/abc/message_service_abc.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Union
|
||||
|
||||
import discord
|
||||
from cpl_query.extension import List
|
||||
from discord.ext.commands import Context
|
||||
|
||||
|
||||
class MessageServiceABC(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_message(self, message: discord.Message, without_tracking=False): pass
|
||||
|
||||
@abstractmethod
|
||||
async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass
|
||||
|
||||
@abstractmethod
|
||||
async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass
|
||||
|
||||
@abstractmethod
|
||||
async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
20
kdb-bot/src/bot_core/abc/module_abc.py
Normal file
20
kdb-bot/src/bot_core/abc/module_abc.py
Normal 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
|
46
kdb-bot/src/bot_core/bot-core.json
Normal file
46
kdb-bot/src/bot_core/bot-core.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "bot-core",
|
||||
"Version": {
|
||||
"Major": "1",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "Sven Heidemann",
|
||||
"AuthorEmail": "sven.heidemann@sh-edraft.de",
|
||||
"Description": "Keksdose bot - core",
|
||||
"LongDescription": "Discord bot for the Keksdose discord Server - core package",
|
||||
"URL": "https://www.sh-edraft.de",
|
||||
"CopyrightDate": "2022",
|
||||
"CopyrightName": "sh-edraft.de",
|
||||
"LicenseName": "MIT",
|
||||
"LicenseDescription": "MIT, see LICENSE for more details.",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli==2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "",
|
||||
"EntryPoint": "",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
26
kdb-bot/src/bot_core/configuration/__init__.py
Normal file
26
kdb-bot/src/bot_core/configuration/__init__.py
Normal 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.configuration'
|
||||
__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')
|
33
kdb-bot/src/bot_core/configuration/bot_logging_settings.py
Normal file
33
kdb-bot/src/bot_core/configuration/bot_logging_settings.py
Normal 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)
|
53
kdb-bot/src/bot_core/configuration/bot_settings.py
Normal file
53
kdb-bot/src/bot_core/configuration/bot_settings.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
from cpl_query.extension import List
|
||||
|
||||
from bot_core.configuration.server_settings import ServerSettings
|
||||
|
||||
|
||||
class BotSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
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]:
|
||||
return self._technicians
|
||||
|
||||
@property
|
||||
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._wait_for_restart = settings["WaitForRestart"]
|
||||
self._wait_for_shutdown = settings["WaitForShutdown"]
|
||||
settings.pop("Technicians")
|
||||
settings.pop("WaitForRestart")
|
||||
settings.pop("WaitForShutdown")
|
||||
servers = List(ServerSettings)
|
||||
for s in settings:
|
||||
st = ServerSettings()
|
||||
settings[s]["Id"] = s
|
||||
st.from_dict(settings[s])
|
||||
servers.append(st)
|
||||
self._servers = servers
|
||||
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()}')
|
20
kdb-bot/src/bot_core/configuration/feature_flags_enum.py
Normal file
20
kdb-bot/src/bot_core/configuration/feature_flags_enum.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class FeatureFlagsEnum(Enum):
|
||||
|
||||
# modules
|
||||
api_module = 'ApiModule'
|
||||
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
|
||||
api_only = 'ApiOnly'
|
||||
presence = 'Presence'
|
50
kdb-bot/src/bot_core/configuration/feature_flags_settings.py
Normal file
50
kdb-bot/src/bot_core/configuration/feature_flags_settings.py
Normal file
@@ -0,0 +1,50 @@
|
||||
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.api_module.value: False, # 13.10.2022 #70
|
||||
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.api_only.value: False, # 13.10.2022 #70
|
||||
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()}')
|
24
kdb-bot/src/bot_core/configuration/file_logging_settings.py
Normal file
24
kdb-bot/src/bot_core/configuration/file_logging_settings.py
Normal 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()}')
|
29
kdb-bot/src/bot_core/configuration/server_settings.py
Normal file
29
kdb-bot/src/bot_core/configuration/server_settings.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class ServerSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._id: int = 0
|
||||
self._message_delete_timer: int = 0
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def message_delete_timer(self) -> int:
|
||||
return self._message_delete_timer
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._id = int(settings['Id'])
|
||||
self._message_delete_timer = int(settings['MessageDeleteTimer'])
|
||||
except Exception as e:
|
||||
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in settings')
|
||||
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
|
26
kdb-bot/src/bot_core/core_extension/__init__.py
Normal file
26
kdb-bot/src/bot_core/core_extension/__init__.py
Normal 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')
|
21
kdb-bot/src/bot_core/core_extension/core_extension_module.py
Normal file
21
kdb-bot/src/bot_core/core_extension/core_extension_module.py
Normal 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)
|
@@ -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')
|
32
kdb-bot/src/bot_core/core_module.py
Normal file
32
kdb-bot/src/bot_core/core_module.py
Normal 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)
|
26
kdb-bot/src/bot_core/events/__init__.py
Normal file
26
kdb-bot/src/bot_core/events/__init__.py
Normal 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')
|
30
kdb-bot/src/bot_core/events/core_on_ready_event.py
Normal file
30
kdb-bot/src/bot_core/events/core_on_ready_event.py
Normal 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')
|
26
kdb-bot/src/bot_core/helper/__init__.py
Normal file
26
kdb-bot/src/bot_core/helper/__init__.py
Normal 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')
|
9
kdb-bot/src/bot_core/helper/log_message_helper.py
Normal file
9
kdb-bot/src/bot_core/helper/log_message_helper.py
Normal 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}'
|
26
kdb-bot/src/bot_core/logging/__init__.py
Normal file
26
kdb-bot/src/bot_core/logging/__init__.py
Normal 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')
|
11
kdb-bot/src/bot_core/logging/command_logger.py
Normal file
11
kdb-bot/src/bot_core/logging/command_logger.py
Normal 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)
|
11
kdb-bot/src/bot_core/logging/database_logger.py
Normal file
11
kdb-bot/src/bot_core/logging/database_logger.py
Normal 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)
|
11
kdb-bot/src/bot_core/logging/message_logger.py
Normal file
11
kdb-bot/src/bot_core/logging/message_logger.py
Normal 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)
|
26
kdb-bot/src/bot_core/pipes/__init__.py
Normal file
26
kdb-bot/src/bot_core/pipes/__init__.py
Normal 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.pipes'
|
||||
__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')
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user