50 Commits

Author SHA1 Message Date
45a6b49cf5 Improved way to load and show server in sidebar #131 2023-02-11 10:34:46 +01:00
4822348e01 Improved way to load server for server dashboard #131 2023-02-11 10:34:35 +01:00
a6df06f13a Added logic to create default api-key for frontend #162-3 2023-02-09 22:09:02 +01:00
7f14aff1bd Added api-key commands #162-3 2023-02-09 20:22:46 +01:00
d03ea1d970 Added ApiKeys to db #162-3 2023-02-09 19:29:25 +01:00
f144564806 Secured graphql playground #162-2 2023-02-09 18:29:19 +01:00
f7c27b77ee Added graphql support #162-2 2023-02-09 16:36:50 +01:00
6aca981fd1 [WIP] Switched from rest to GraphQL #162-2 2023-02-09 16:09:34 +01:00
4266db7e35 [WIP] Updated packages #162-2 2023-02-09 15:27:39 +01:00
667b9fa87e [WIP] Switched to gql #162-2 2023-02-09 15:19:31 +01:00
79837afdfb Removed server route #162-2 2023-02-09 14:32:38 +01:00
8273b2b98e Build frontend & fixed oauth scopes #162 2023-02-09 14:30:51 +01:00
eaa22efd08 Merge branch '1.0.0' into #162 2023-02-08 19:26:21 +01:00
b33c5dc9bf Fixed mutations #162 2023-02-08 19:24:07 +01:00
e9559dec08 Fixed queries #162 2023-02-08 19:10:56 +01:00
5d470be583 Changed from snake to camel case #162 2023-02-08 19:05:32 +01:00
1dfe7edcba Added services to di #162 2023-02-08 17:50:43 +01:00
af8c2dea60 Added mutations #162 2023-02-08 17:48:54 +01:00
23e238b7d5 Removed server from queries where user set #162 2023-02-08 12:34:47 +01:00
fde318b85d Improved filters & queries #162 2023-02-08 12:32:16 +01:00
93c60b9176 Improved filters #162 2023-02-08 12:05:02 +01:00
12f8f669ed Improved filters #162 2023-02-07 19:09:27 +01:00
36fd3c73b9 Added client filter #162 2023-02-07 18:58:09 +01:00
3f7cfc47af Finished auto role filter #162 2023-02-07 18:53:28 +01:00
eb58c34c4d Added auto role filter #162 2023-02-07 17:52:38 +01:00
e3c0a0dea3 Reformatted files with black #162 2023-02-07 17:07:25 +01:00
dbfae8d8a4 Added more gql filters #162 2023-02-07 17:07:14 +01:00
16066864ed Improved queries & filtering #162 2023-01-16 19:18:23 +01:00
807827e30f Improved queries & filtering #162 2023-01-16 15:13:45 +01:00
c75cc54d16 Improved queries #162 2023-01-15 17:47:40 +01:00
6a1ad1ec9f Added filters #162 2023-01-15 17:06:04 +01:00
8256ebed71 Added auto role query #162 2023-01-15 16:24:16 +01:00
eb7fce140a Added more queries #162 2023-01-15 11:00:00 +01:00
ebcf876457 Added more queries #162 2023-01-15 10:59:54 +01:00
5efb1da0b8 Added more queries #162 2023-01-15 10:59:25 +01:00
b9e66bba9d Added more queries #162 2023-01-15 10:36:09 +01:00
b8484185e9 Improved level stuff #162 2023-01-15 09:51:40 +01:00
552e686aeb Updated model #162 2023-01-15 02:37:35 +01:00
efb772094b Updated cpl-core #162 2023-01-15 02:35:39 +01:00
95b9eea236 Added graphql from prototype #162 2023-01-15 02:28:28 +01:00
b95a951a1b Merge branch 'prototype/graphql/ariadne' into #162
# Conflicts:
#	kdb-bot/cpl-workspace.json
#	kdb-bot/src/bot/config
#	kdb-bot/src/bot/module_list.py
#	kdb-bot/src/bot_api/api.py
#	kdb-bot/src/bot_data/service/client_repository_service.py
#	kdb-bot/src/bot_data/service/server_repository_service.py
2023-01-15 02:25:05 +01:00
df42acec26 Added mutations without saving data 2023-01-07 16:31:28 +01:00
44204f5b94 Improved query & filtering 2023-01-07 15:53:53 +01:00
ce85bb332a Improved query & filtering 2023-01-07 15:53:44 +01:00
7a836a7f59 Added filtering 2023-01-07 15:17:11 +01:00
dacb429d9b Moved graphql stuff to own lib 2023-01-07 12:41:22 +01:00
77e18027a0 Improved ariadne with DI 2023-01-06 23:18:40 +01:00
b13695b018 Moved to ariadne 2023-01-06 16:21:21 +01:00
75500076a7 Improved gql data layer 2023-01-06 14:47:45 +01:00
5455a6b359 Added first graphene models 2023-01-06 14:14:37 +01:00
129 changed files with 12455 additions and 5367 deletions

View File

@@ -0,0 +1,34 @@
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class query(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
import textwrap
code = textwrap.dedent(
"""\
from bot_graphql.abc.data_query_abc import DataQueryABC
class $ClassName(DataQueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Name")
self.set_field("id", self.resolve_id)
@staticmethod
def resolve_id(x, *_):
return x.id
"""
)
return self.build_code_str(
code,
ClassName=self._class_name,
)
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "query", [])

View File

@@ -6,6 +6,7 @@
"bot-api": "src/bot_api/bot-api.json", "bot-api": "src/bot_api/bot-api.json",
"bot-core": "src/bot_core/bot-core.json", "bot-core": "src/bot_core/bot-core.json",
"bot-data": "src/bot_data/bot-data.json", "bot-data": "src/bot_data/bot-data.json",
"bot-graphql": "src/bot_graphql/bot-graphql.json",
"auto-role": "src/modules/auto_role/auto-role.json", "auto-role": "src/modules/auto_role/auto-role.json",
"base": "src/modules/base/base.json", "base": "src/modules/base/base.json",
"boot-log": "src/modules/boot_log/boot-log.json", "boot-log": "src/modules/boot_log/boot-log.json",

View File

@@ -16,7 +16,7 @@
"LicenseName": "MIT", "LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.", "LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [ "Dependencies": [
"cpl-core==2022.12.1.post2", "cpl-core==2022.12.1.post3",
"cpl-translation==2022.12.1", "cpl-translation==2022.12.1",
"cpl-query==2022.12.2.post1", "cpl-query==2022.12.2.post1",
"cpl-discord==2022.12.1.post2", "cpl-discord==2022.12.1.post2",
@@ -26,12 +26,13 @@
"PyJWT==2.6.0", "PyJWT==2.6.0",
"waitress==2.1.2", "waitress==2.1.2",
"Flask-SocketIO==5.3.2", "Flask-SocketIO==5.3.2",
"eventlet==0.33.2", "eventlet==0.33.3",
"requests-oauthlib==1.3.1", "requests-oauthlib==1.3.1",
"icmplib==3.0.3" "icmplib==3.0.3",
"ariadne==0.17.1"
], ],
"DevDependencies": [ "DevDependencies": [
"cpl-cli==2022.12.1.post2" "cpl-cli==2022.12.1.post3"
], ],
"PythonVersion": ">=3.10.4", "PythonVersion": ">=3.10.4",
"PythonPath": {}, "PythonPath": {},
@@ -55,6 +56,7 @@
"../bot_api/bot-api.json", "../bot_api/bot-api.json",
"../bot_core/bot-core.json", "../bot_core/bot-core.json",
"../bot_data/bot-data.json", "../bot_data/bot-data.json",
"../bot_graphql/bot-graphql.json",
"../modules/auto_role/auto-role.json", "../modules/auto_role/auto-role.json",
"../modules/base/base.json", "../modules/base/base.json",
"../modules/boot_log/boot-log.json", "../modules/boot_log/boot-log.json",

View File

@@ -4,6 +4,7 @@ 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
from bot_graphql.graphql_module import GraphQLModule
from modules.auto_role.auto_role_module import AutoRoleModule from modules.auto_role.auto_role_module import AutoRoleModule
from modules.base.base_module import BaseModule from modules.base.base_module import BaseModule
from modules.boot_log.boot_log_module import BootLogModule from modules.boot_log.boot_log_module import BootLogModule
@@ -23,6 +24,7 @@ class ModuleList:
[ [
CoreModule, # has to be first! CoreModule, # has to be first!
DataModule, DataModule,
GraphQLModule,
PermissionModule, PermissionModule,
DatabaseModule, DatabaseModule,
AutoRoleModule, AutoRoleModule,

View File

@@ -4,6 +4,7 @@ from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.environment import ApplicationEnvironmentABC
from bot_data.abc.migration_abc import MigrationABC from bot_data.abc.migration_abc import MigrationABC
from bot_data.migration.api_key_migration import ApiKeyMigration
from bot_data.migration.api_migration import ApiMigration from bot_data.migration.api_migration import ApiMigration
from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration
from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.auto_role_migration import AutoRoleMigration
@@ -32,3 +33,4 @@ class StartupMigrationExtension(StartupExtensionABC):
services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0 services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0
services.add_transient(MigrationABC, AutoRoleFix1Migration) # 30.12.2022 #151 - 0.3.0 services.add_transient(MigrationABC, AutoRoleFix1Migration) # 30.12.2022 #151 - 0.3.0
services.add_transient(MigrationABC, UserMessageCountPerHourMigration) # 11.01.2023 #168 - 0.3.1 services.add_transient(MigrationABC, UserMessageCountPerHourMigration) # 11.01.2023 #168 - 0.3.1
services.add_transient(MigrationABC, ApiKeyMigration) # 09.02.2023 #162 - 1.0.0

View File

@@ -283,7 +283,17 @@
"technician": { "technician": {
"restart_message": "Bin gleich wieder da :D", "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 :)", "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 :)",
"log_message": "Hier sind deine Logdateien! :)" "log_message": "Hier sind deine Logdateien! :)",
"api_key": {
"get": "API-Schlüssel für {}: {}",
"add": {
"success": "API-Schlüssel für {} wurde erstellt: {}"
},
"remove": {
"not_found": "API-Schlüssel konnte nicht gefunden werden!",
"success": "API-Schlüssel wurde entfernt :D"
}
}
} }
}, },
"api": { "api": {

View File

@@ -83,6 +83,10 @@ class AuthServiceABC(ABC):
async def verify_login(self, token_str: str) -> bool: async def verify_login(self, token_str: str) -> bool:
pass pass
@abstractmethod
async def verify_api_key(self, api_key: str) -> bool:
pass
@abstractmethod @abstractmethod
async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO:
pass pass

View File

@@ -1,4 +1,3 @@
import re
import sys import sys
import textwrap import textwrap
import uuid import uuid
@@ -16,7 +15,6 @@ from werkzeug.exceptions import NotFound
from bot_api.configuration.api_settings import ApiSettings from bot_api.configuration.api_settings import ApiSettings
from bot_api.configuration.authentication_settings import AuthenticationSettings 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_error_code_enum import ServiceErrorCode
from bot_api.exception.service_exception import ServiceException from bot_api.exception.service_exception import ServiceException
from bot_api.logging.api_logger import ApiLogger from bot_api.logging.api_logger import ApiLogger
@@ -30,7 +28,6 @@ class Api(Flask):
logger: ApiLogger, logger: ApiLogger,
services: ServiceProviderABC, services: ServiceProviderABC,
api_settings: ApiSettings, api_settings: ApiSettings,
frontend_settings: FrontendSettings,
auth_settings: AuthenticationSettings, auth_settings: AuthenticationSettings,
*args, *args,
**kwargs, **kwargs,
@@ -146,20 +143,13 @@ class Api(Flask):
return response return response
def start(self): def start(self):
self._logger.info( self._logger.info(__name__, f"Starting API {self._api_settings.host}:{self._api_settings.port}")
__name__,
f"Starting API {self._api_settings.host}:{self._api_settings.port}",
)
self._register_routes() self._register_routes()
self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key) self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key)
# from waitress import serve # from waitress import serve
# https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html # 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) # serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10)
wsgi.server( wsgi.server(eventlet.listen((self._api_settings.host, self._api_settings.port)), self, log_output=False)
eventlet.listen((self._api_settings.host, self._api_settings.port)),
self,
log_output=False,
)
def on_connect(self): def on_connect(self):
self._logger.info(__name__, f"Client connected") self._logger.info(__name__, f"Client connected")

View File

@@ -13,7 +13,7 @@ from bot_api.api import Api
from bot_api.api_thread import ApiThread from bot_api.api_thread import ApiThread
from bot_api.controller.auth_controller import AuthController from bot_api.controller.auth_controller import AuthController
from bot_api.controller.auth_discord_controller import AuthDiscordController from bot_api.controller.auth_discord_controller import AuthDiscordController
from bot_api.controller.discord.server_controller import ServerController from bot_api.controller.graphql_controller import GraphQLController
from bot_api.controller.gui_controller import GuiController from bot_api.controller.gui_controller import GuiController
from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent
from bot_api.service.auth_service import AuthService from bot_api.service.auth_service import AuthService
@@ -45,7 +45,7 @@ class ApiModule(ModuleABC):
services.add_transient(AuthDiscordController) services.add_transient(AuthDiscordController)
services.add_transient(GuiController) services.add_transient(GuiController)
services.add_transient(DiscordService) services.add_transient(DiscordService)
services.add_transient(ServerController) services.add_transient(GraphQLController)
# cpl-discord # cpl-discord
self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent) self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent)

View File

@@ -2,12 +2,9 @@ from cpl_core.application import ApplicationExtensionABC
from cpl_core.configuration import ConfigurationABC from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.dependency_injection import ServiceProviderABC
from bot_api.abc.auth_service_abc import AuthServiceABC
from bot_api.configuration.authentication_settings import AuthenticationSettings
from bot_api.route.route import Route from bot_api.route.route import Route
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
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
class AppApiExtension(ApplicationExtensionABC): class AppApiExtension(ApplicationExtensionABC):
@@ -19,7 +16,4 @@ class AppApiExtension(ApplicationExtensionABC):
if not feature_flags.get_flag(FeatureFlagsEnum.api_module): if not feature_flags.get_flag(FeatureFlagsEnum.api_module):
return return
auth_settings: AuthenticationSettings = config.get_configuration(AuthenticationSettings) Route.init_authorize()
auth_users: AuthUserRepositoryABC = services.get_service(AuthUserRepositoryABC)
auth: AuthServiceABC = services.get_service(AuthServiceABC)
Route.init_authorize(auth_users, auth)

