forked from sh-edraft.de/sh_discord_bot
Added flask support #70
This commit is contained in:
parent
d05dec3605
commit
0019f868ad
@ -11,13 +11,13 @@
|
|||||||
"boot-log": "src/modules/boot_log/boot-log.json",
|
"boot-log": "src/modules/boot_log/boot-log.json",
|
||||||
"database": "src/modules/database/database.json",
|
"database": "src/modules/database/database.json",
|
||||||
"moderator": "src/modules/moderator/moderator.json",
|
"moderator": "src/modules/moderator/moderator.json",
|
||||||
"permission": "src/modules/permission/permission.json"
|
"permission": "src/modules/permission/permission.json",
|
||||||
|
"bot-api": "src/bot_api/bot-api.json"
|
||||||
},
|
},
|
||||||
"Scripts": {
|
"Scripts": {
|
||||||
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
|
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
|
||||||
"stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;",
|
"stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;",
|
||||||
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;",
|
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;",
|
||||||
|
|
||||||
"build-docker": "cpl b; docker-compose down; docker build -t kdb .",
|
"build-docker": "cpl b; docker-compose down; docker build -t kdb .",
|
||||||
"compose": "docker-compose up -d",
|
"compose": "docker-compose up -d",
|
||||||
"docker": "cpl build-docker; cpl compose;"
|
"docker": "cpl build-docker; cpl compose;"
|
||||||
|
@ -7,6 +7,10 @@ from cpl_discord.configuration import DiscordBotSettings
|
|||||||
from cpl_discord.service import DiscordBotServiceABC, DiscordBotService
|
from cpl_discord.service import DiscordBotServiceABC, DiscordBotService
|
||||||
from cpl_translation import TranslatePipe, TranslationServiceABC, TranslationSettings
|
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):
|
class Application(DiscordBotApplicationABC):
|
||||||
|
|
||||||
@ -14,6 +18,7 @@ class Application(DiscordBotApplicationABC):
|
|||||||
DiscordBotApplicationABC.__init__(self, config, services)
|
DiscordBotApplicationABC.__init__(self, config, services)
|
||||||
|
|
||||||
self._services = services
|
self._services = services
|
||||||
|
self._config = config
|
||||||
|
|
||||||
# cpl-core
|
# cpl-core
|
||||||
self._logger: LoggerABC = services.get_service(LoggerABC)
|
self._logger: LoggerABC = services.get_service(LoggerABC)
|
||||||
@ -24,6 +29,12 @@ class Application(DiscordBotApplicationABC):
|
|||||||
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
|
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
|
||||||
self._t: TranslatePipe = services.get_service(TranslatePipe)
|
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
|
self._is_stopping = False
|
||||||
|
|
||||||
async def configure(self):
|
async def configure(self):
|
||||||
@ -32,6 +43,12 @@ class Application(DiscordBotApplicationABC):
|
|||||||
async def main(self):
|
async def main(self):
|
||||||
try:
|
try:
|
||||||
self._logger.debug(__name__, f'Starting...')
|
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):
|
||||||
|
return
|
||||||
|
|
||||||
self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}')
|
self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}')
|
||||||
await self._bot.start_async()
|
await self._bot.start_async()
|
||||||
await self._bot.stop_async()
|
await self._bot.stop_async()
|
||||||
|
@ -12,6 +12,12 @@
|
|||||||
"FileLogLevel": "TRACE"
|
"FileLogLevel": "TRACE"
|
||||||
},
|
},
|
||||||
"BotLoggingSettings": {
|
"BotLoggingSettings": {
|
||||||
|
"Api": {
|
||||||
|
"Path": "logs/",
|
||||||
|
"Filename": "api.log",
|
||||||
|
"ConsoleLogLevel": "TRACE",
|
||||||
|
"FileLogLevel": "TRACE"
|
||||||
|
},
|
||||||
"Command": {
|
"Command": {
|
||||||
"Path": "logs/",
|
"Path": "logs/",
|
||||||
"Filename": "commands.log",
|
"Filename": "commands.log",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"FeatureFlags": {
|
"FeatureFlags": {
|
||||||
|
"ApiModule": true,
|
||||||
"AdminModule": true,
|
"AdminModule": true,
|
||||||
"AutoRoleModule": true,
|
"AutoRoleModule": true,
|
||||||
"BaseModule": true,
|
"BaseModule": true,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from cpl_query.extension import List
|
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_extension.core_extension_module import CoreExtensionModule
|
||||||
from bot_core.core_module import CoreModule
|
from bot_core.core_module import CoreModule
|
||||||
from bot_data.data_module import DataModule
|
from bot_data.data_module import DataModule
|
||||||
@ -20,6 +21,7 @@ class ModuleList:
|
|||||||
return List(type, [
|
return List(type, [
|
||||||
CoreModule, # has to be first!
|
CoreModule, # has to be first!
|
||||||
DataModule,
|
DataModule,
|
||||||
|
ApiModule,
|
||||||
AdminModule,
|
AdminModule,
|
||||||
AutoRoleModule,
|
AutoRoleModule,
|
||||||
BaseModule,
|
BaseModule,
|
||||||
|
@ -9,6 +9,7 @@ from cpl_core.dependency_injection import ServiceProviderABC
|
|||||||
from cpl_core.environment import ApplicationEnvironment
|
from cpl_core.environment import ApplicationEnvironment
|
||||||
from cpl_core.logging import LoggerABC
|
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.abc.custom_file_logger_abc import CustomFileLoggerABC
|
||||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
|
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
|
||||||
@ -40,6 +41,9 @@ class Startup(StartupABC):
|
|||||||
services.add_singleton(CustomFileLoggerABC, DatabaseLogger)
|
services.add_singleton(CustomFileLoggerABC, DatabaseLogger)
|
||||||
services.add_singleton(CustomFileLoggerABC, MessageLogger)
|
services.add_singleton(CustomFileLoggerABC, MessageLogger)
|
||||||
|
|
||||||
|
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module):
|
||||||
|
services.add_singleton(CustomFileLoggerABC, ApiLogger)
|
||||||
|
|
||||||
services.add_translation()
|
services.add_translation()
|
||||||
services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings))
|
services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings))
|
||||||
|
|
||||||
|
1
src/bot_api/__init__.py
Normal file
1
src/bot_api/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# imports:
|
52
src/bot_api/api.py
Normal file
52
src/bot_api/api.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import sys
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from cpl_core.dependency_injection import ServiceProviderABC
|
||||||
|
from flask import Flask, request
|
||||||
|
|
||||||
|
from bot_api.logging.api_logger import ApiLogger
|
||||||
|
from bot_api.route.route import Route
|
||||||
|
|
||||||
|
|
||||||
|
class Api(Flask):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
logger: ApiLogger,
|
||||||
|
services: ServiceProviderABC,
|
||||||
|
*args, **kwargs
|
||||||
|
):
|
||||||
|
if not args:
|
||||||
|
kwargs.setdefault('import_name', __name__)
|
||||||
|
|
||||||
|
Flask.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self._logger = logger
|
||||||
|
self._services = services
|
||||||
|
|
||||||
|
# register before request
|
||||||
|
self.before_request_funcs.setdefault(None, []).append(self.before_request)
|
||||||
|
|
||||||
|
def _register_routes(self):
|
||||||
|
for path, f in Route.registered_routes.items():
|
||||||
|
cls = None
|
||||||
|
qual_name_split = f.__qualname__.split('.')
|
||||||
|
if len(qual_name_split) > 0:
|
||||||
|
cls_type = vars(sys.modules[f.__module__])[qual_name_split[0]]
|
||||||
|
cls = self._services.get_service(cls_type)
|
||||||
|
|
||||||
|
partial_f = partial(f, self if cls is None else cls)
|
||||||
|
partial_f.__name__ = f.__name__
|
||||||
|
self.route(path)(partial_f)
|
||||||
|
|
||||||
|
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}')
|
||||||
|
self._logger.trace(__name__, f'Body: {request.get_json(force=True, silent=True)}')
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self._logger.info(__name__, f'Starting API')
|
||||||
|
self._register_routes()
|
||||||
|
from waitress import serve
|
||||||
|
serve(self, host="0.0.0.0", port=5000)
|
26
src/bot_api/api_module.py
Normal file
26
src/bot_api/api_module.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from cpl_core.configuration import ConfigurationABC
|
||||||
|
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||||
|
from cpl_core.environment import ApplicationEnvironmentABC
|
||||||
|
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from bot_api.api import Api
|
||||||
|
from bot_api.api_thread import ApiThread
|
||||||
|
from bot_api.controller.api_controller import ApiController
|
||||||
|
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):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||||
|
services.add_singleton(ApiThread)
|
||||||
|
services.add_singleton(Flask, Api)
|
||||||
|
|
||||||
|
services.add_transient(ApiController)
|
27
src/bot_api/api_thread.py
Normal file
27
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=not feature_flags.get_flag(FeatureFlagsEnum.api_only))
|
||||||
|
|
||||||
|
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)
|
48
src/bot_api/bot-api.json
Normal file
48
src/bot_api/bot-api.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"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-Classful==0.14.2"
|
||||||
|
],
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
}
|
0
src/bot_api/controller/__init__.py
Normal file
0
src/bot_api/controller/__init__.py
Normal file
22
src/bot_api/controller/api_controller.py
Normal file
22
src/bot_api/controller/api_controller.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from cpl_translation import TranslatePipe
|
||||||
|
|
||||||
|
from bot_api.api import Api
|
||||||
|
from bot_api.logging.api_logger import ApiLogger
|
||||||
|
from bot_api.route.route import Route
|
||||||
|
|
||||||
|
|
||||||
|
class ApiController:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
logger: ApiLogger,
|
||||||
|
t: TranslatePipe,
|
||||||
|
api: Api
|
||||||
|
):
|
||||||
|
self._logger = logger
|
||||||
|
self._t = t
|
||||||
|
self._api = api
|
||||||
|
|
||||||
|
@Route.route('/api/hello-world')
|
||||||
|
def hello_world(self):
|
||||||
|
return self._t.transform('common.hello_world')
|
0
src/bot_api/controller/api_route.py
Normal file
0
src/bot_api/controller/api_route.py
Normal file
0
src/bot_api/logging/__init__.py
Normal file
0
src/bot_api/logging/__init__.py
Normal file
11
src/bot_api/logging/api_logger.py
Normal file
11
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)
|
0
src/bot_api/route/__init__.py
Normal file
0
src/bot_api/route/__init__.py
Normal file
11
src/bot_api/route/route.py
Normal file
11
src/bot_api/route/route.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class Route:
|
||||||
|
registered_routes = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def route(cls, path=None):
|
||||||
|
# simple decorator for class based views
|
||||||
|
def inner(fn):
|
||||||
|
cls.registered_routes[path] = fn
|
||||||
|
return fn
|
||||||
|
|
||||||
|
return inner
|
@ -4,6 +4,7 @@ from enum import Enum
|
|||||||
class FeatureFlagsEnum(Enum):
|
class FeatureFlagsEnum(Enum):
|
||||||
|
|
||||||
# modules
|
# modules
|
||||||
|
api_module = 'ApiModule'
|
||||||
admin_module = 'AdminModule'
|
admin_module = 'AdminModule'
|
||||||
auto_role_module = 'AutoRoleModule'
|
auto_role_module = 'AutoRoleModule'
|
||||||
base_module = 'BaseModule'
|
base_module = 'BaseModule'
|
||||||
@ -15,4 +16,5 @@ class FeatureFlagsEnum(Enum):
|
|||||||
moderator_module = 'ModeratorModule'
|
moderator_module = 'ModeratorModule'
|
||||||
permission_module = 'PermissionModule'
|
permission_module = 'PermissionModule'
|
||||||
# features
|
# features
|
||||||
|
api_only = 'ApiOnly'
|
||||||
presence = 'Presence'
|
presence = 'Presence'
|
||||||
|
@ -14,6 +14,7 @@ class FeatureFlagsSettings(ConfigurationModelABC):
|
|||||||
|
|
||||||
self._flags = {
|
self._flags = {
|
||||||
# modules
|
# modules
|
||||||
|
FeatureFlagsEnum.api_module.value: False, # 13.10.2022 #70
|
||||||
FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48
|
FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48
|
||||||
FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54
|
FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54
|
||||||
FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48
|
FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48
|
||||||
@ -25,6 +26,7 @@ class FeatureFlagsSettings(ConfigurationModelABC):
|
|||||||
FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48
|
FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48
|
||||||
FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48
|
FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48
|
||||||
# features
|
# features
|
||||||
|
FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70
|
||||||
FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56
|
FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user