View File

@@ -8,7 +8,7 @@ from cpl_core.utils import CredentialManager
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
from flask import jsonify, Response from flask import jsonify, Response
from flask import request, session from flask import request
from requests_oauthlib import OAuth2Session from requests_oauthlib import OAuth2Session
from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.abc.auth_service_abc import AuthServiceABC
@@ -16,10 +16,8 @@ from bot_api.api import Api
from bot_api.configuration.discord_authentication_settings import ( from bot_api.configuration.discord_authentication_settings import (
DiscordAuthenticationSettings, DiscordAuthenticationSettings,
) )
from bot_api.json_processor import JSONProcessor
from bot_api.logging.api_logger import ApiLogger from bot_api.logging.api_logger import ApiLogger
from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_dto import AuthUserDTO
from bot_api.model.o_auth_dto import OAuthDTO
from bot_api.route.route import Route from bot_api.route.route import Route
from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_role_enum import AuthRoleEnum
@@ -59,7 +57,7 @@ class AuthDiscordController:
self._bot.user.id, self._bot.user.id,
redirect_uri=self._auth_settings.redirect_url, redirect_uri=self._auth_settings.redirect_url,
state=request.args.get("state"), state=request.args.get("state"),
scope=self._auth_settings.scope, scope=self._auth_settings.scope.to_list(),
) )
token = discord.fetch_token( token = discord.fetch_token(
self._auth_settings.token_url, self._auth_settings.token_url,
@@ -74,7 +72,7 @@ class AuthDiscordController:
oauth = OAuth2Session( oauth = OAuth2Session(
self._bot.user.id, self._bot.user.id,
redirect_uri=self._auth_settings.redirect_url, redirect_uri=self._auth_settings.redirect_url,
scope=self._auth_settings.scope, scope=self._auth_settings.scope.to_list(),
) )
login_url, state = oauth.authorization_url(self._auth_settings.auth_url) login_url, state = oauth.authorization_url(self._auth_settings.auth_url)
return jsonify({"loginUrl": login_url}) return jsonify({"loginUrl": login_url})

View File

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

View File

@@ -1,67 +0,0 @@
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 Response, jsonify, request
from bot_api.api import Api
from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria
from bot_api.json_processor import JSONProcessor
from bot_api.logging.api_logger import ApiLogger
from bot_api.route.route import Route
from bot_api.service.discord_service import DiscordService
from bot_data.model.auth_role_enum import AuthRoleEnum
class ServerController:
BasePath = f"/api/discord/server"
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
logger: ApiLogger,
t: TranslatePipe,
api: Api,
mail_settings: EMailClientSettings,
mailer: EMailClientABC,
discord_service: DiscordService,
):
self._config = config
self._env = env
self._logger = logger
self._t = t
self._api = api
self._mail_settings = mail_settings
self._mailer = mailer
self._discord_service = discord_service
@Route.get(f"{BasePath}/get/servers")
@Route.authorize(role=AuthRoleEnum.admin)
async def get_all_servers(self) -> Response:
result = await self._discord_service.get_all_servers()
result = result.select(lambda x: x.to_dict()).to_list()
return jsonify(result)
@Route.get(f"{BasePath}/get/servers-by-user")
@Route.authorize
async def get_all_servers_by_user(self) -> Response:
result = await self._discord_service.get_all_servers_by_user()
result = result.select(lambda x: x.to_dict()).to_list()
return jsonify(result)
@Route.post(f"{BasePath}/get/filtered")
@Route.authorize
async def get_filtered_servers(self) -> Response:
dto: ServerSelectCriteria = JSONProcessor.process(
ServerSelectCriteria, request.get_json(force=True, silent=True)
)
result = await self._discord_service.get_filtered_servers_async(dto)
result.result = result.result.select(lambda x: x.to_dict()).to_list()
return jsonify(result.to_dict())
@Route.get(f"{BasePath}/get/<id>")
@Route.authorize
async def get_server_by_id(self, id: int) -> Response:
result = await self._discord_service.get_server_by_id_async(id).to_list()
return jsonify(result.to_dict())

View File

@@ -0,0 +1,44 @@
from ariadne import graphql_sync
from ariadne.constants import PLAYGROUND_HTML
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from flask import request, jsonify
from bot_api.logging.api_logger import ApiLogger
from bot_api.route.route import Route
from bot_graphql.schema import Schema
class GraphQLController:
BasePath = f"/api/graphql"
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
logger: ApiLogger,
schema: Schema,
):
self._config = config
self._env = env
self._logger = logger
self._schema = schema
@Route.get(f"{BasePath}/playground")
@Route.authorize(skip_in_dev=True)
async def playground(self):
if self._env.environment_name != "development":
return "", 403
return PLAYGROUND_HTML, 200
@Route.post(f"{BasePath}")
@Route.authorize(by_api_key=True)
async def graphql(self):
data = request.get_json()
# Note: Passing the request to the context is optional.
# In Flask, the current request is always accessible as flask.request
success, result = graphql_sync(self._schema.schema, data, context_value=request)
return jsonify(result), 200 if success else 400

View File

@@ -4,7 +4,6 @@ from werkzeug.exceptions import Unauthorized
class ServiceErrorCode(Enum): class ServiceErrorCode(Enum):
Unknown = 0 Unknown = 0
InvalidDependencies = 1 InvalidDependencies = 1

View File

@@ -2,6 +2,8 @@ import functools
from functools import wraps from functools import wraps
from typing import Optional, Callable from typing import Optional, Callable
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_core.environment import ApplicationEnvironmentABC
from flask import request, jsonify from flask import request, jsonify
from flask_cors import cross_origin from flask_cors import cross_origin
@@ -18,19 +20,42 @@ class Route:
_auth_users: Optional[AuthUserRepositoryABC] = None _auth_users: Optional[AuthUserRepositoryABC] = None
_auth: Optional[AuthServiceABC] = None _auth: Optional[AuthServiceABC] = None
_env = "production"
@classmethod @classmethod
def init_authorize(cls, auth_users: AuthUserRepositoryABC, auth: AuthServiceABC): @ServiceProviderABC.inject
def init_authorize(cls, env: ApplicationEnvironmentABC, auth_users: AuthUserRepositoryABC, auth: AuthServiceABC):
cls._auth_users = auth_users cls._auth_users = auth_users
cls._auth = auth cls._auth = auth
cls._env = env.environment_name
@classmethod @classmethod
def authorize(cls, f: Callable = None, role: AuthRoleEnum = None): def authorize(cls, f: Callable = None, role: AuthRoleEnum = None, skip_in_dev=False, by_api_key=False):
if f is None: if f is None:
return functools.partial(cls.authorize, role=role) return functools.partial(cls.authorize, role=role, skip_in_dev=skip_in_dev, by_api_key=by_api_key)
@wraps(f) @wraps(f)
async def decorator(*args, **kwargs): async def decorator(*args, **kwargs):
if skip_in_dev and cls._env == "development":
return await f(*args, **kwargs)
if "Authorization" not in request.headers and by_api_key and "API-Key" in request.headers:
valid = False
try:
valid = cls._auth.verify_api_key(request.headers["API-Key"])
except ServiceException as e:
error = ErrorDTO(e.error_code, e.message)
return jsonify(error.to_dict()), 403
except Exception as e:
return jsonify(e), 500
if not valid:
ex = ServiceException(ServiceErrorCode.Unauthorized, f"API-Key invalid")
error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401
return await f(*args, **kwargs)
token = None token = None
if "Authorization" in request.headers: if "Authorization" in request.headers:
bearer = request.headers.get("Authorization") bearer = request.headers.get("Authorization")

View File

@@ -31,9 +31,11 @@ from bot_api.model.reset_password_dto import ResetPasswordDTO
from bot_api.model.token_dto import TokenDTO from bot_api.model.token_dto import TokenDTO
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.api_key import ApiKey
from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_role_enum import AuthRoleEnum
from bot_data.model.auth_user import AuthUser from bot_data.model.auth_user import AuthUser
from bot_data.model.auth_user_users_relation import AuthUserUsersRelation from bot_data.model.auth_user_users_relation import AuthUserUsersRelation
@@ -49,9 +51,9 @@ class AuthService(AuthServiceABC):
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
db: DatabaseContextABC, db: DatabaseContextABC,
auth_users: AuthUserRepositoryABC, auth_users: AuthUserRepositoryABC,
api_keys: ApiKeyRepositoryABC,
users: UserRepositoryABC, users: UserRepositoryABC,
servers: ServerRepositoryABC, servers: ServerRepositoryABC,
# mailer: MailThread,
mailer: EMailClientABC, mailer: EMailClientABC,
t: TranslatePipe, t: TranslatePipe,
auth_settings: AuthenticationSettings, auth_settings: AuthenticationSettings,
@@ -64,6 +66,7 @@ class AuthService(AuthServiceABC):
self._bot = bot self._bot = bot
self._db = db self._db = db
self._auth_users = auth_users self._auth_users = auth_users
self._api_keys = api_keys
self._users = users self._users = users
self._servers = servers self._servers = servers
self._mailer = mailer self._mailer = mailer
@@ -82,6 +85,11 @@ class AuthService(AuthServiceABC):
return False return False
def _get_api_key_str(self, api_key: ApiKey) -> str:
return hashlib.sha256(
f"{api_key.identifier}:{api_key.key}+{self._auth_settings.secret_key}".encode("utf-8")
).hexdigest()
def generate_token(self, user: AuthUser) -> str: def generate_token(self, user: AuthUser) -> str:
token = jwt.encode( token = jwt.encode(
payload={ payload={
@@ -221,7 +229,12 @@ class AuthService(AuthServiceABC):
raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists") raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user = AUT.to_db(user_dto) user = AUT.to_db(user_dto)
if self._auth_users.get_all_auth_users().count() == 0: if (
self._auth_users.get_all_auth_users()
.where(lambda x: x.name != "internal" and x.email != "internal@localhost")
.count()
== 0
):
user.auth_role = AuthRoleEnum.admin user.auth_role = AuthRoleEnum.admin
user.password_salt = uuid.uuid4() user.password_salt = uuid.uuid4()
@@ -478,6 +491,18 @@ class AuthService(AuthServiceABC):
return True return True
def verify_api_key(self, api_key: str) -> bool:
try:
keys = self._api_keys.get_api_keys().select(self._get_api_key_str)
if not keys.contains(api_key):
raise ServiceException(ServiceErrorCode.InvalidData, "API-Key invalid")
except Exception as e:
self._logger.error(__name__, f"Token invalid", e)
return False
return True
async def login_async(self, user_dto: AuthUser) -> TokenDTO: async def login_async(self, user_dto: AuthUser) -> TokenDTO:
if user_dto is None: if user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, "User not set") raise ServiceException(ServiceErrorCode.InvalidData, "User not set")

View File

@@ -0,0 +1,35 @@
from abc import ABC, abstractmethod
from cpl_query.extension import List
from bot_data.model.api_key import ApiKey
class ApiKeyRepositoryABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod
def get_api_keys(self) -> List[ApiKey]:
pass
@abstractmethod
def get_api_key(self, identifier: str, key: str) -> ApiKey:
pass
@abstractmethod
def get_api_key_by_key(self, key: str) -> ApiKey:
pass
@abstractmethod
def add_api_key(self, api_key: ApiKey):
pass
@abstractmethod
def update_api_key(self, api_key: ApiKey):
pass
@abstractmethod
def delete_api_key(self, api_key: ApiKey):
pass

View File

@@ -2,6 +2,7 @@ from abc import ABC, abstractmethod
from typing import Optional from typing import Optional
from cpl_query.extension import List from cpl_query.extension import List
from bot_data.model.client import Client from bot_data.model.client import Client
@@ -22,6 +23,10 @@ class ClientRepositoryABC(ABC):
def get_client_by_discord_id(self, discord_id: int) -> Client: def get_client_by_discord_id(self, discord_id: int) -> Client:
pass pass
@abstractmethod
def get_clients_by_server_id(self, server_id: int) -> List[Client]:
pass
@abstractmethod @abstractmethod
def find_client_by_discord_id(self, discord_id: int) -> Optional[Client]: def find_client_by_discord_id(self, discord_id: int) -> Optional[Client]:
pass pass

View File

@@ -2,6 +2,7 @@ from abc import ABC, abstractmethod
from typing import Optional from typing import Optional
from cpl_query.extension import List from cpl_query.extension import List
from bot_data.model.user_joined_server import UserJoinedServer from bot_data.model.user_joined_server import UserJoinedServer
@@ -18,6 +19,10 @@ class UserJoinedServerRepositoryABC(ABC):
def get_user_joined_server_by_id(self, id: int) -> UserJoinedServer: def get_user_joined_server_by_id(self, id: int) -> UserJoinedServer:
pass pass
@abstractmethod
def get_user_joined_server_by_server_id(self, server_id: int) -> UserJoinedServer:
pass
@abstractmethod @abstractmethod
def get_user_joined_servers_by_user_id(self, user_id: int) -> list[UserJoinedServer]: def get_user_joined_servers_by_user_id(self, user_id: int) -> list[UserJoinedServer]:
pass pass

View File

@@ -27,6 +27,10 @@ class UserRepositoryABC(ABC):
def get_users_by_discord_id(self, discord_id: int) -> List[User]: def get_users_by_discord_id(self, discord_id: int) -> List[User]:
pass pass
@abstractmethod
def get_users_by_server_id(self, server_id: int) -> List[User]:
pass
@abstractmethod @abstractmethod
def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User:
pass pass

View File

@@ -5,6 +5,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC
@@ -20,6 +21,7 @@ from bot_data.abc.user_message_count_per_hour_repository_abc import (
UserMessageCountPerHourRepositoryABC, UserMessageCountPerHourRepositoryABC,
) )
from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.service.api_key_repository_service import ApiKeyRepositoryService
from bot_data.service.auth_user_repository_service import AuthUserRepositoryService from bot_data.service.auth_user_repository_service import AuthUserRepositoryService
from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService
from bot_data.service.client_repository_service import ClientRepositoryService from bot_data.service.client_repository_service import ClientRepositoryService
@@ -48,6 +50,7 @@ class DataModule(ModuleABC):
pass pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(ApiKeyRepositoryABC, ApiKeyRepositoryService)
services.add_transient(AuthUserRepositoryABC, AuthUserRepositoryService) services.add_transient(AuthUserRepositoryABC, AuthUserRepositoryService)
services.add_transient(ServerRepositoryABC, ServerRepositoryService) services.add_transient(ServerRepositoryABC, ServerRepositoryService)
services.add_transient(UserRepositoryABC, UserRepositoryService) services.add_transient(UserRepositoryABC, UserRepositoryService)

View File

@@ -6,7 +6,6 @@ from bot_core.logging.database_logger import DatabaseLogger
class DBContext(DatabaseContext): class DBContext(DatabaseContext):
def __init__(self, logger: DatabaseLogger): def __init__(self, logger: DatabaseLogger):
self._logger = logger self._logger = logger
DatabaseContext.__init__(self) DatabaseContext.__init__(self)

View File

@@ -0,0 +1,38 @@
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.migration_abc import MigrationABC
from bot_data.db_context import DBContext
class ApiKeyMigration(MigrationABC):
name = "1.0_ApiKeyMigration"
def __init__(self, logger: DatabaseLogger, db: DBContext):
MigrationABC.__init__(self)
self._logger = logger
self._db = db
self._cursor = db.cursor
def upgrade(self):
self._logger.debug(__name__, "Running upgrade")
self._cursor.execute(
str(
f"""
CREATE TABLE IF NOT EXISTS `ApiKeys` (
`Id` BIGINT NOT NULL AUTO_INCREMENT,
`Identifier` VARCHAR(255) NOT NULL,
`Key` VARCHAR(255) NOT NULL,
`CreatorId` BIGINT NULL,
`CreatedAt` DATETIME(6),
`LastModifiedAt` DATETIME(6),
PRIMARY KEY(`Id`),
FOREIGN KEY (`CreatorId`) REFERENCES `Users`(`UserId`),
CONSTRAINT UC_Identifier_Key UNIQUE (`Identifier`,`Key`),
CONSTRAINT UC_Key UNIQUE (`Key`)
);
"""
)
)
def downgrade(self):
self._cursor.execute("DROP TABLE `ApiKeys`;")

View File

@@ -4,7 +4,7 @@ from bot_data.db_context import DBContext
class AutoRoleFix1Migration(MigrationABC): class AutoRoleFix1Migration(MigrationABC):
name = "0.3.0_AutoRoleMigration" name = "0.3.0_AutoRoleFixMigration"
def __init__(self, logger: DatabaseLogger, db: DBContext): def __init__(self, logger: DatabaseLogger, db: DBContext):
MigrationABC.__init__(self) MigrationABC.__init__(self)

View File

@@ -0,0 +1,102 @@
from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC
from bot_data.model.user import User
class ApiKey(TableABC):
def __init__(
self,
identifier: str,
key: str,
creator: Optional[User],
created_at: datetime = None,
modified_at: datetime = None,
id=0,
):
self._id = id
self._identifier = identifier
self._key = key
self._creator = creator
TableABC.__init__(self)
self._created_at = created_at if created_at is not None else self._created_at
self._modified_at = modified_at if modified_at is not None else self._modified_at
@property
def identifier(self) -> str:
return self._identifier
@property
def key(self) -> str:
return self._key
@property
def creator(self) -> Optional[User]:
return self._creator
@staticmethod
def get_select_all_string() -> str:
return str(
f"""
SELECT * FROM `ApiKeys`;
"""
)
@staticmethod
def get_select_string(identifier: str, key: str) -> str:
return str(
f"""
SELECT * FROM `ApiKeys`
WHERE `Identifier` = '{identifier}'
AND `Key` = '{key}';
"""
)
@staticmethod
def get_select_by_key(key: str) -> str:
return str(
f"""
SELECT * FROM `ApiKeys`
WHERE `Key` = '{key}';
"""
)
@property
def insert_string(self) -> str:
return str(
f"""
INSERT INTO `ApiKeys` (
`Identifier`, `Key`, `CreatorId`, `CreatedAt`, `LastModifiedAt`
) VALUES (
'{self._identifier}',
'{self._key}',
{"NULL" if self._creator is None else self._creator.user_id},
'{self._created_at}',
'{self._modified_at}'
);
"""
)
@property
def udpate_string(self) -> str:
return str(
f"""
UPDATE `ApiKeys`
SET `Identifier` = '{self._identifier}',
`Key` = '{self._key}',
`LastModifiedAt` = '{self._modified_at}'
WHERE `Id` = {self._id};
"""
)
@property
def delete_string(self) -> str:
return str(
f"""
DELETE FROM `ApiKeys`
WHERE `Id` = {self._id};
"""
)

View File

@@ -2,6 +2,5 @@ from enum import Enum
class AuthRoleEnum(Enum): class AuthRoleEnum(Enum):
normal = 0 normal = 0
admin = 1 admin = 1

View File

@@ -3,11 +3,13 @@ from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from bot_data.model.server import Server
class AutoRole(TableABC): class AutoRole(TableABC):
def __init__( def __init__(
self, self,
server_id: int, server: Optional[Server],
dc_channel_id: int, dc_channel_id: int,
dc_message_id: int, dc_message_id: int,
created_at: datetime = None, created_at: datetime = None,
@@ -15,7 +17,7 @@ class AutoRole(TableABC):
id=0, id=0,
): ):
self._auto_role_id = id self._auto_role_id = id
self._server_id = server_id self._server = server
self._discord_channel_id = dc_channel_id self._discord_channel_id = dc_channel_id
self._discord_message_id = dc_message_id self._discord_message_id = dc_message_id
@@ -28,17 +30,25 @@ class AutoRole(TableABC):
return self._auto_role_id return self._auto_role_id
@property @property
def server_id(self) -> int: def server(self) -> Server:
return self._server_id return self._server
@property @property
def discord_channel_id(self) -> int: def discord_channel_id(self) -> int:
return self._discord_channel_id return self._discord_channel_id
@discord_channel_id.setter
def discord_channel_id(self, value: int):
self._discord_channel_id = value
@property @property
def discord_message_id(self) -> int: def discord_message_id(self) -> int:
return self._discord_message_id return self._discord_message_id
@discord_message_id.setter
def discord_message_id(self, value: int):
self._discord_message_id = value
@staticmethod @staticmethod
def get_select_all_string() -> str: def get_select_all_string() -> str:
return str( return str(
@@ -81,7 +91,7 @@ class AutoRole(TableABC):
INSERT INTO `AutoRoles` ( INSERT INTO `AutoRoles` (
`ServerId`, `DiscordChannelId`, `DiscordMessageId`, `CreatedAt`, `LastModifiedAt` `ServerId`, `DiscordChannelId`, `DiscordMessageId`, `CreatedAt`, `LastModifiedAt`
) VALUES ( ) VALUES (
{self._server_id}, {self._server.server_id},
{self._discord_channel_id}, {self._discord_channel_id},
{self._discord_message_id}, {self._discord_message_id},
'{self._created_at}', '{self._created_at}',
@@ -95,7 +105,7 @@ class AutoRole(TableABC):
return str( return str(
f""" f"""
UPDATE `AutoRoles` UPDATE `AutoRoles`
SET `ServerId` = {self._server_id}, SET `ServerId` = {self._server.server_id},
`DiscordChannelId` = {self._discord_channel_id}, `DiscordChannelId` = {self._discord_channel_id},
`DiscordMessageId` = {self._discord_message_id}, `DiscordMessageId` = {self._discord_message_id},
`LastModifiedAt` = '{self._modified_at}' `LastModifiedAt` = '{self._modified_at}'

View File

@@ -1,13 +1,14 @@
from datetime import datetime from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from bot_data.model.auto_role import AutoRole
class AutoRoleRule(TableABC): class AutoRoleRule(TableABC):
def __init__( def __init__(
self, self,
auto_role_id: int, auto_role: AutoRole,
discord_emoji_name: str, discord_emoji_name: str,
discord_role_id: int, discord_role_id: int,
created_at: datetime = None, created_at: datetime = None,
@@ -15,7 +16,7 @@ class AutoRoleRule(TableABC):
id=0, id=0,
): ):
self._auto_role_rule_id = id self._auto_role_rule_id = id
self._auto_role_id = auto_role_id self._auto_role = auto_role
self._discord_emoji_name = discord_emoji_name self._discord_emoji_name = discord_emoji_name
self._discord_role_id = discord_role_id self._discord_role_id = discord_role_id
@@ -28,17 +29,25 @@ class AutoRoleRule(TableABC):
return self._auto_role_rule_id return self._auto_role_rule_id
@property @property
def auto_role_id(self) -> int: def auto_role(self) -> AutoRole:
return self._auto_role_id return self._auto_role
@property @property
def emoji_name(self) -> str: def emoji_name(self) -> str:
return self._discord_emoji_name return self._discord_emoji_name
@emoji_name.setter
def emoji_name(self, value: str):
self._discord_emoji_name = value
@property @property
def role_id(self) -> int: def role_id(self) -> int:
return self._discord_role_id return self._discord_role_id
@role_id.setter
def role_id(self, value: int):
self._discord_role_id = value
@staticmethod @staticmethod
def get_select_all_string() -> str: def get_select_all_string() -> str:
return str( return str(
@@ -72,7 +81,7 @@ class AutoRoleRule(TableABC):
INSERT INTO `AutoRoleRules` ( INSERT INTO `AutoRoleRules` (
`AutoRoleId`, `DiscordEmojiName`, `DiscordRoleId`, `CreatedAt`, `LastModifiedAt` `AutoRoleId`, `DiscordEmojiName`, `DiscordRoleId`, `CreatedAt`, `LastModifiedAt`
) VALUES ( ) VALUES (
{self._auto_role_id}, {self._auto_role},
'{self._discord_emoji_name}', '{self._discord_emoji_name}',
{self._discord_role_id}, {self._discord_role_id},
'{self._created_at}', '{self._created_at}',
@@ -86,7 +95,7 @@ class AutoRoleRule(TableABC):
return str( return str(
f""" f"""
UPDATE `AutoRoleRules` UPDATE `AutoRoleRules`
SET `AutoRoleId` = {self._auto_role_id}, SET `AutoRoleId` = {self._auto_role},
`DiscordEmojiName` = {self._discord_emoji_name}, `DiscordEmojiName` = {self._discord_emoji_name},
`DiscordRoleId` = {self._discord_role_id}, `DiscordRoleId` = {self._discord_role_id},
`LastModifiedAt` = '{self._modified_at}' `LastModifiedAt` = '{self._modified_at}'

View File

@@ -1,8 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC
from bot_data.model.server import Server from cpl_core.database import TableABC
class KnownUser(TableABC): class KnownUser(TableABC):

View File

@@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from bot_data.model.server import Server from bot_data.model.server import Server
@@ -62,6 +63,15 @@ class User(TableABC):
""" """
) )
@staticmethod
def get_select_by_server_id_string(server_id: int) -> str:
return str(
f"""
SELECT * FROM `Users`
WHERE `ServerId` = {server_id};
"""
)
@staticmethod @staticmethod
def get_select_by_discord_id_string(id: int) -> str: def get_select_by_discord_id_string(id: int) -> str:
return str( return str(

View File

@@ -1,10 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from bot_data.model.user import User from bot_data.model.user import User
from bot_data.model.server import Server
class UserJoinedServer(TableABC): class UserJoinedServer(TableABC):
@@ -69,6 +67,15 @@ class UserJoinedServer(TableABC):
""" """
) )
@staticmethod
def get_select_by_server_id_string(id: int) -> str:
return str(
f"""
SELECT * FROM `UserJoinedServers`
WHERE `ServerId` = {id};
"""
)
@staticmethod @staticmethod
def get_select_by_user_id_string(id: int) -> str: def get_select_by_user_id_string(id: int) -> str:
return str( return str(

View File

@@ -0,0 +1,73 @@
from typing import Optional
from cpl_core.database.context import DatabaseContextABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.api_key import ApiKey
class ApiKeyRepositoryService(ApiKeyRepositoryABC):
def __init__(
self,
logger: DatabaseLogger,
db_context: DatabaseContextABC,
users: UserRepositoryABC,
):
self._logger = logger
self._context = db_context
self._users = users
ApiKeyRepositoryABC.__init__(self)
@staticmethod
def _get_value_from_result(value: any) -> Optional[any]:
if isinstance(value, str) and "NULL" in value:
return None
return value
def _api_key_from_result(self, sql_result: tuple) -> ApiKey:
creator = self._get_value_from_result(sql_result[3])
api_key = ApiKey(
self._get_value_from_result(sql_result[1]),
self._get_value_from_result(sql_result[2]),
None if creator is None else self._users.get_user_by_id(int(creator)),
self._get_value_from_result(sql_result[4]),
self._get_value_from_result(sql_result[5]),
id=self._get_value_from_result(sql_result[0]),
)
return api_key
def get_api_keys(self) -> List[ApiKey]:
api_keys = List(ApiKey)
self._logger.trace(__name__, f"Send SQL command: {ApiKey.get_select_all_string()}")
results = self._context.select(ApiKey.get_select_all_string())
for result in results:
api_keys.append(self._api_key_from_result(result))
return api_keys
def get_api_key(self, identifier: str, key: str) -> ApiKey:
self._logger.trace(__name__, f"Send SQL command: {ApiKey.get_select_string(identifier, key)}")
return self._api_key_from_result(self._context.select(ApiKey.get_select_string(identifier, key))[0])
def get_api_key_by_key(self, key: str) -> ApiKey:
self._logger.trace(__name__, f"Send SQL command: {ApiKey.get_select_by_key(key)}")
return self._api_key_from_result(self._context.select(ApiKey.get_select_by_key(key))[0])
def add_api_key(self, api_key: ApiKey):
self._logger.trace(__name__, f"Send SQL command: {api_key.insert_string}")
self._context.cursor.execute(api_key.insert_string)
def update_api_key(self, api_key: ApiKey):
self._logger.trace(__name__, f"Send SQL command: {api_key.udpate_string}")
self._context.cursor.execute(api_key.udpate_string)
def delete_api_key(self, api_key: ApiKey):
self._logger.trace(__name__, f"Send SQL command: {api_key.delete_string}")
self._context.cursor.execute(api_key.delete_string)

View File

@@ -5,14 +5,16 @@ from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role import AutoRole
from bot_data.model.auto_role_rule import AutoRoleRule from bot_data.model.auto_role_rule import AutoRoleRule
class AutoRoleRepositoryService(AutoRoleRepositoryABC): class AutoRoleRepositoryService(AutoRoleRepositoryABC):
def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC): def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC):
self._logger = logger self._logger = logger
self._context = db_context self._context = db_context
self._servers = servers
AutoRoleRepositoryABC.__init__(self) AutoRoleRepositoryABC.__init__(self)
@@ -21,14 +23,20 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_all_string()}") self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_all_string()}")
results = self._context.select(AutoRole.get_select_all_string()) results = self._context.select(AutoRole.get_select_all_string())
for result in results: for result in results:
auto_roles.append(AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0])) auto_roles.append(
AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
)
return auto_roles return auto_roles
def get_auto_role_by_id(self, id: int) -> AutoRole: def get_auto_role_by_id(self, id: int) -> AutoRole:
self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_id_string(id)}") self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_id_string(id)}")
result = self._context.select(AutoRole.get_select_by_id_string(id))[0] result = self._context.select(AutoRole.get_select_by_id_string(id))[0]
return AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0]) return AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
def find_auto_role_by_id(self, id: int) -> Optional[AutoRole]: def find_auto_role_by_id(self, id: int) -> Optional[AutoRole]:
self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_id_string(id)}") self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_id_string(id)}")
@@ -38,14 +46,20 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
result = result[0] result = result[0]
return AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0]) return AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
def get_auto_roles_by_server_id(self, id: int) -> List[AutoRole]: def get_auto_roles_by_server_id(self, id: int) -> List[AutoRole]:
self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_server_id_string(id)}") self._logger.trace(__name__, f"Send SQL command: {AutoRole.get_select_by_server_id_string(id)}")
auto_roles = List(AutoRole) auto_roles = List(AutoRole)
results = self._context.select(AutoRole.get_select_by_server_id_string(id)) results = self._context.select(AutoRole.get_select_by_server_id_string(id))
for result in results: for result in results:
auto_roles.append(AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0])) auto_roles.append(
AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
)
return auto_roles return auto_roles
@@ -55,7 +69,9 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
f"Send SQL command: {AutoRole.get_select_by_message_id_string(id)}", f"Send SQL command: {AutoRole.get_select_by_message_id_string(id)}",
) )
result = self._context.select(AutoRole.get_select_by_message_id_string(id))[0] result = self._context.select(AutoRole.get_select_by_message_id_string(id))[0]
return AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0]) return AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
def find_auto_role_by_message_id(self, id: int) -> Optional[AutoRole]: def find_auto_role_by_message_id(self, id: int) -> Optional[AutoRole]:
self._logger.trace( self._logger.trace(
@@ -68,7 +84,9 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
result = result[0] result = result[0]
return AutoRole(result[1], result[2], result[3], result[4], result[5], id=result[0]) return AutoRole(
self._servers.get_server_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
def add_auto_role(self, auto_role: AutoRole): def add_auto_role(self, auto_role: AutoRole):
self._logger.trace(__name__, f"Send SQL command: {auto_role.insert_string}") self._logger.trace(__name__, f"Send SQL command: {auto_role.insert_string}")
@@ -87,14 +105,20 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
self._logger.trace(__name__, f"Send SQL command: {AutoRoleRule.get_select_all_string()}") self._logger.trace(__name__, f"Send SQL command: {AutoRoleRule.get_select_all_string()}")
results = self._context.select(AutoRoleRule.get_select_all_string()) results = self._context.select(AutoRoleRule.get_select_all_string())
for result in results: for result in results:
auto_role_rules.append(AutoRoleRule(result[1], result[2], result[3], result[4], result[5], id=result[0])) auto_role_rules.append(
AutoRoleRule(
self.get_auto_role_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
)
return auto_role_rules return auto_role_rules
def get_auto_role_rule_by_id(self, id: int) -> AutoRoleRule: def get_auto_role_rule_by_id(self, id: int) -> AutoRoleRule:
self._logger.trace(__name__, f"Send SQL command: {AutoRoleRule.get_select_by_id_string(id)}") self._logger.trace(__name__, f"Send SQL command: {AutoRoleRule.get_select_by_id_string(id)}")
result = self._context.select(AutoRoleRule.get_select_by_id_string(id))[0] result = self._context.select(AutoRoleRule.get_select_by_id_string(id))[0]
return AutoRoleRule(result[1], result[2], result[3], result[4], result[5], id=result[0]) return AutoRoleRule(
self.get_auto_role_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
def get_auto_role_rules_by_auto_role_id(self, id: int) -> List[AutoRoleRule]: def get_auto_role_rules_by_auto_role_id(self, id: int) -> List[AutoRoleRule]:
auto_role_rules = List(AutoRoleRule) auto_role_rules = List(AutoRoleRule)
@@ -104,7 +128,11 @@ class AutoRoleRepositoryService(AutoRoleRepositoryABC):
) )
results = self._context.select(AutoRoleRule.get_select_by_auto_role_id_string(id)) results = self._context.select(AutoRoleRule.get_select_by_auto_role_id_string(id))
for result in results: for result in results:
auto_role_rules.append(AutoRoleRule(result[1], result[2], result[3], result[4], result[5], id=result[0])) auto_role_rules.append(
AutoRoleRule(
self.get_auto_role_by_id(result[1]), result[2], result[3], result[4], result[5], id=result[0]
)
)
return auto_role_rules return auto_role_rules

View File

@@ -38,6 +38,8 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )
) )
@@ -55,9 +57,33 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )
def get_clients_by_server_id(self, server_id: int) -> List[Client]:
clients = List(Client)
self._logger.trace(__name__, f"Send SQL command: {Client.get_select_by_server_id_string(server_id)}")
results = self._context.select(Client.get_select_by_server_id_string(server_id))
for result in results:
clients.append(
Client(
result[1],
result[2],
result[3],
result[4],
result[5],
result[6],
self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0],
)
)
return clients
def get_client_by_discord_id(self, discord_id: int) -> Client: def get_client_by_discord_id(self, discord_id: int) -> Client:
self._logger.trace( self._logger.trace(
__name__, __name__,
@@ -72,6 +98,8 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )
@@ -94,6 +122,8 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )
@@ -116,6 +146,8 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )
@@ -138,6 +170,8 @@ class ClientRepositoryService(ClientRepositoryABC):
result[5], result[5],
result[6], result[6],
self._servers.get_server_by_id(result[7]), self._servers.get_server_by_id(result[7]),
result[8],
result[9],
id=result[0], id=result[0],
) )

View File

@@ -1,6 +1,5 @@
from cpl_core.database.context import DatabaseContextABC from cpl_core.database.context import DatabaseContextABC
from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.dependency_injection import ServiceProviderABC
from cpl_query.extension import List
from bot_core.logging.database_logger import DatabaseLogger from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.data_seeder_abc import DataSeederABC from bot_data.abc.data_seeder_abc import DataSeederABC
@@ -18,12 +17,10 @@ class SeederService:
self._db = db self._db = db
self._seeder = List(type, DataSeederABC.__subclasses__())
async def seed(self): async def seed(self):
self._logger.info(__name__, f"Seed data") self._logger.info(__name__, f"Seed data")
for seeder in self._seeder: for seeder in self._services.get_services(list[DataSeederABC]):
seeder_as_service: DataSeederABC = self._services.get_service(seeder) seeder: DataSeederABC = seeder
self._logger.debug(__name__, f"Starting seeder {seeder.__name__}") self._logger.debug(__name__, f"Starting seeder {type(seeder).__name__}")
await seeder_as_service.seed() await seeder.seed()
self._db.save_changes() self._db.save_changes()

View File

@@ -22,7 +22,7 @@ class ServerRepositoryService(ServerRepositoryABC):
self._logger.trace(__name__, f"Send SQL command: {Server.get_select_all_string()}") self._logger.trace(__name__, f"Send SQL command: {Server.get_select_all_string()}")
results = self._context.select(Server.get_select_all_string()) results = self._context.select(Server.get_select_all_string())
for result in results: for result in results:
servers.append(Server(result[1], id=result[0])) servers.append(Server(result[1], result[2], result[3], id=result[0]))
return servers return servers
@@ -54,7 +54,7 @@ class ServerRepositoryService(ServerRepositoryABC):
def get_server_by_id(self, server_id: int) -> Server: def get_server_by_id(self, server_id: int) -> Server:
self._logger.trace(__name__, f"Send SQL command: {Server.get_select_by_id_string(server_id)}") self._logger.trace(__name__, f"Send SQL command: {Server.get_select_by_id_string(server_id)}")
result = self._context.select(Server.get_select_by_id_string(server_id))[0] result = self._context.select(Server.get_select_by_id_string(server_id))[0]
return Server(result[1], id=result[0]) return Server(result[1], result[2], result[3], id=result[0])
def get_server_by_discord_id(self, discord_id: int) -> Server: def get_server_by_discord_id(self, discord_id: int) -> Server:
self._logger.trace( self._logger.trace(
@@ -62,7 +62,7 @@ class ServerRepositoryService(ServerRepositoryABC):
f"Send SQL command: {Server.get_select_by_discord_id_string(discord_id)}", f"Send SQL command: {Server.get_select_by_discord_id_string(discord_id)}",
) )
result = self._context.select(Server.get_select_by_discord_id_string(discord_id))[0] result = self._context.select(Server.get_select_by_discord_id_string(discord_id))[0]
return Server(result[1], id=result[0]) return Server(result[1], result[2], result[3], id=result[0])
def find_server_by_discord_id(self, discord_id: int) -> Optional[Server]: def find_server_by_discord_id(self, discord_id: int) -> Optional[Server]:
self._logger.trace( self._logger.trace(

View File

@@ -57,6 +57,21 @@ class UserJoinedServerRepositoryService(UserJoinedServerRepositoryABC):
id=result[0], id=result[0],
) )
def get_user_joined_server_by_server_id(self, server_id: int) -> UserJoinedServer:
self._logger.trace(
__name__,
f"Send SQL command: {UserJoinedServer.get(id)}",
)
result = self._context.select(UserJoinedServer.get_select_by_id_string(id))[0]
return UserJoinedServer(
self._users.get_user_by_id(result[1]),
result[2],
result[3],
result[4],
result[5],
id=result[0],
)
def get_user_joined_servers_by_user_id(self, user_id: int) -> List[UserJoinedServer]: def get_user_joined_servers_by_user_id(self, user_id: int) -> List[UserJoinedServer]:
joins = List(UserJoinedServer) joins = List(UserJoinedServer)
self._logger.trace( self._logger.trace(

View File

@@ -85,6 +85,25 @@ class UserRepositoryService(UserRepositoryABC):
return users return users
def get_users_by_server_id(self, server_id: int) -> List[User]:
users = List(User)
self._logger.trace(
__name__,
f"Send SQL command: {User.get_select_by_server_id_string(server_id)}",
)
results = self._context.select(User.get_select_by_server_id_string(server_id))
for result in results:
users.append(
User(
result[1],
result[2],
self._servers.get_server_by_id(result[3]),
id=result[0],
)
)
return users
def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User:
self._logger.trace( self._logger.trace(
__name__, __name__,

View File

@@ -0,0 +1 @@
# imports

View File

View File

@@ -0,0 +1,19 @@
from cpl_core.database import TableABC
from bot_graphql.abc.query_abc import QueryABC
class DataQueryABC(QueryABC):
def __init__(self, name: str):
QueryABC.__init__(self, name)
self.set_field("createdAt", self.resolve_created_at)
self.set_field("modifiedAt", self.resolve_modified_at)
@staticmethod
def resolve_created_at(entry: TableABC, *_):
return entry.created_at
@staticmethod
def resolve_modified_at(entry: TableABC, *_):
return entry.modified_at

View File

@@ -0,0 +1,54 @@
import functools
from abc import ABC, abstractmethod
from inspect import signature, Parameter
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_query.extension import List
class FilterABC(ABC):
def __init__(self):
ABC.__init__(self)
@abstractmethod
def from_dict(self, values: dict):
pass
@abstractmethod
def filter(self, query: List, *args) -> List:
pass
@staticmethod
@ServiceProviderABC.inject
def get_filter(f, values: dict, services: ServiceProviderABC):
sig = signature(f)
for param in sig.parameters.items():
parameter = param[1]
if parameter.name == "self" or parameter.name == "cls" or parameter.annotation == Parameter.empty:
continue
if issubclass(parameter.annotation, FilterABC):
filter = services.get_service(parameter.annotation)
filter.from_dict(values)
return filter @ staticmethod
@staticmethod
@ServiceProviderABC.inject
def get_collection_filter(filter_type: type, values: dict, services: ServiceProviderABC):
filter: FilterABC = services.get_service(filter_type)
filter.from_dict(values)
return filter
@classmethod
def resolve_filter_annotation(cls, f=None):
if f is None:
return functools.partial(cls.resolve_filter_annotation)
@functools.wraps(f)
def decorator(*args, **kwargs):
if "filter" in kwargs:
kwargs["filter"] = cls.get_filter(f, kwargs["filter"])
return f(*args, **kwargs)
return decorator

View File

@@ -0,0 +1,50 @@
from typing import Callable
from ariadne import ObjectType
from cpl_query.extension import List
from bot_graphql.abc.filter_abc import FilterABC
from bot_graphql.filter.page import Page
from bot_graphql.filter.sort import Sort
class QueryABC(ObjectType):
__abstract__ = True
def __init__(self, name: str):
ObjectType.__init__(self, name)
def add_collection(self, name: str, get_collection: Callable, filter_type: type = None):
def wrapper(*args, **kwargs):
if filter_type is not None and "filter" in kwargs:
kwargs["filter"] = FilterABC.get_collection_filter(filter_type, kwargs["filter"])
else:
kwargs["filter"] = None
if "page" in kwargs:
page = Page()
page.from_dict(kwargs["page"])
kwargs["page"] = page
if "sort" in kwargs:
sort = Sort()
sort.from_dict(kwargs["sort"])
kwargs["sort"] = sort
return self._resolve_collection(get_collection(*args), *args, **kwargs)
self.set_field(f"{name}s", wrapper)
self.set_field(f"{name}Count", lambda *args: get_collection(*args).count())
# @FilterABC.resolve_filter_annotation
def _resolve_collection(self, collection: List, *_, filter: FilterABC = None, page: Page = None, sort: Sort = None):
if filter is not None:
return filter.filter(collection)
if page is not None:
return page.filter(collection)
if sort is not None:
return sort.filter(collection)
return collection

View File

@@ -0,0 +1,44 @@
{
"ProjectSettings": {
"Name": "bot-data",
"Version": {
"Major": "0",
"Minor": "1",
"Micro": "0"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
"Description": "Keksdose bot - graphql",
"LongDescription": "Discord bot for the Keksdose discord Server - graphql package",
"URL": "https://www.sh-edraft.de",
"CopyrightDate": "2023",
"CopyrightName": "sh-edraft.de",
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"cpl-core>=2022.12.1"
],
"DevDependencies": [
"cpl-cli>=2022.12.1"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {},
"Classifiers": []
},
"BuildSettings": {
"ProjectType": "library",
"SourcePath": "",
"OutputPath": "../../dist",
"Main": "",
"EntryPoint": "",
"IncludePackageData": false,
"Included": [],
"Excluded": [
"*/__pycache__",
"*/logs",
"*/tests"
],
"PackageData": {},
"ProjectReferences": []
}
}

View File

@@ -0,0 +1,63 @@
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_data.model.auto_role import AutoRole
from bot_graphql.abc.filter_abc import FilterABC
class AutoRoleFilter(FilterABC):
def __init__(
self,
bot: DiscordBotServiceABC,
):
FilterABC.__init__(self)
self._bot = bot
self._id = None
self._channel_id = None
self._channel_name = None
self._message_id = None
self._server = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "channelId" in values:
self._channel_id = int(values["channelId"])
if "channelName" in values:
self._channel_name = values["channelName"]
if "messageId" in values:
self._message_id = int(values["messageId"])
if "server" in values:
from bot_graphql.filter.server_filter import ServerFilter
server = ServerFilter()
server.from_dict(values["server"])
self._server = server
def filter(self, query: List[AutoRole]) -> List[AutoRole]:
if self._id is not None:
query = query.where(lambda x: x.auto_role_id == self._id)
if self._channel_id is not None:
query = query.where(lambda x: x.discord_channel_id == self._channel_id)
if self._channel_name is not None and self._channel_id is not None:
query = query.where(
lambda x: self._bot.get_channel(x.discord_channel_id).name == self._channel_name
or self._channel_name in self._bot.get_channel(x.discord_channel_id).name
)
if self._message_id is not None:
query = query.where(lambda x: x.discord_message_id == self._message_id)
if self._server is not None:
servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.server_id)
query = query.where(lambda x: x.server.server_id in servers)
return query

View File

@@ -0,0 +1,63 @@
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_data.model.auto_role_rule import AutoRoleRule
from bot_graphql.abc.filter_abc import FilterABC
class AutoRoleRuleFilter(FilterABC):
def __init__(self, services: ServiceProviderABC, bot: DiscordBotServiceABC):
FilterABC.__init__(self)
self._services = services
self._bot = bot
self._id = None
self._emoji_name = None
self._role_id = None
self._role_name = None
self._auto_role = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "emojiName" in values:
self._emoji_name = values["emojiName"]
if "roleId" in values:
self._role_id = int(values["roleId"])
if "roleName" in values:
self._role_name = values["roleName"]
if "autoRole" in values:
from bot_graphql.filter.auto_role_filter import AutoRoleFilter
self._auto_role: AutoRoleFilter = self._services.get_service(AutoRoleFilter)
self._auto_role.from_dict(values["auto_role"])
def filter(self, query: List[AutoRoleRule]) -> List[AutoRoleRule]:
if self._id is not None:
query = query.where(lambda x: x.auto_role_rule_id == self._id)
if self._emoji_name is not None:
query = query.where(lambda x: x.emoji_name == self._emoji_name)
if self._role_id is not None:
query = query.where(lambda x: x.role_id == self._role_id)
if self._role_name is not None and self._role_id is not None:
def get_role_name(x: AutoRoleRule):
guild = self._bot.get_guild(x.auto_role.server.discord_server_id)
name = guild.get_role(x.role_id).name
return name == self._role_name or self._role_name in name
query = query.where(get_role_name)
if self._auto_role is not None:
auto_roles = self._auto_role.filter(query.select(lambda x: x.auto_role)).select(lambda x: x.auto_role_id)
query = query.where(lambda x: x.auto_role.auto_role_id in auto_roles)
return query

View File

@@ -0,0 +1,47 @@
from cpl_query.extension import List
from bot_data.model.client import Client
from bot_graphql.abc.filter_abc import FilterABC
class ClientFilter(FilterABC):
def __init__(self):
FilterABC.__init__(self)
self._id = None
self._discord_id = None
self._name = None
self._server = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "discordId" in values:
self._id = int(values["discordId"])
if "name" in values:
self._name = values["name"]
if "server" in values:
from bot_graphql.filter.server_filter import ServerFilter
server = ServerFilter()
server.from_dict(values["server"])
self._server = server
def filter(self, query: List[Client]) -> List[Client]:
if self._id is not None:
query = query.where(lambda x: x.client_id == self._id)
if self._discord_id is not None:
query = query.where(lambda x: x.client_id == self._discord_id)
if self._name is not None:
query = query.where(lambda x: self._name.lower() == x.name.lower() or self._name.lower() in x.name.lower())
if self._server is not None:
servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.server_id)
query = query.where(lambda x: x.server.server_id in servers)
return query

View File

@@ -0,0 +1,42 @@
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_query.extension import List
from bot_data.model.level import Level
from bot_graphql.abc.filter_abc import FilterABC
class LevelFilter(FilterABC):
def __init__(self, services: ServiceProviderABC):
FilterABC.__init__(self)
self._services = services
self._id = None
self._name = None
self._server = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "name" in values:
self._name = values["name"]
if "server" in values:
from bot_graphql.filter.server_filter import ServerFilter
self._server: ServerFilter = self._services.get_service(LevelFilter)
self._server.from_dict(values["server"])
def filter(self, query: List[Level]) -> List[Level]:
if self._id is not None:
query = query.where(lambda x: x.id == self._id)
if self._name is not None:
query = query.where(lambda x: self._name.lower() == x.name.lower() or self._name.lower() in x.name.lower())
if self._server is not None:
servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.server_id)
query = query.where(lambda x: x.server.server_id in servers)
return query

View File

@@ -0,0 +1,25 @@
from cpl_query.extension import List
from bot_graphql.abc.filter_abc import FilterABC
class Page(FilterABC):
def __init__(self):
FilterABC.__init__(self)
self._page_index = None
self._page_size = None
def from_dict(self, values: dict):
if "pageIndex" in values:
self._page_index = int(values["pageIndex"])
if "pageSize" in values:
self._page_size = int(values["pageSize"])
def filter(self, query: List, *args) -> List:
if self._page_size is not None and self._page_index is not None:
skip = self._page_size * self._page_index
result = query.skip(skip).take(self._page_size)
return result
return query

View File

@@ -0,0 +1,46 @@
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.container import Guild
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_data.model.server import Server
from bot_graphql.abc.filter_abc import FilterABC
class ServerFilter(FilterABC):
def __init__(self):
FilterABC.__init__(self)
self._id = None
self._discord_id = None
self._name = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "discordId" in values:
self._discord_id = int(values["discordId"])
if "name" in values:
self._name = values["name"]
@ServiceProviderABC.inject
def filter(self, query: List[Server], bot: DiscordBotServiceABC) -> List[Server]:
if self._id is not None:
query = query.where(lambda x: x.server_id == self._id)
if self._discord_id is not None:
query = query.where(lambda x: x.discord_server_id == self._discord_id)
if self._name is not None:
def where_guild(x: Guild):
guild = bot.get_guild(x.discord_server_id)
return guild is not None and (
self._name.lower() == guild.name.lower() or self._name.lower() in guild.name.lower()
)
query = query.where(where_guild)
return query

View File

@@ -0,0 +1,20 @@
from cpl_query.extension import List
from bot_graphql.abc.filter_abc import FilterABC
class Sort(FilterABC):
def __init__(self):
FilterABC.__init__(self)
self._sort_direction = None
self._sort_column = None
def from_dict(self, values: dict):
if "sortDirection" in values:
self._sort_direction = values["sortDirection"]
if "sortColumn" in values:
self._sort_column = values["sortColumn"]
def filter(self, query: List, *args) -> List:
return query

View File

@@ -0,0 +1,89 @@
from typing import Optional
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_data.model.user import User
from bot_graphql.abc.filter_abc import FilterABC
from bot_graphql.filter.level_filter import LevelFilter
from modules.level.service.level_service import LevelService
class UserFilter(FilterABC):
def __init__(
self,
services: ServiceProviderABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsABC,
levels: LevelService,
):
FilterABC.__init__(self)
self._services = services
self._bot = bot
self._client_utils = client_utils
self._levels = levels
self._id = None
self._discord_id = None
self._name = None
self._xp = None
self._ontime = None
self._level: Optional[LevelFilter] = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "discordId" in values:
self._discord_id = int(values["discordId"])
if "name" in values:
self._name = values["name"]
if "xp" in values:
self._xp = int(values["xp"])
if "ontime" in values:
self._ontime = int(values["ontime"])
if "level" in values:
self._level: LevelFilter = self._services.get_service(LevelFilter)
self._level.from_dict(values["level"])
if "server" in values:
from bot_graphql.filter.server_filter import ServerFilter
self._server: ServerFilter = self._services.get_service(ServerFilter)
self._server.from_dict(values["server"])
def filter(self, query: List[User]) -> List[User]:
if self._id is not None:
query = query.where(lambda x: x.id == self._id)
if self._discord_id is not None:
query = query.where(lambda x: x.discord_id == self._discord_id)
if self._name is not None:
query = query.where(
lambda x: self._bot.get_user(x.discord_id).name == self._name
or self._name in self._bot.get_user(x.discord_id).name
)
if self._xp is not None:
query = query.where(lambda x: x.xp == self._xp)
if self._ontime is not None:
query = query.where(lambda x: self._client_utils.get_ontime_for_user(x) == self._ontime)
if self._level is not None:
levels = self._level.filter(query.select(lambda x: self._levels.get_level(x))).select(lambda x: x.id)
query = query.where(lambda x: self._levels.get_level(x).id in levels)
if self._server is not None:
servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.server_id)
query = query.where(lambda x: x.server.server_id in servers)
return query

View File

@@ -0,0 +1,55 @@
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_data.model.user_joined_server import UserJoinedServer
from bot_graphql.abc.filter_abc import FilterABC
class UserJoinedServerFilter(FilterABC):
def __init__(
self,
services: ServiceProviderABC,
bot: DiscordBotServiceABC,
):
FilterABC.__init__(self)
self._services = services
self._bot = bot
self._id = None
self._user = None
self._joined_on = None
self._leaved_on = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "user" in values:
from bot_graphql.filter.user_filter import UserFilter
self._user: UserFilter = self._services.get_service(UserFilter)
self._user.from_dict(values["user"])
if "joinedOn" in values:
self._joined_on = values["joinedOn"]
if "leavedOn" in values:
self._leaved_on = values["leavedOn"]
def filter(self, query: List[UserJoinedServer]) -> List[UserJoinedServer]:
if self._id is not None:
query = query.where(lambda x: x.id == self._id)
if self._user is not None:
users = self._user.filter(query.select(lambda x: x.user)).select(lambda x: x.user_id)
query = query.where(lambda x: x.user.user_id in users)
if self._joined_on is not None:
query = query.where(lambda x: x.joined_on == self._joined_on or self._joined_on in x.joined_on)
if self._leaved_on is not None:
query = query.where(lambda x: x.leaved_on == self._leaved_on or self._leaved_on in x.leaved_on)
return query

View File

@@ -0,0 +1,74 @@
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel
from bot_graphql.abc.filter_abc import FilterABC
class UserJoinedVoiceChannelFilter(FilterABC):
def __init__(
self,
services: ServiceProviderABC,
bot: DiscordBotServiceABC,
):
FilterABC.__init__(self)
self._services = services
self._bot = bot
self._id = None
self._channel_id = None
self._channel_name = None
self._user = None
self._joined_on = None
self._leaved_on = None
def from_dict(self, values: dict):
if "id" in values:
self._id = int(values["id"])
if "channelId" in values:
self._channel_id = int(values["channelId"])
if "channelName" in values:
self._channel_name = values["channelName"]
if "user" in values:
from bot_graphql.filter.user_filter import UserFilter
self._user: UserFilter = self._services.get_service(UserFilter)
self._user.from_dict(values["user"])
if "joinedOn" in values:
self._joined_on = values["joinedOn"]
if "leavedOn" in values:
self._leaved_on = values["leavedOn"]
def filter(self, query: List[UserJoinedVoiceChannel]) -> List[UserJoinedVoiceChannel]:
if self._id is not None:
query = query.where(lambda x: x.id == self._id)
if self._channel_id is not None:
query = query.where(lambda x: x.dc_channel_id == self._channel_id)
if self._channel_name is not None and self._channel_id is not None:
def get_channel_name(x: UserJoinedVoiceChannel):
name = self._bot.get_channel(x.dc_channel_id).name
return name == self._channel_name or self._channel_name in name
query = query.where(get_channel_name)
if self._user is not None:
users = self._user.filter(query.select(lambda x: x.user)).select(lambda x: x.user_id)
query = query.where(lambda x: x.user.user_id in users)
if self._joined_on is not None:
query = query.where(lambda x: x.joined_on == self._joined_on or self._joined_on in x.joined_on)
if self._leaved_on is not None:
query = query.where(lambda x: x.leaved_on == self._leaved_on or self._leaved_on in x.leaved_on)
return query

View File

@@ -0,0 +1,78 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.service.seeder_service import SeederService
from bot_graphql.abc.filter_abc import FilterABC
from bot_graphql.abc.query_abc import QueryABC
from bot_graphql.filter.auto_role_filter import AutoRoleFilter
from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter
from bot_graphql.filter.client_filter import ClientFilter
from bot_graphql.filter.level_filter import LevelFilter
from bot_graphql.filter.server_filter import ServerFilter
from bot_graphql.filter.user_filter import UserFilter
from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter
from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter
from bot_graphql.graphql_service import GraphQLService
from bot_graphql.mutation import Mutation
from bot_graphql.mutations.auto_role_mutation import AutoRoleMutation
from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation
from bot_graphql.mutations.level_mutation import LevelMutation
from bot_graphql.mutations.user_mutation import UserMutation
from bot_graphql.queries.auto_role_query import AutoRoleQuery
from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery
from bot_graphql.queries.client_query import ClientQuery
from bot_graphql.queries.known_user_query import KnownUserQuery
from bot_graphql.queries.level_query import LevelQuery
from bot_graphql.queries.server_query import ServerQuery
from bot_graphql.queries.user_joined_server_query import UserJoinedServerQuery
from bot_graphql.queries.user_joined_voice_channel_query import UserJoinedVoiceChannelQuery
from bot_graphql.queries.user_query import UserQuery
from bot_graphql.query import Query
from bot_graphql.schema import Schema
class GraphQLModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.data_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_singleton(Schema)
services.add_singleton(GraphQLService)
services.add_singleton(Query)
services.add_singleton(Mutation)
# queries
services.add_transient(QueryABC, AutoRoleQuery)
services.add_transient(QueryABC, AutoRoleRuleQuery)
services.add_transient(QueryABC, ClientQuery)
services.add_transient(QueryABC, KnownUserQuery)
services.add_transient(QueryABC, LevelQuery)
services.add_transient(QueryABC, ServerQuery)
services.add_transient(QueryABC, UserQuery)
services.add_transient(QueryABC, UserJoinedServerQuery)
services.add_transient(QueryABC, UserJoinedVoiceChannelQuery)
# filters
services.add_singleton(FilterABC, AutoRoleFilter)
services.add_singleton(FilterABC, AutoRoleRuleFilter)
services.add_singleton(FilterABC, ClientFilter)
services.add_singleton(FilterABC, LevelFilter)
services.add_singleton(FilterABC, ServerFilter)
services.add_singleton(FilterABC, UserFilter)
services.add_singleton(FilterABC, UserJoinedServerFilter)
services.add_singleton(FilterABC, UserJoinedVoiceChannelFilter)
# mutations
services.add_transient(QueryABC, AutoRoleMutation)
services.add_transient(QueryABC, AutoRoleRuleMutation)
services.add_transient(QueryABC, LevelMutation)
services.add_transient(QueryABC, UserMutation)
services.add_transient(SeederService)

View File

@@ -0,0 +1,6 @@
from bot_graphql.abc.query_abc import QueryABC
class GraphQLService:
def __init__(self, queries: list[QueryABC]):
self._queries = queries

View File

@@ -0,0 +1,35 @@
type AutoRole implements TableQuery {
id: ID
channelId: String
channelName: String
messageId: String
server: Server
autoRoleRuleCount: Int
autoRoleRules(filter: AutoRoleRuleFilter, page: Page, sort: Sort): [AutoRoleRule]
createdAt: String
modifiedAt: String
}
input AutoRoleFilter {
id: ID
channelId: String
channelName: String
messageId: String
server: ServerFilter
}
type AutoRoleMutation {
createAutoRole(input: AutoRoleInput!): AutoRole
updateAutoRole(input: AutoRoleInput!): AutoRole
deleteAutoRole(id: ID): AutoRole
}
input AutoRoleInput {
id: ID
channelId: String
messageId: String
serverId: ID
}

View File

@@ -0,0 +1,32 @@
type AutoRoleRule implements TableQuery {
id: ID
emojiName: String
roleId: String
roleName: String
autoRole: AutoRole
createdAt: String
modifiedAt: String
}
input AutoRoleRuleFilter {
id: ID
emojiName: String
roleId: String
roleName: String
autoRole: AutoRoleFilter
}
type AutoRoleRuleMutation {
createAutoRoleRule(input: AutoRoleRuleInput!): AutoRoleRule
updateAutoRoleRule(input: AutoRoleRuleInput!): AutoRoleRule
deleteAutoRoleRule(id: ID): AutoRoleRule
}
input AutoRoleRuleInput {
id: ID
emojiName: String
roleId: String
autoRoleId: ID
}

View File

@@ -0,0 +1,14 @@
interface TableQuery {
createdAt: String
modifiedAt: String
}
input Page {
pageIndex: Int
pageSize: Int
}
input Sort {
sortDirection: String
sortColumn: String
}

View File

@@ -0,0 +1,22 @@
type Client implements TableQuery {
id: ID
discordId: String
name: String
sentMessageCount: Int
receivedMessageCount: Int
deletedMessageCount: Int
receivedCommandCount: Int
movedUsersCount: Int
server: Server
createdAt: String
modifiedAt: String
}
input ClientFilter {
id: ID
discordId: String
name: String
server: ServerFilter
}

View File

@@ -0,0 +1,7 @@
type KnownUser implements TableQuery {
id: ID
discordId: String
createdAt: String
modifiedAt: String
}

View File

@@ -0,0 +1,33 @@
type Level implements TableQuery {
id: ID
name: String
color: String
minXp: Int
permissions: String
server: Server
createdAt: String
modifiedAt: String
}
input LevelFilter {
id: ID
name: String
server: ServerFilter
}
type LevelMutation {
createLevel(input: LevelInput!): Level
updateLevel(input: LevelInput!): Level
deleteLevel(id: ID): Level
}
input LevelInput {
id: ID
name: String
color: String
minXp: Int
permissions: String
serverId: ID
}

View File

@@ -0,0 +1,6 @@
type Mutation {
autoRole: AutoRoleMutation
autoRoleRule: AutoRoleRuleMutation
level: LevelMutation
user: UserMutation
}

View File

@@ -0,0 +1,28 @@
type Query {
autoRoleCount: Int
autoRoles(filter: AutoRoleFilter, page: Page, sort: Sort): [AutoRole]
autoRoleRuleCount: Int
autoRoleRules(filter: AutoRoleRuleFilter, page: Page, sort: Sort): [AutoRoleRule]
clientCount: Int
clients(filter: ClientFilter, page: Page, sort: Sort): [Client]
knownUserCount: Int
knownUsers: [KnownUser]
levelCount: Int
levels(filter: LevelFilter, page: Page, sort: Sort): [Level]
serverCount: Int
servers(filter: ServerFilter, page: Page, sort: Sort): [Server]
userJoinedServerCount: Int
userJoinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [User]
userJoinedVoiceChannelCount: Int
userJoinedVoiceChannels(filter: UserJoinedVoiceChannelFilter, page: Page, sort: Sort): [User]
userCount: Int
users(filter: UserFilter, page: Page, sort: Sort): [User]
}

View File

@@ -0,0 +1,27 @@
type Server implements TableQuery {
id: ID
discordId: String
name: String
iconURL: String
autoRoleCount: Int
autoRoles(filter: AutoRoleFilter, page: Page, sort: Sort): [AutoRole]
clientCount: Int
clients(filter: ClientFilter, page: Page, sort: Sort): [Client]
levelCount: Int
levels(filter: LevelFilter, page: Page, sort: Sort): [Level]
userCount: Int
users(filter: UserFilter, page: Page, sort: Sort): [User]
createdAt: String
modifiedAt: String
}
input ServerFilter {
id: ID
discordId: String
name: String
}

View File

@@ -0,0 +1,38 @@
type User implements TableQuery {
id: ID
discordId: String
name: String
xp: Int
ontime: Float
level: Level
joinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [UserJoinedServer]
joinedServerCount: Int
joinedVoiceChannels(filter: UserJoinedVoiceChannelFilter, page: Page, sort: Sort): [UserJoinedVoiceChannel]
joinedVoiceChannelCount: Int
server: Server
createdAt: String
modifiedAt: String
}
input UserFilter {
id: ID
discordId: String
name: String
xp: Int
ontime: Float
level: LevelFilter
server: ServerFilter
}
type UserMutation {
updateUser(input: UserInput!): User
}
input UserInput {
id: ID
xp: Int
}

View File

@@ -0,0 +1,16 @@
type UserJoinedServer implements TableQuery {
id: ID
user: User
joinedOn: String
leavedOn: String
createdAt: String
modifiedAt: String
}
input UserJoinedServerFilter {
id: ID
user: UserFilter
joinedOn: String
leavedOn: String
}

View File

@@ -0,0 +1,20 @@
type UserJoinedVoiceChannel implements TableQuery {
id: ID
channelId: String
channelName: String
user: User
joinedOn: String
leavedOn: String
createdAt: String
modifiedAt: String
}
input UserJoinedVoiceChannelFilter {
id: ID
channelId: String
channelName: String
user: UserFilter
joinedOn: String
leavedOn: String
}

View File

@@ -0,0 +1,39 @@
from ariadne import MutationType
from bot_graphql.mutations.auto_role_mutation import AutoRoleMutation
from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation
from bot_graphql.mutations.level_mutation import LevelMutation
from bot_graphql.mutations.user_mutation import UserMutation
class Mutation(MutationType):
def __init__(
self,
auto_role_mutation: AutoRoleMutation,
auto_role_rule_mutation: AutoRoleRuleMutation,
level_mutation: LevelMutation,
user_mutation: UserMutation,
):
MutationType.__init__(self)
self._auto_role_mutation = auto_role_mutation
self._auto_role_rule_mutation = auto_role_rule_mutation
self._level_mutation = level_mutation
self._user_mutation = user_mutation
self.set_field("autoRole", self.resolve_auto_role)
self.set_field("autoRoleRule", self.resolve_auto_role_rule)
self.set_field("level", self.resolve_level)
self.set_field("user", self.resolve_user)
def resolve_auto_role(self, *_):
return self._auto_role_mutation
def resolve_auto_role_rule(self, *_):
return self._auto_role_rule_mutation
def resolve_level(self, *_):
return self._level_mutation
def resolve_user(self, *_):
return self._user_mutation

View File

@@ -0,0 +1,55 @@
from cpl_core.database.context import DatabaseContextABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_graphql.abc.query_abc import QueryABC
class AutoRoleMutation(QueryABC):
def __init__(
self,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
db: DatabaseContextABC,
):
QueryABC.__init__(self, "AutoRoleMutation")
self._servers = servers
self._auto_roles = auto_roles
self._db = db
self.set_field("createAutoRole", self.resolve_create_auto_role)
self.set_field("updateAutoRole", self.resolve_update_auto_role)
self.set_field("deleteAutoRole", self.resolve_delete_auto_role)
def resolve_create_auto_role(self, *_, input: dict):
auto_role = AutoRole(self._servers.get_server_by_id(input["serverId"]), input["channelId"], input["messageId"])
self._auto_roles.add_auto_role(auto_role)
self._db.save_changes()
def get_new(x: AutoRole):
return (
x.server.server_id == input["serverId"]
and x.discord_channel_id == input["channelId"]
and x.discord_message_id == input["messageId"]
)
return self._auto_roles.get_auto_roles_by_server_id(auto_role.server.server_id).where(get_new).last()
def resolve_update_auto_role(self, *_, input: dict):
auto_role = self._auto_roles.get_auto_role_by_id(input["id"])
auto_role.discord_channel_id = input["channelId"] if "channelId" in input else auto_role.discord_channel_id
auto_role.discord_message_id = input["messageId"] if "messageId" in input else auto_role.discord_message_id
self._auto_roles.update_auto_role(auto_role)
self._db.save_changes()
auto_role = self._auto_roles.get_auto_role_by_id(input["id"])
return auto_role
def resolve_delete_auto_role(self, *_, id: int):
auto_role = self._auto_roles.get_auto_role_by_id(id)
self._auto_roles.delete_auto_role(auto_role)
self._db.save_changes()
return auto_role

View File

@@ -0,0 +1,61 @@
from cpl_core.database.context import DatabaseContextABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role_rule import AutoRoleRule
from bot_graphql.abc.query_abc import QueryABC
class AutoRoleRuleMutation(QueryABC):
def __init__(
self,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
db: DatabaseContextABC,
):
QueryABC.__init__(self, "AutoRoleRuleMutation")
self._servers = servers
self._auto_roles = auto_roles
self._db = db
self.set_field("createAutoRoleRule", self.resolve_create_auto_role_rule)
self.set_field("updateAutoRoleRule", self.resolve_update_auto_role_rule)
self.set_field("deleteAutoRoleRule", self.resolve_delete_auto_role_rule)
def resolve_create_auto_role_rule(self, *_, input: dict):
auto_role_rule = AutoRoleRule(
self._auto_roles.get_auto_role_by_id(input["autoRoleId"]), input["emojiName"], input["roleId"]
)
self._auto_roles.add_auto_role_rule(auto_role_rule)
self._db.save_changes()
def get_new(x: AutoRoleRule):
return (
x.auto_role.auto_role_id == input["autoRoleId"]
and x.emoji_name == input["emojiName"]
and x.role_id == input["roleId"]
)
return (
self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role_rule.auto_role.auto_role_id)
.where(get_new)
.last()
)
def resolve_update_auto_role_rule(self, *_, input: dict):
auto_role_rule = self._auto_roles.get_auto_role_rule_by_id(input["id"])
auto_role_rule.emoji_name = input["emojiName"] if "emojiName" in input else auto_role_rule.emoji_name
auto_role_rule.role_id = input["roleId"] if "roleId" in input else auto_role_rule.role_id
self._auto_roles.update_auto_role_rule(auto_role_rule)
self._db.save_changes()
auto_role_rule = self._auto_roles.get_auto_role_rule_by_id(input["id"])
return auto_role_rule
def resolve_delete_auto_role_rule(self, *_, id: int):
auto_role_rule = self._auto_roles.get_auto_role_rule_by_id(id)
self._auto_roles.delete_auto_role_rule(auto_role_rule)
self._db.save_changes()
return auto_role_rule

View File

@@ -0,0 +1,64 @@
from cpl_core.database.context import DatabaseContextABC
from bot_data.abc.level_repository_abc import LevelRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.level import Level
from bot_graphql.abc.query_abc import QueryABC
class LevelMutation(QueryABC):
def __init__(
self,
servers: ServerRepositoryABC,
levels: LevelRepositoryABC,
db: DatabaseContextABC,
):
QueryABC.__init__(self, "LevelMutation")
self._servers = servers
self._levels = levels
self._db = db
self.set_field("createLevel", self.resolve_create_level)
self.set_field("updateLevel", self.resolve_update_level)
self.set_field("deleteLevel", self.resolve_delete_level)
def resolve_create_level(self, *_, input: dict):
level = Level(
input["name"],
input["color"],
int(input["minXp"]),
int(input["permissions"]),
self._servers.get_server_by_id(input["serverId"]),
)
self._levels.add_level(level)
self._db.save_changes()
def get_new_level(l: Level):
return (
l.name == level.name
and l.color == level.color
and l.min_xp == level.min_xp
and l.permissions == level.permissions
)
return self._levels.get_levels_by_server_id(level.server.server_id).where(get_new_level).last()
def resolve_update_level(self, *_, input: dict):
level = self._levels.get_level_by_id(input["id"])
level.name = input["name"] if "name" in input else level.name
level.color = input["color"] if "color" in input else level.color
level.min_xp = input["minXp"] if "minXp" in input else level.min_xp
level.permissions = input["permissions"] if "permissions" in input else level.permissions
self._levels.update_level(level)
self._db.save_changes()
level = self._levels.get_level_by_id(input["id"])
return level
def resolve_delete_level(self, *_, id: int):
level = self._levels.get_level_by_id(id)
self._levels.delete_level(level)
self._db.save_changes()
return level

View File

@@ -0,0 +1,31 @@
from cpl_core.database.context import DatabaseContextABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_graphql.abc.query_abc import QueryABC
class UserMutation(QueryABC):
def __init__(
self,
servers: ServerRepositoryABC,
users: UserRepositoryABC,
db: DatabaseContextABC,
):
QueryABC.__init__(self, "UserMutation")
self._servers = servers
self._users = users
self._db = db
self.set_field("updateUser", self.resolve_update_user)
def resolve_update_user(self, *_, input: dict):
user = self._users.get_user_by_id(input["id"])
user.xp = input["xp"] if "xp" in input else user.xp
self._users.update_user(user)
self._db.save_changes()
user = self._users.get_user_by_id(input["id"])
return user

View File

@@ -0,0 +1,52 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.filter.server_filter import ServerFilter
class AutoRoleQuery(DataQueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
auto_role_rules: AutoRoleRepositoryABC,
servers: ServerRepositoryABC,
):
DataQueryABC.__init__(self, "AutoRole")
self._bot = bot
self._auto_role_rules = auto_role_rules
self._servers = servers
self.set_field("id", self.resolve_id)
self.set_field("channelId", self.resolve_channel_id)
self.set_field("channelName", self.resolve_channel_name)
self.set_field("messageId", self.resolve_message_id)
self.set_field("server", self.resolve_server)
self.add_collection(
"autoRoleRule", lambda x, *_: self._auto_role_rules.get_auto_role_rules_by_auto_role_id(x.auto_role_id)
)
@staticmethod
def resolve_id(x: AutoRole, *_):
return x.auto_role_id
@staticmethod
def resolve_channel_id(x: AutoRole, *_):
return x.discord_channel_id
def resolve_channel_name(self, x: AutoRole, *_):
channel = self._bot.get_channel(x.discord_channel_id)
return None if channel is None else channel.name
@staticmethod
def resolve_message_id(x: AutoRole, *_):
return x.discord_message_id
def resolve_server(self, x: AutoRole, *_, filter: ServerFilter = None):
if filter is not None:
return filter.filter(self._servers.get_server_by_id(x.server.server_id))
return self._servers.get_server_by_id(x.server.server_id)

View File

@@ -0,0 +1,42 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.model.auto_role_rule import AutoRoleRule
from bot_graphql.abc.data_query_abc import DataQueryABC
class AutoRoleRuleQuery(DataQueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
auto_roles: AutoRoleRepositoryABC,
):
DataQueryABC.__init__(self, "AutoRoleRule")
self._bot = bot
self._auto_roles = auto_roles
self.set_field("id", self.resolve_id)
self.set_field("emojiName", self.resolve_emoji_name)
self.set_field("roleId", self.resolve_role_id)
self.set_field("roleName", self.resolve_role_name)
self.set_field("autoRole", self.resolve_auto_role)
@staticmethod
def resolve_id(x: AutoRoleRule, *_):
return x.auto_role_rule_id
@staticmethod
def resolve_emoji_name(x: AutoRoleRule, *_):
return x.emoji_name
@staticmethod
def resolve_role_id(x: AutoRoleRule, *_):
return x.role_id
def resolve_role_name(self, x: AutoRoleRule, *_):
guild = self._bot.get_guild(x.auto_role.server.discord_server_id)
return guild.get_role(x.role_id).name
def resolve_auto_role(self, x: AutoRoleRule, *_):
return self._auto_roles.get_auto_role_by_id(x.auto_role.auto_role_id)

View File

@@ -0,0 +1,59 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.client import Client
from bot_graphql.abc.data_query_abc import DataQueryABC
class ClientQuery(DataQueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
):
DataQueryABC.__init__(self, "Client")
self._bot = bot
self.set_field("id", self.resolve_id)
self.set_field("discordId", self.resolve_discord_id)
self.set_field("name", self.resolve_name)
self.set_field("sentMessageCount", self.resolve_sent_message_count)
self.set_field("receivedMessageCount", self.resolve_received_message_count)
self.set_field("deletedMessageCount", self.resolve_deleted_message_count)
self.set_field("receivedCommandCount", self.resolve_received_command_count)
self.set_field("movedUsersCount", self.resolve_moved_users_count)
self.set_field("server", self.resolve_server)
@staticmethod
def resolve_id(client: Client, *_):
return client.client_id
@staticmethod
def resolve_discord_id(client: Client, *_):
return client.discord_id
def resolve_name(self, client: Client, *_):
return self._bot.user.name
@staticmethod
def resolve_sent_message_count(client: Client, *_):
return client.sent_message_count
@staticmethod
def resolve_received_message_count(client: Client, *_):
return client.received_command_count
@staticmethod
def resolve_deleted_message_count(client: Client, *_):
return client.deleted_message_count
@staticmethod
def resolve_received_command_count(client: Client, *_):
return client.received_command_count
@staticmethod
def resolve_moved_users_count(client: Client, *_):
return client.moved_users_count
@staticmethod
def resolve_server(client: Client, *_):
return client.server

View File

@@ -0,0 +1,18 @@
from bot_data.model.known_user import KnownUser
from bot_graphql.abc.data_query_abc import DataQueryABC
class KnownUserQuery(DataQueryABC):
def __init__(self):
DataQueryABC.__init__(self, "KnownUser")
self.set_field("id", self.resolve_id)
self.set_field("discordId", self.resolve_discord_id)
@staticmethod
def resolve_id(x: KnownUser, *_):
return x.known_user_id
@staticmethod
def resolve_discord_id(x: KnownUser, *_):
return x.discord_id

View File

@@ -0,0 +1,38 @@
from bot_data.model.level import Level
from bot_graphql.abc.data_query_abc import DataQueryABC
class LevelQuery(DataQueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Level")
self.set_field("id", self.resolve_id)
self.set_field("name", self.resolve_name)
self.set_field("color", self.resolve_color)
self.set_field("minXp", self.resolve_min_xp)
self.set_field("permissions", self.resolve_permissions)
self.set_field("server", self.resolve_server)
@staticmethod
def resolve_id(level: Level, *_):
return level.id
@staticmethod
def resolve_name(level: Level, *_):
return level.name
@staticmethod
def resolve_color(level: Level, *_):
return level.color
@staticmethod
def resolve_min_xp(level: Level, *_):
return level.min_xp
@staticmethod
def resolve_permissions(level: Level, *_):
return level.permissions
@staticmethod
def resolve_server(level: Level, *_):
return level.server

View File

@@ -0,0 +1,61 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.level_repository_abc import LevelRepositoryABC
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.server import Server
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.filter.user_filter import UserFilter
class ServerQuery(DataQueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
auto_roles: AutoRoleRepositoryABC,
clients: ClientRepositoryABC,
levels: LevelRepositoryABC,
users: UserRepositoryABC,
ujs: UserJoinedServerRepositoryABC,
ujvs: UserJoinedVoiceChannelRepositoryABC,
):
DataQueryABC.__init__(self, "Server")
self._bot = bot
self._auto_roles = auto_roles
self._clients = clients
self._levels = levels
self._users = users
self._ujs = ujs
self._ujvs = ujvs
self.set_field("id", self.resolve_id)
self.set_field("discordId", self.resolve_discord_id)
self.set_field("name", self.resolve_name)
self.set_field("iconURL", self.resolve_icon_url)
self.add_collection(
"autoRole", lambda server, *_: self._auto_roles.get_auto_roles_by_server_id(server.server_id)
)
self.add_collection("client", lambda server, *_: self._clients.get_clients_by_server_id(server.server_id))
self.add_collection("level", lambda server, *_: self._levels.get_levels_by_server_id(server.server_id))
self.add_collection("user", lambda server, *_: self._users.get_users_by_server_id(server.server_id), UserFilter)
@staticmethod
def resolve_id(server: Server, *_):
return server.server_id
@staticmethod
def resolve_discord_id(server: Server, *_):
return server.discord_server_id
def resolve_name(self, server: Server, *_):
guild = self._bot.get_guild(server.discord_server_id)
return None if guild is None else guild.name
def resolve_icon_url(self, server: Server, *_):
guild = self._bot.get_guild(server.discord_server_id)
return None if guild is None else guild.icon.url

View File

@@ -0,0 +1,28 @@
from bot_data.model.user_joined_server import UserJoinedServer
from bot_graphql.abc.data_query_abc import DataQueryABC
class UserJoinedServerQuery(DataQueryABC):
def __init__(self):
DataQueryABC.__init__(self, "UserJoinedServer")
self.set_field("id", self.resolve_id)
self.set_field("user", self.resolve_user)
self.set_field("joinedOn", self.resolve_joined_on)
self.set_field("leavedOn", self.resolve_leaved_on)
@staticmethod
def resolve_id(x: UserJoinedServer, *_):
return x.join_id
@staticmethod
def resolve_user(x: UserJoinedServer, *_):
return x.user
@staticmethod
def resolve_joined_on(x: UserJoinedServer, *_):
return x.joined_on
@staticmethod
def resolve_leaved_on(x: UserJoinedServer, *_):
return x.leaved_on

View File

@@ -0,0 +1,41 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel
from bot_graphql.abc.data_query_abc import DataQueryABC
class UserJoinedVoiceChannelQuery(DataQueryABC):
def __init__(self, bot: DiscordBotServiceABC):
DataQueryABC.__init__(self, "UserJoinedVoiceChannel")
self._bot = bot
self.set_field("id", self.resolve_id)
self.set_field("channelId", self.resolve_channel_id)
self.set_field("channelName", self.resolve_channel_name)
self.set_field("user", self.resolve_user)
self.set_field("joinedOn", self.resolve_joined_on)
self.set_field("leavedOn", self.resolve_leaved_on)
@staticmethod
def resolve_id(x: UserJoinedVoiceChannel, *_):
return x.join_id
@staticmethod
def resolve_channel_id(x: UserJoinedVoiceChannel, *_):
return x.dc_channel_id
def resolve_channel_name(self, x: UserJoinedVoiceChannel, *_):
return self._bot.get_channel(x.dc_channel_id).name
@staticmethod
def resolve_user(x: UserJoinedVoiceChannel, *_):
return x.user
@staticmethod
def resolve_joined_on(x: UserJoinedVoiceChannel, *_):
return x.joined_on
@staticmethod
def resolve_leaved_on(x: UserJoinedVoiceChannel, *_):
return x.leaved_on

View File

@@ -0,0 +1,65 @@
from cpl_discord.service import DiscordBotServiceABC
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.model.user import User
from bot_graphql.abc.data_query_abc import DataQueryABC
from modules.level.service.level_service import LevelService
class UserQuery(DataQueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
levels: LevelService,
client_utils: ClientUtilsABC,
ujs: UserJoinedServerRepositoryABC,
ujvs: UserJoinedVoiceChannelRepositoryABC,
):
DataQueryABC.__init__(self, "User")
self._bot = bot
self._levels = levels
self._client_utils = client_utils
self._ujs = ujs
self._ujvs = ujvs
self.set_field("id", self.resolve_id)
self.set_field("discordId", self.resolve_discord_id)
self.set_field("name", self.resolve_name)
self.set_field("xp", self.resolve_xp)
self.set_field("ontime", self.resolve_ontime)
self.set_field("level", self.resolve_level)
self.add_collection("joinedServer", lambda user, *_: self._ujs.get_user_joined_servers_by_user_id(user.user_id))
self.add_collection(
"joinedVoiceChannel", lambda user, *_: self._ujvs.get_user_joined_voice_channels_by_user_id(user.user_id)
)
self.set_field("server", self.resolve_server)
@staticmethod
def resolve_id(user: User, *_):
return user.user_id
@staticmethod
def resolve_discord_id(user: User, *_):
return user.discord_id
def resolve_name(self, user: User, *_):
guild = self._bot.get_guild(user.server.discord_server_id)
user = guild.get_member(user.discord_id)
return None if user is None else user.name
@staticmethod
def resolve_xp(user: User, *_):
return user.xp
def resolve_ontime(self, user: User, *_):
return self._client_utils.get_ontime_for_user(user)
def resolve_level(self, user: User, *_):
return self._levels.get_level(user)
@staticmethod
def resolve_server(user: User, *_):
return user.server

View File

@@ -0,0 +1,49 @@
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
from bot_data.abc.level_repository_abc import LevelRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_graphql.abc.query_abc import QueryABC
from bot_graphql.filter.auto_role_filter import AutoRoleFilter
from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter
from bot_graphql.filter.level_filter import LevelFilter
from bot_graphql.filter.server_filter import ServerFilter
from bot_graphql.filter.user_filter import UserFilter
class Query(QueryABC):
def __init__(
self,
auto_roles: AutoRoleRepositoryABC,
clients: ClientRepositoryABC,
known_users: KnownUserRepositoryABC,
levels: LevelRepositoryABC,
servers: ServerRepositoryABC,
user_joined_servers: UserJoinedServerRepositoryABC,
user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC,
users: UserRepositoryABC,
):
QueryABC.__init__(self, "Query")
self._auto_roles = auto_roles
self._clients = clients
self._known_users = known_users
self._levels = levels
self._servers = servers
self._user_joined_servers = user_joined_servers
self._user_joined_voice_channels = user_joined_voice_channel
self._users = users
self.add_collection("autoRole", lambda *_: self._auto_roles.get_auto_roles(), AutoRoleFilter)
self.add_collection("autoRoleRule", lambda *_: self._auto_roles.get_auto_role_rules(), AutoRoleRuleFilter)
self.add_collection("client", lambda *_: self._clients.get_clients())
self.add_collection("knownUser", lambda *_: self._known_users.get_users())
self.add_collection("level", lambda *_: self._levels.get_levels(), LevelFilter)
self.add_collection("server", lambda *_: self._servers.get_servers(), ServerFilter)
self.add_collection("userJoinedServer", lambda *_: self._user_joined_servers.get_user_joined_servers())
self.add_collection(
"userJoinedVoiceChannel", lambda *_: self._user_joined_voice_channels.get_user_joined_voice_channels()
)
self.add_collection("user", lambda *_: self._users.get_users(), UserFilter)

View File

@@ -0,0 +1,18 @@
import os
from ariadne import make_executable_schema, load_schema_from_path
from graphql import GraphQLSchema
from bot_graphql.abc.query_abc import QueryABC
from bot_graphql.mutation import Mutation
from bot_graphql.query import Query
class Schema:
def __init__(self, query: Query, mutation: Mutation, queries: list[QueryABC]):
type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "model/"))
self._schema = make_executable_schema(type_defs, query, mutation, *queries)
@property
def schema(self) -> GraphQLSchema:
return self._schema

View File

@@ -1,9 +1,8 @@
from typing import List as TList, Optional, Any from typing import List as TList
import discord import discord
from cpl_core.database.context import DatabaseContextABC from cpl_core.database.context import DatabaseContextABC
from cpl_discord.command import DiscordCommandABC from cpl_discord.command import DiscordCommandABC
from cpl_discord.container import TextChannel
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List from cpl_query.extension import List
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
@@ -138,10 +137,10 @@ class AutoRoleGroup(DiscordCommandABC):
self._logger.trace(__name__, f"Finished command auto-role add") self._logger.trace(__name__, f"Finished command auto-role add")
return return
server_id = self._servers.get_server_by_discord_id(ctx.guild.id).server_id server = self._servers.get_server_by_discord_id(ctx.guild.id)
self._auto_roles.add_auto_role(AutoRole(server_id, int(channel.id), int(message_id))) self._auto_roles.add_auto_role(AutoRole(server, int(channel.id), int(message_id)))
self._db_context.save_changes() self._db_context.save_changes()
self._logger.info(__name__, f"Saved auto-role for message {message_id} at server {server_id}") self._logger.info(__name__, f"Saved auto-role for message {message_id} at server {server}")
await self._message_service.send_ctx_msg( await self._message_service.send_ctx_msg(
ctx, self._t.transform("modules.auto_role.add.success").format(message_id) ctx, self._t.transform("modules.auto_role.add.success").format(message_id)
) )

View File

@@ -8,6 +8,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.abc.data_seeder_abc import DataSeederABC
from modules.level.command.level_group import LevelGroup from modules.level.command.level_group import LevelGroup
from modules.level.events.level_on_member_join_event import LevelOnMemberJoinEvent from modules.level.events.level_on_member_join_event import LevelOnMemberJoinEvent
from modules.level.events.level_on_message_event import LevelOnMessageEvent from modules.level.events.level_on_message_event import LevelOnMessageEvent
@@ -29,7 +30,7 @@ class LevelModule(ModuleABC):
env.set_working_directory(cwd) env.set_working_directory(cwd)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(LevelSeeder) services.add_transient(DataSeederABC, LevelSeeder)
services.add_transient(LevelService) services.add_transient(LevelService)
# commands # commands

View File

@@ -12,7 +12,6 @@ from bot_data.model.statistic import Statistic
class AddStatisticForm(ui.Modal): class AddStatisticForm(ui.Modal):
description = ui.TextInput(label="Beschreibung", required=True) description = ui.TextInput(label="Beschreibung", required=True)
code = ui.TextInput(label="Code", required=True, style=TextStyle.long) code = ui.TextInput(label="Code", required=True, style=TextStyle.long)

View File

@@ -0,0 +1,44 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.database.context import DatabaseContextABC
from cpl_discord.service import DiscordBotServiceABC
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.data_seeder_abc import DataSeederABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.api_key import ApiKey
class ApiKeySeeder(DataSeederABC):
def __init__(
self,
logger: DatabaseLogger,
config: ConfigurationABC,
bot: DiscordBotServiceABC,
db: DatabaseContextABC,
users: UserRepositoryABC,
api_keys: ApiKeyRepositoryABC,
):
DataSeederABC.__init__(self)
self._logger = logger
self._config = config
self._bot = bot
self._db = db
self._users = users
self._api_keys = api_keys
async def seed(self):
self._logger.debug(__name__, f"API-Key seeder started")
if self._api_keys.get_api_keys().count() > 0:
self._logger.debug(__name__, f"Skip API-Key seeder")
return
try:
frontend_key = ApiKey("frontend", "87f529fd-a32e-40b3-a1d1-7a1583cf3ff5", None)
self._api_keys.add_api_key(frontend_key)
self._db.save_changes()
self._logger.info(__name__, f"Created frontend API-Key")
except Exception as e:
self._logger.fatal(__name__, "Cannot create frontend API-Key", e)

View File

@@ -0,0 +1,139 @@
import hashlib
import uuid
from typing import List as TList
import discord
from cpl_core.database.context import DatabaseContextABC
from cpl_discord.command import DiscordCommandABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from discord import app_commands
from discord.ext import commands
from discord.ext.commands import Context
from bot_api.configuration.authentication_settings import AuthenticationSettings
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.helper.command_checks import CommandChecks
from bot_core.logging.command_logger import CommandLogger
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.api_key import ApiKey
from modules.permission.abc.permission_service_abc import PermissionServiceABC
class ApiKeyGroup(DiscordCommandABC):
def __init__(
self,
logger: CommandLogger,
auth_settings: AuthenticationSettings,
message_service: MessageServiceABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsABC,
permission_service: PermissionServiceABC,
translate: TranslatePipe,
db: DatabaseContextABC,
servers: ServerRepositoryABC,
users: UserRepositoryABC,
api_keys: ApiKeyRepositoryABC,
):
DiscordCommandABC.__init__(self)
self._logger = logger
self._auth_settings = auth_settings
self._message_service = message_service
self._bot = bot
self._client_utils = client_utils
self._permissions = permission_service
self._t = translate
self._db = db
self._servers = servers
self._users = users
self._api_keys = api_keys
def _get_api_key_str(self, api_key: ApiKey) -> str:
return hashlib.sha256(
f"{api_key.identifier}:{api_key.key}+{self._auth_settings.secret_key}".encode("utf-8")
).hexdigest()
@commands.hybrid_group(name="api-key")
@commands.guild_only()
async def api_key(self, ctx: Context):
pass
@api_key.command()
@commands.guild_only()
@CommandChecks.check_is_ready()
@CommandChecks.check_is_member_technician()
async def get(self, ctx: Context, key: str, wait: int = None):
self._logger.debug(__name__, f"Received command api-key get {ctx}: {key},{wait}")
api_key = self._api_keys.get_api_key_by_key(key)
await self._message_service.send_ctx_msg(
ctx,
self._t.transform("modules.technician.api_key.get").format(
api_key.identifier, self._get_api_key_str(api_key)
),
)
self._logger.trace(__name__, f"Finished command api-key get")
@get.autocomplete("key")
async def get_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
keys = self._api_keys.get_api_keys()
return [
app_commands.Choice(name=f"{key.identifier}: {key.key}", value=key.key)
for key in self._client_utils.get_auto_complete_list(keys, current, lambda x: x.key)
]
@api_key.command()
@commands.guild_only()
@CommandChecks.check_is_ready()
@CommandChecks.check_is_member_moderator()
async def add(self, ctx: Context, identifier: str):
self._logger.debug(__name__, f"Received command api-key add {ctx}: {identifier}")
server = self._servers.get_server_by_discord_id(ctx.guild.id)
user = self._users.get_user_by_discord_id_and_server_id(ctx.author.id, server.server_id)
api_key = ApiKey(identifier, str(uuid.uuid4()), user)
self._api_keys.add_api_key(api_key)
self._db.save_changes()
await self._message_service.send_ctx_msg(
ctx,
self._t.transform("modules.technician.api_key.add.success").format(
identifier, self._get_api_key_str(api_key)
),
)
self._logger.trace(__name__, f"Finished command api-key add")
@api_key.command()
@commands.guild_only()
@CommandChecks.check_is_ready()
@CommandChecks.check_is_member_moderator()
async def remove(self, ctx: Context, key: str):
self._logger.debug(__name__, f"Received command api-key remove {ctx}: {key}")
keys = self._api_keys.get_api_keys().where(lambda x: x.key == key)
if keys.count() < 1:
await self._message_service.send_ctx_msg(
ctx,
self._t.transform("modules.technician.api_key.remove.not_found"),
)
api_key = keys.single()
self._api_keys.delete_api_key(api_key)
self._db.save_changes()
await self._message_service.send_ctx_msg(ctx, self._t.transform("modules.technician.api_key.remove.success"))
self._logger.trace(__name__, f"Finished command api-key remove")
@remove.autocomplete("key")
async def set_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]:
keys = self._api_keys.get_api_keys()
return [
app_commands.Choice(name=f"{key.identifier}: {key.key}", value=key.key)
for key in self._client_utils.get_auto_complete_list(keys, current, lambda x: x.key)
]

View File

@@ -5,11 +5,14 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.abc.data_seeder_abc import DataSeederABC
from modules.base.abc.base_helper_abc import BaseHelperABC from modules.base.abc.base_helper_abc import BaseHelperABC
from modules.base.service.base_helper_service import BaseHelperService
from modules.technician.api_key_seeder import ApiKeySeeder
from modules.technician.command.api_key_group import ApiKeyGroup
from modules.technician.command.log_command import LogCommand from modules.technician.command.log_command import LogCommand
from modules.technician.command.restart_command import RestartCommand from modules.technician.command.restart_command import RestartCommand
from modules.technician.command.shutdown_command import ShutdownCommand from modules.technician.command.shutdown_command import ShutdownCommand
from modules.base.service.base_helper_service import BaseHelperService
class TechnicianModule(ModuleABC): class TechnicianModule(ModuleABC):
@@ -20,9 +23,11 @@ class TechnicianModule(ModuleABC):
pass pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(DataSeederABC, ApiKeySeeder)
services.add_transient(BaseHelperABC, BaseHelperService) services.add_transient(BaseHelperABC, BaseHelperService)
# commands # commands
self._dc.add_command(RestartCommand) self._dc.add_command(RestartCommand)
self._dc.add_command(ShutdownCommand) self._dc.add_command(ShutdownCommand)
self._dc.add_command(LogCommand) self._dc.add_command(LogCommand)
self._dc.add_command(ApiKeyGroup)
# events # events

View File

@@ -16,7 +16,6 @@ class RemoveConfig:
build: BuildSettings, build: BuildSettings,
post_build: PostBuildSettings, post_build: PostBuildSettings,
): ):
self._env = env self._env = env
self._workspace = ws self._workspace = ws
self._project = project self._project = project

View File

@@ -1,16 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR

8153
kdb-web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "kdb-web", "name": "kdb-web",
"version": "0.3.0", "version": "0.3.dev162-3",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"update-version": "ts-node-esm update-version.ts", "update-version": "ts-node-esm update-version.ts",
@@ -16,29 +16,29 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^14.0.0", "@angular/animations": "^15.1.4",
"@angular/common": "^14.0.0", "@angular/common": "^15.1.4",
"@angular/compiler": "^14.0.0", "@angular/compiler": "^15.1.4",
"@angular/core": "^14.0.0", "@angular/core": "^15.1.4",
"@angular/forms": "^14.0.0", "@angular/forms": "^15.1.4",
"@angular/platform-browser": "^14.0.0", "@angular/platform-browser": "^15.1.4",
"@angular/platform-browser-dynamic": "^14.0.0", "@angular/platform-browser-dynamic": "^15.1.4",
"@angular/router": "^14.0.0", "@angular/router": "^15.1.4",
"@auth0/angular-jwt": "^5.1.0", "@auth0/angular-jwt": "^5.1.0",
"@microsoft/signalr": "^6.0.9", "@microsoft/signalr": "^6.0.9",
"@ngx-translate/core": "^14.0.0", "@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0", "@ngx-translate/http-loader": "^7.0.0",
"@types/socket.io-client": "^3.0.0", "@types/socket.io-client": "^3.0.0",
"primeicons": "^6.0.1", "primeicons": "^6.0.1",
"primeng": "^14.1.2", "primeng": "^15.2.0",
"rxjs": "~7.5.0", "rxjs": "~7.5.0",
"socket.io-client": "^4.5.3", "socket.io-client": "^4.5.3",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^14.0.0", "@angular-devkit/build-angular": "^15.1.5",
"@angular/cli": "~14.0.0", "@angular/cli": "~15.1.5",
"@angular/compiler-cli": "^14.0.0", "@angular/compiler-cli": "^15.1.4",
"@types/jasmine": "~4.0.0", "@types/jasmine": "~4.0.0",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"jasmine-core": "~4.1.0", "jasmine-core": "~4.1.0",
@@ -48,6 +48,6 @@
"karma-jasmine": "~5.0.0", "karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "~4.7.2" "typescript": "~4.9.5"
} }
} }

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