Compare commits
70 Commits
49d9509255
...
#139
Author | SHA1 | Date | |
---|---|---|---|
069c701a05 | |||
5efcddf983 | |||
5beaf24bdc | |||
4bbcd387c6 | |||
117e1aeda8 | |||
7260ed0164 | |||
77d723b9da | |||
75c316f2d2 | |||
de78cec96c | |||
9de66d4fd4 | |||
4a0f5c28c1 | |||
d2f99003ff | |||
9dd3fd4b8e | |||
d18500b96c | |||
91fdf34d32 | |||
12369cdbe3 | |||
25c698273a | |||
2868b1afe2 | |||
0d1c15b31d | |||
840da350e4 | |||
bd94c42eae | |||
c7a925b997 | |||
7fb6d22c3f | |||
c5b5297058 | |||
9ed66c2560 | |||
6e6157ccf2 | |||
f136d6164e | |||
9b5033b80e | |||
f5a71a8450 | |||
d3279eb7c7 | |||
ec7aeb8712 | |||
fd609eb923 | |||
a7dbc75d2e | |||
b0459567f4 | |||
25b7b18013 | |||
87350cba1a | |||
864d181de0 | |||
90011be760 | |||
47dd6fdc2d | |||
8445c23e7f | |||
e6fc41090a | |||
7c79c6f992 | |||
7b8dca64bf | |||
549b05087f | |||
83d18da58f | |||
18e4465b17 | |||
2e20bb12de | |||
4f4e80fb6b | |||
fd1245fb4f | |||
fdb358c45e | |||
c438a91b87 | |||
a46fbcd9fc | |||
91285540c6 | |||
ab2145d5df | |||
63fe566044 | |||
d45d787cea | |||
442170eca9 | |||
2c7f4647af | |||
53604706c2 | |||
42d8a16d05 | |||
d933cae73c | |||
ce73145dbb | |||
ab7fdec499 | |||
d2968e0652 | |||
cebdc1b860 | |||
87435614db | |||
e9386633d8 | |||
c09841a3da | |||
9017535bfb | |||
3c1f017e3a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -143,4 +143,5 @@ PythonImportHelper-v2-Completion.json
|
||||
deploy/
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
.idea/
|
||||
selenium-data/
|
@@ -1,2 +1,14 @@
|
||||
# kd_discord_bot
|
||||
|
||||
## Test Bot
|
||||
|
||||
To test the bot run unittests or call ```cpl test```.
|
||||
Configure test instance by creating the file ./test/ui_tests/.env and set following environment variables:
|
||||
|
||||
```sh
|
||||
KDB_TEST_DB_PASSWORD=
|
||||
KDB_TEST_NAME=
|
||||
KDB_TEST_TOKEN=
|
||||
KDB_TEST_DISCORD_MAIL=
|
||||
KDB_TEST_DISCORD_PASSWORD=
|
||||
```
|
@@ -13,32 +13,32 @@
|
||||
"level": "src/modules/level/level.json",
|
||||
"permission": "src/modules/permission/permission.json",
|
||||
"stats": "src/modules/stats/stats.json",
|
||||
"technician": "src/modules/technician/technician.json",
|
||||
"ui-tests": "test/ui_tests/ui-tests.json",
|
||||
"ui-tests-shared": "test/ui_tests_shared/ui-tests-shared.json",
|
||||
"ui-tests-tests": "test/ui_tests_tests/ui-tests-tests.json",
|
||||
"get-version": "tools/get_version/get-version.json",
|
||||
"post-build": "tools/post_build/post-build.json",
|
||||
"set-version": "tools/set_version/set-version.json"
|
||||
},
|
||||
"Scripts": {
|
||||
"sv": "cpl set-version",
|
||||
"test": "export $(cat test/ui_tests/.env); export PYTHONPATH=$PWD/src:$PYTHONPATH; cpl run ui-tests",
|
||||
"sv": "cpl set-version $ARGS",
|
||||
"set-version": "cpl run set-version $ARGS; echo '';",
|
||||
|
||||
"gv": "cpl get-version",
|
||||
"get-version": "export VERSION=$(cpl run get-version); echo $VERSION;",
|
||||
|
||||
"pre-build": "cpl set-version $ARGS",
|
||||
"post-build": "cpl run post-build",
|
||||
|
||||
"pre-prod": "cpl build",
|
||||
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
|
||||
|
||||
"pre-stage": "cpl build",
|
||||
"stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;",
|
||||
|
||||
"pre-dev": "cpl build",
|
||||
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;",
|
||||
|
||||
"docker-build": "cpl b; docker-compose down; docker build -t kdb-bot/kdb-bot:$(cpl gv) .",
|
||||
"docker-compose": "docker-compose up -d",
|
||||
"docker": "cpl docker-build; cpl docker-compose;"
|
||||
"docker-build": "cpl build $ARGS; docker build -t kdb-bot/kdb-bot:$(cpl gv) .;",
|
||||
"dc-up": "docker-compose up -d",
|
||||
"dc-down": "docker-compose down",
|
||||
"docker": "cpl dc-down; cpl docker-build; cpl dc-up;"
|
||||
}
|
||||
}
|
||||
}
|
Submodule kdb-bot/docker updated: b3d5e9feef...48c2683965
17
kdb-bot/dockerfile
Normal file
17
kdb-bot/dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM python:3.10.4-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./dist/bot/build/ .
|
||||
|
||||
RUN python -m pip install --upgrade pip
|
||||
|
||||
RUN apk update
|
||||
RUN apk add --update alpine-sdk linux-headers
|
||||
RUN apk add bash
|
||||
RUN apk add nano
|
||||
|
||||
RUN pip install -r requirements.txt --extra-index-url https://pip.sh-edraft.de
|
||||
RUN pip install flask[async]
|
||||
|
||||
CMD [ "bash", "/app/bot/bot"]
|
@@ -16,7 +16,7 @@
|
||||
"LicenseName": "MIT",
|
||||
"LicenseDescription": "MIT, see LICENSE for more details.",
|
||||
"Dependencies": [
|
||||
"cpl-core==2022.10.0.post7",
|
||||
"cpl-core==2022.10.0.post9",
|
||||
"cpl-translation==2022.10.0.post2",
|
||||
"cpl-query==2022.10.0.post2",
|
||||
"cpl-discord==2022.10.0.post6",
|
||||
@@ -27,7 +27,8 @@
|
||||
"waitress==2.1.2",
|
||||
"Flask-SocketIO==5.3.1",
|
||||
"eventlet==0.33.1",
|
||||
"requests-oauthlib==1.3.1"
|
||||
"requests-oauthlib==1.3.1",
|
||||
"icmplib==3.0.3"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli==2022.10.0"
|
||||
@@ -59,9 +60,9 @@
|
||||
"../modules/boot_log/boot-log.json",
|
||||
"../modules/database/database.json",
|
||||
"../modules/level/level.json",
|
||||
"../modules/permission/level.json",
|
||||
"../modules/permission/permission.json",
|
||||
"../modules/permission/stats.json"
|
||||
"../modules/stats/stats.json",
|
||||
"../modules/technician/technician.json"
|
||||
]
|
||||
}
|
||||
}
|
Submodule kdb-bot/src/bot/config updated: bd8d3a5dad...c8983770f7
@@ -11,6 +11,7 @@ from modules.database.database_module import DatabaseModule
|
||||
from modules.level.level_module import LevelModule
|
||||
from modules.permission.permission_module import PermissionModule
|
||||
from modules.stats.stats_module import StatsModule
|
||||
from modules.technician.technician_module import TechnicianModule
|
||||
|
||||
|
||||
class ModuleList:
|
||||
@@ -21,13 +22,14 @@ class ModuleList:
|
||||
return List(type, [
|
||||
CoreModule, # has to be first!
|
||||
DataModule,
|
||||
PermissionModule,
|
||||
DatabaseModule,
|
||||
AutoRoleModule,
|
||||
BaseModule,
|
||||
DatabaseModule,
|
||||
LevelModule,
|
||||
PermissionModule,
|
||||
ApiModule,
|
||||
StatsModule,
|
||||
TechnicianModule,
|
||||
# has to be last!
|
||||
BootLogModule,
|
||||
CoreExtensionModule,
|
||||
|
@@ -72,11 +72,6 @@
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"admin": {
|
||||
"restart_message": "Bin gleich wieder da :D",
|
||||
"shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)",
|
||||
"deploy_message": "Der neue Stand wurde hochgeladen."
|
||||
},
|
||||
"auto_role": {
|
||||
"list": {
|
||||
"title": "Beobachtete Nachrichten:",
|
||||
@@ -156,6 +151,15 @@
|
||||
},
|
||||
"footer": ""
|
||||
},
|
||||
"mass_move": {
|
||||
"moved": "Alle Personen aus {} wurden nach {} verschoben.",
|
||||
"channel_from_error": "Du musst dich in einem Voicechannel befinden oder die Option \"channel_from\" mit angeben."
|
||||
},
|
||||
"presence": {
|
||||
"changed": "Presence wurde geändert.",
|
||||
"removed": "Presence wurde entfernt.",
|
||||
"max_char_count_exceeded": "Der Text darf nicht mehr als 128 Zeichen lang sein!"
|
||||
},
|
||||
"user_info": {
|
||||
"fields": {
|
||||
"id": "Id",
|
||||
@@ -249,6 +253,11 @@
|
||||
"failed": "Statistik kann nicht gelöscht werden :(",
|
||||
"success": "Statistik wurde gelöscht :D"
|
||||
}
|
||||
},
|
||||
"technician": {
|
||||
"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 :)",
|
||||
"log_message": "Hier sind deine Logdateien! :)"
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
|
@@ -70,6 +70,9 @@ class AuthServiceABC(ABC):
|
||||
@abstractmethod
|
||||
async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def login_discord_async(self, oauth_dto: AuthUserDTO) -> TokenDTO: pass
|
||||
|
||||
@abstractmethod
|
||||
async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass
|
||||
|
||||
|
Submodule kdb-bot/src/bot_api/config updated: 98303ffd45...e6046881b5
@@ -82,8 +82,18 @@ class AuthDiscordController:
|
||||
), response['id'])
|
||||
return jsonify(result.to_dict())
|
||||
|
||||
@Route.post(f'{BasePath}/register')
|
||||
async def discord_register(self):
|
||||
dto: OAuthDTO = JSONProcessor.process(OAuthDTO, request.get_json(force=True, silent=True))
|
||||
await self._auth_service.add_auth_user_by_oauth_async(dto)
|
||||
return '', 200
|
||||
@Route.get(f'{BasePath}/login')
|
||||
async def discord_login(self) -> Response:
|
||||
response = self._get_user_from_discord_response()
|
||||
dto = AuthUserDTO(
|
||||
0,
|
||||
response['username'],
|
||||
response['discriminator'],
|
||||
response['email'],
|
||||
str(uuid.uuid4()),
|
||||
None,
|
||||
AuthRoleEnum.normal
|
||||
)
|
||||
|
||||
result = await self._auth_service.login_discord_async(dto)
|
||||
return jsonify(result.to_dict())
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import enum
|
||||
from inspect import signature, Parameter
|
||||
|
||||
from cpl_core.utils import String
|
||||
@@ -28,6 +29,9 @@ class JSONProcessor:
|
||||
if isinstance(value, dict):
|
||||
value = JSONProcessor.process(parameter.annotation, value)
|
||||
|
||||
if issubclass(parameter.annotation, enum.Enum):
|
||||
value = parameter.annotation(value)
|
||||
|
||||
args.append(value)
|
||||
|
||||
elif parameter.default != Parameter.empty:
|
||||
|
@@ -85,7 +85,7 @@ class AuthUserDTO(DtoABC):
|
||||
self._email = values['email']
|
||||
self._password = values['password']
|
||||
self._is_confirmed = values['isConfirmed']
|
||||
self._auth_role = values['authRole']
|
||||
self._auth_role = AuthRoleEnum(values['authRole'])
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
|
@@ -33,8 +33,8 @@ class UpdateAuthUserDTO(DtoABC):
|
||||
return self._change_password
|
||||
|
||||
def from_dict(self, values: dict):
|
||||
self._auth_user = values['authUser']
|
||||
self._new_auth_user = values['newAuthUser']
|
||||
self._auth_user = AuthUserDTO().from_dict(values['authUser'])
|
||||
self._new_auth_user = AuthUserDTO().from_dict(values['newAuthUser'])
|
||||
self._change_password = False if 'changePassword' not in values else bool(values['changePassword'])
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
|
@@ -460,6 +460,24 @@ class AuthService(AuthServiceABC):
|
||||
self._db.save_changes()
|
||||
return TokenDTO(token, refresh_token)
|
||||
|
||||
async def login_discord_async(self, user_dto: AuthUserDTO) -> TokenDTO:
|
||||
if user_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, 'User not set')
|
||||
|
||||
db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
|
||||
if db_user is None:
|
||||
await self.add_auth_user_async(user_dto)
|
||||
# raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found')
|
||||
|
||||
db_user = self._auth_users.get_auth_user_by_email(user_dto.email)
|
||||
token = self.generate_token(db_user)
|
||||
refresh_token = self._create_and_save_refresh_token(db_user)
|
||||
if db_user.forgot_password_id is not None:
|
||||
db_user.forgot_password_id = None
|
||||
|
||||
self._db.save_changes()
|
||||
return TokenDTO(token, refresh_token)
|
||||
|
||||
async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO:
|
||||
if token_dto is None:
|
||||
raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set')
|
||||
|
@@ -13,10 +13,14 @@ class CustomFileLoggerABC(Logger, ABC):
|
||||
@abstractmethod
|
||||
def __init__(self, key: str, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
|
||||
self._key = key
|
||||
settings: LoggingSettings = config.get_configuration(f'{FileLoggingSettings.__name__}_{key}')
|
||||
Logger.__init__(self, settings, time_format, env)
|
||||
self._settings: LoggingSettings = config.get_configuration(f'{FileLoggingSettings.__name__}_{key}')
|
||||
Logger.__init__(self, self._settings, time_format, env)
|
||||
self._begin_log()
|
||||
|
||||
@property
|
||||
def settings(self) -> LoggingSettings:
|
||||
return self._settings
|
||||
|
||||
def _begin_log(self):
|
||||
console_level = self._console.value
|
||||
self._console = LoggingLevelEnum.OFF
|
||||
|
@@ -28,4 +28,4 @@ class MessageServiceABC(ABC):
|
||||
async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
||||
|
||||
@abstractmethod
|
||||
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass
|
||||
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True, **kwargs): pass
|
||||
|
@@ -119,7 +119,7 @@ class MessageService(MessageServiceABC):
|
||||
if ctx.guild is not None:
|
||||
await self.delete_message(msg, without_tracking)
|
||||
|
||||
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False):
|
||||
async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False, **kwargs):
|
||||
if interaction is None:
|
||||
self._logger.warn(__name__, 'Message context is empty')
|
||||
self._logger.debug(__name__, f'Message: {message}')
|
||||
@@ -128,9 +128,9 @@ class MessageService(MessageServiceABC):
|
||||
self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}')
|
||||
try:
|
||||
if isinstance(message, discord.Embed):
|
||||
await interaction.response.send_message(embed=message)
|
||||
await interaction.response.send_message(embed=message, **kwargs)
|
||||
else:
|
||||
await interaction.response.send_message(message)
|
||||
await interaction.response.send_message(message, **kwargs)
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e)
|
||||
else:
|
||||
|
@@ -9,7 +9,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
from modules.auto_role.command.auto_role_group import AutoRoleGroup
|
||||
from modules.auto_role.events.auto_role_on_raw_reaction_add import AutoRoleOnRawReactionAddEvent
|
||||
from modules.auto_role.events.auto_role_on_raw_reaction_remove import AutoRoleOnRawReactionRemoveEvent
|
||||
from modules.auto_role.helper.reaction_handler import ReactionHandler
|
||||
from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler
|
||||
|
||||
|
||||
class AutoRoleModule(ModuleABC):
|
||||
@@ -21,7 +21,7 @@ class AutoRoleModule(ModuleABC):
|
||||
pass
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
services.add_transient(ReactionHandler)
|
||||
services.add_transient(AutoRoleReactionHandler)
|
||||
# commands
|
||||
self._dc.add_command(AutoRoleGroup)
|
||||
# events
|
||||
|
@@ -98,7 +98,7 @@ class AutoRoleGroup(DiscordCommandABC):
|
||||
message = List(discord.Message, [message async for message in channel.history(limit=50)]).where(lambda m: m.id == int(message_id)).single_or_default()
|
||||
if message is None:
|
||||
self._logger.debug(__name__, f'Message with id {message_id} not found in {channel.name}')
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.not_found').format(message_id, channel.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.not_found').format(message_id, channel.mention))
|
||||
self._logger.trace(__name__, f'Finished command auto-role add')
|
||||
return
|
||||
|
||||
@@ -250,7 +250,7 @@ class AutoRoleGroup(DiscordCommandABC):
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot add reaction {rule.emoji_name} to message: {auto_role_from_db.discord_message_id}', e)
|
||||
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.success').format(emoji, role.name, auto_role))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.success').format(emoji, role.mention, auto_role))
|
||||
self._logger.trace(__name__, f'Finished command auto-role rule add')
|
||||
|
||||
@add.autocomplete('auto_role')
|
||||
|
@@ -6,7 +6,7 @@ from discord import RawReactionActionEvent
|
||||
from bot_core.helper.event_checks import EventChecks
|
||||
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
|
||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||
from modules.auto_role.helper.reaction_handler import ReactionHandler
|
||||
from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler
|
||||
|
||||
|
||||
class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC):
|
||||
@@ -17,7 +17,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC):
|
||||
bot: DiscordBotServiceABC,
|
||||
servers: ServerRepositoryABC,
|
||||
auto_roles: AutoRoleRepositoryABC,
|
||||
reaction_handler: ReactionHandler
|
||||
reaction_handler: AutoRoleReactionHandler
|
||||
):
|
||||
OnRawReactionAddABC.__init__(self)
|
||||
|
||||
|
@@ -6,7 +6,7 @@ from discord import RawReactionActionEvent
|
||||
from bot_core.helper.event_checks import EventChecks
|
||||
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
|
||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||
from modules.auto_role.helper.reaction_handler import ReactionHandler
|
||||
from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler
|
||||
|
||||
|
||||
class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC):
|
||||
@@ -17,7 +17,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC):
|
||||
bot: DiscordBotServiceABC,
|
||||
servers: ServerRepositoryABC,
|
||||
auto_roles: AutoRoleRepositoryABC,
|
||||
reaction_handler: ReactionHandler
|
||||
reaction_handler: AutoRoleReactionHandler
|
||||
):
|
||||
OnRawReactionRemoveABC.__init__(self)
|
||||
|
||||
|
@@ -10,7 +10,7 @@ from bot_data.model.auto_role import AutoRole
|
||||
from bot_data.model.auto_role_rule import AutoRoleRule
|
||||
|
||||
|
||||
class ReactionHandler:
|
||||
class AutoRoleReactionHandler:
|
||||
|
||||
def __init__(
|
||||
self,
|
@@ -10,18 +10,21 @@ from modules.base.abc.base_helper_abc import BaseHelperABC
|
||||
from modules.base.command.afk_command import AFKCommand
|
||||
from modules.base.command.help_command import HelpCommand
|
||||
from modules.base.command.info_command import InfoCommand
|
||||
from modules.base.command.mass_move_command import MassMoveCommand
|
||||
from modules.base.command.ping_command import PingCommand
|
||||
from modules.base.command.presence_command import PresenceCommand
|
||||
from modules.base.command.purge_command import PurgeCommand
|
||||
from modules.base.command.restart_command import RestartCommand
|
||||
from modules.base.command.shutdown_command import ShutdownCommand
|
||||
from modules.base.command.user_group import UserGroup
|
||||
from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent
|
||||
from modules.base.events.base_on_command_event import BaseOnCommandEvent
|
||||
from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent
|
||||
from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent
|
||||
from modules.base.events.base_on_message_event import BaseOnMessageEvent
|
||||
from modules.base.events.base_on_raw_reaction_add import BaseOnRawReactionAddEvent
|
||||
from modules.base.events.base_on_raw_reaction_remove import BaseOnRawReactionRemoveEvent
|
||||
from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent
|
||||
from modules.base.events.base_on_voice_state_update_event_help_channel import BaseOnVoiceStateUpdateEventHelpChannel
|
||||
from modules.base.helper.base_reaction_handler import BaseReactionHandler
|
||||
from modules.base.service.base_helper_service import BaseHelperService
|
||||
|
||||
|
||||
@@ -35,14 +38,15 @@ class BaseModule(ModuleABC):
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
services.add_transient(BaseHelperABC, BaseHelperService)
|
||||
services.add_transient(BaseReactionHandler)
|
||||
# commands
|
||||
self._dc.add_command(AFKCommand)
|
||||
self._dc.add_command(HelpCommand)
|
||||
self._dc.add_command(InfoCommand)
|
||||
self._dc.add_command(MassMoveCommand)
|
||||
self._dc.add_command(PingCommand)
|
||||
self._dc.add_command(PresenceCommand)
|
||||
|
||||
self._dc.add_command(RestartCommand)
|
||||
self._dc.add_command(ShutdownCommand)
|
||||
self._dc.add_command(PurgeCommand)
|
||||
self._dc.add_command(UserGroup)
|
||||
# events
|
||||
@@ -51,5 +55,7 @@ class BaseModule(ModuleABC):
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_add.value, BaseOnRawReactionAddEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_remove.value, BaseOnRawReactionRemoveEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEventHelpChannel)
|
||||
|
49
kdb-bot/src/modules/base/command/mass_move_command.py
Normal file
49
kdb-bot/src/modules/base/command/mass_move_command.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
from cpl_discord.command import DiscordCommandABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
|
||||
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||
from bot_core.helper.command_checks import CommandChecks
|
||||
from bot_core.logging.command_logger import CommandLogger
|
||||
|
||||
|
||||
class MassMoveCommand(DiscordCommandABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: CommandLogger,
|
||||
message_service: MessageServiceABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
translate: TranslatePipe,
|
||||
):
|
||||
DiscordCommandABC.__init__(self)
|
||||
self._logger = logger
|
||||
self._message_service = message_service
|
||||
self._bot = bot
|
||||
self._t = translate
|
||||
|
||||
@commands.hybrid_command(name='mass-move')
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_moderator()
|
||||
async def mass_move(self, ctx: Context, channel_to: discord.VoiceChannel,
|
||||
channel_from: discord.VoiceChannel = None):
|
||||
self._logger.debug(__name__, f'Received command mass-move {ctx}')
|
||||
|
||||
if channel_from is None and ctx.author.voice is None:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.mass_move.channel_from_error'))
|
||||
return
|
||||
|
||||
if channel_from is None:
|
||||
channel_from = ctx.author.voice.channel
|
||||
|
||||
moves = [member.move_to(channel_to) for member in channel_from.members]
|
||||
await asyncio.gather(*moves)
|
||||
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.mass_move.moved').format(channel_from.mention, channel_to.mention))
|
||||
|
||||
self._logger.trace(__name__, f'Finished mass-move command')
|
@@ -1,3 +1,4 @@
|
||||
import discord
|
||||
from cpl_discord.command import DiscordCommandABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
@@ -8,6 +9,10 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||
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.server_repository_abc import ServerRepositoryABC
|
||||
from modules.base.abc.base_helper_abc import BaseHelperABC
|
||||
from modules.base.configuration.base_server_settings import BaseServerSettings
|
||||
from modules.permission.abc.permission_service_abc import PermissionServiceABC
|
||||
|
||||
|
||||
class PingCommand(DiscordCommandABC):
|
||||
@@ -18,7 +23,10 @@ class PingCommand(DiscordCommandABC):
|
||||
message_service: MessageServiceABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
client_utils: ClientUtilsServiceABC,
|
||||
translate: TranslatePipe
|
||||
translate: TranslatePipe,
|
||||
permissions: PermissionServiceABC,
|
||||
base_helper: BaseHelperABC,
|
||||
servers: ServerRepositoryABC,
|
||||
):
|
||||
DiscordCommandABC.__init__(self)
|
||||
|
||||
@@ -27,13 +35,34 @@ class PingCommand(DiscordCommandABC):
|
||||
self._bot = bot
|
||||
self._client_utils = client_utils
|
||||
self._t = translate
|
||||
self._permissions = permissions
|
||||
self._base_helper = base_helper
|
||||
self._servers = servers
|
||||
|
||||
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
|
||||
|
||||
@staticmethod
|
||||
def _get_ping(url: str) -> float:
|
||||
from icmplib import ping
|
||||
ping_result = ping(url, count=4, interval=0.2, privileged=False)
|
||||
return ping_result.avg_rtt
|
||||
|
||||
@commands.hybrid_command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
async def ping(self, ctx: Context):
|
||||
self._logger.debug(__name__, f'Received command ping {ctx}')
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong'))
|
||||
if self._permissions.is_member_technician(ctx.author):
|
||||
embed = discord.Embed(
|
||||
title=self._t.transform('modules.base.info.title'),
|
||||
description=self._t.transform('modules.base.info.description'),
|
||||
color=int('ef9d0d', 16)
|
||||
)
|
||||
server = self._servers.get_server_by_discord_id(ctx.guild.id)
|
||||
settings: BaseServerSettings = self._base_helper.get_config(server.discord_server_id)
|
||||
for server in settings.ping_urls:
|
||||
embed.add_field(name=server, value=f'{self._get_ping(server)} ms', inline=False)
|
||||
await self._message_service.send_ctx_msg(ctx, embed)
|
||||
else:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong'))
|
||||
self._logger.trace(__name__, f'Finished ping command')
|
||||
|
48
kdb-bot/src/modules/base/command/presence_command.py
Normal file
48
kdb-bot/src/modules/base/command/presence_command.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import discord
|
||||
from cpl_discord.command import DiscordCommandABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
|
||||
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||
from bot_core.helper.command_checks import CommandChecks
|
||||
from bot_core.logging.command_logger import CommandLogger
|
||||
|
||||
|
||||
class PresenceCommand(DiscordCommandABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: CommandLogger,
|
||||
message_service: MessageServiceABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
translate: TranslatePipe,
|
||||
):
|
||||
DiscordCommandABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._message_service = message_service
|
||||
self._bot = bot
|
||||
self._t = translate
|
||||
|
||||
@commands.hybrid_command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_moderator()
|
||||
async def presence(self, ctx: Context, text: str = ''):
|
||||
self._logger.debug(__name__, f'Received command presence {ctx}')
|
||||
|
||||
if text == '':
|
||||
await self._bot.change_presence(activity=None)
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.removed'))
|
||||
return
|
||||
|
||||
if len(text) > 128:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.max_char_count_exceeded'))
|
||||
return
|
||||
|
||||
await self._bot.change_presence(activity=discord.Game(name=text))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.changed'))
|
||||
|
||||
self._logger.trace(__name__, f'Finished presence command')
|
@@ -62,10 +62,14 @@ class UserGroup(DiscordCommandABC):
|
||||
@user.command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_moderator()
|
||||
async def info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None):
|
||||
self._logger.debug(__name__, f'Received command user-info {ctx}:{member},{wait}')
|
||||
|
||||
is_mod = self._permissions.is_member_moderator(ctx.author)
|
||||
if member is not None and not is_mod:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message'))
|
||||
return
|
||||
|
||||
if member is None or not isinstance(member, discord.Member):
|
||||
member = ctx.author
|
||||
|
||||
@@ -94,20 +98,26 @@ class UserGroup(DiscordCommandABC):
|
||||
roles += f'{role.name}\n'
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.roles'), value=roles, inline=False)
|
||||
|
||||
joins_string = ''
|
||||
for join in joins:
|
||||
joins_string += f'{self._date.transform(join.joined_on)}\n'
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string)
|
||||
if is_mod or member == ctx.author:
|
||||
joins_string = ''
|
||||
for join in joins:
|
||||
joins_string += f'{self._date.transform(join.joined_on)}\n'
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string)
|
||||
|
||||
lefts_string = ''
|
||||
for join in joins:
|
||||
if join.leaved_on is None:
|
||||
if lefts_string == '':
|
||||
lefts_string = '/'
|
||||
continue
|
||||
lefts_string += f'{self._date.transform(join.leaved_on)}\n'
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string)
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False)
|
||||
if is_mod or member == ctx.author:
|
||||
lefts_string = ''
|
||||
for join in joins:
|
||||
if join.leaved_on is None:
|
||||
if lefts_string == '':
|
||||
lefts_string = '/'
|
||||
continue
|
||||
lefts_string += f'{self._date.transform(join.leaved_on)}\n'
|
||||
|
||||
await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait)
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string)
|
||||
|
||||
if is_mod or member == ctx.author:
|
||||
embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False)
|
||||
|
||||
# send to interaction because of sensitive data
|
||||
await self._message_service.send_interaction_msg(ctx.interaction, embed, wait_before_delete=wait)
|
||||
self._logger.trace(__name__, f'Finished user-info command')
|
||||
|
@@ -13,11 +13,13 @@ class BaseServerSettings(ConfigurationModelABC):
|
||||
self._id: int = 0
|
||||
self._max_voice_state_hours: int = 0
|
||||
self._xp_per_message: int = 0
|
||||
self._xp_per_reaction: int = 0
|
||||
self._xp_per_ontime_hour: int = 0
|
||||
self._afk_channel_ids: List[int] = List(int)
|
||||
self._afk_command_channel_id: int = 0
|
||||
self._help_command_reference_url: str = ''
|
||||
self._help_voice_channel_id: int = 0
|
||||
self._ping_urls = List(str)
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
@@ -31,6 +33,10 @@ class BaseServerSettings(ConfigurationModelABC):
|
||||
def xp_per_message(self) -> int:
|
||||
return self._xp_per_message
|
||||
|
||||
@property
|
||||
def xp_per_reaction(self) -> int:
|
||||
return self._xp_per_reaction
|
||||
|
||||
@property
|
||||
def xp_per_ontime_hour(self) -> int:
|
||||
return self._xp_per_ontime_hour
|
||||
@@ -51,17 +57,24 @@ class BaseServerSettings(ConfigurationModelABC):
|
||||
def help_voice_channel_id(self) -> int:
|
||||
return self._help_voice_channel_id
|
||||
|
||||
@property
|
||||
def ping_urls(self) -> List[str]:
|
||||
return self._ping_urls
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._id = int(settings['Id'])
|
||||
self._max_voice_state_hours = int(settings['MaxVoiceStateHours'])
|
||||
self._xp_per_message = int(settings['XpPerMessage'])
|
||||
self._xp_per_reaction = int(settings['XpPerReaction'])
|
||||
self._xp_per_ontime_hour = int(settings['XpPerOntimeHour'])
|
||||
for index in settings['AFKChannelIds']:
|
||||
self._afk_channel_ids.append(int(index))
|
||||
self._afk_command_channel_id = settings['AFKCommandChannelId']
|
||||
self._help_command_reference_url = settings['HelpCommandReferenceUrl']
|
||||
self._help_voice_channel_id = settings['HelpVoiceChannelId']
|
||||
for url in settings['PingURLs']:
|
||||
self._ping_urls.append(url)
|
||||
except Exception as e:
|
||||
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
|
||||
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
|
||||
|
@@ -70,17 +70,17 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC):
|
||||
await self._messenger.send_dm_message(self._t.transform('modules.base.welcome_message').format(member.guild.name), member)
|
||||
|
||||
for admin in self._permission_service.get_admins(member.guild.id):
|
||||
await self._messenger.send_dm_message(self._t.transform('modules.base.welcome_message_for_team').format(member.name), admin)
|
||||
await self._messenger.send_dm_message(self._t.transform('modules.base.welcome_message_for_team').format(member.mention), admin)
|
||||
|
||||
for moderator in self._permission_service.get_moderators(member.guild.id):
|
||||
await self._messenger.send_dm_message(self._t.transform('modules.base.welcome_message_for_team').format(member.name), moderator)
|
||||
await self._messenger.send_dm_message(self._t.transform('modules.base.welcome_message_for_team').format(member.mention), moderator)
|
||||
|
||||
try:
|
||||
server = self._servers.get_server_by_discord_id(member.guild.id)
|
||||
|
||||
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id)
|
||||
if user is not None:
|
||||
self._user_joins.add_user_joined_server(UserJoinedServer(user, datetime.now()))
|
||||
self._db.save_changes()
|
||||
return
|
||||
|
||||
self._logger.debug(__name__, f'Add user: {member.id}')
|
||||
|
36
kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py
Normal file
36
kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from cpl_core.logging import LoggerABC
|
||||
from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from discord import RawReactionActionEvent
|
||||
|
||||
from bot_core.helper.event_checks import EventChecks
|
||||
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
|
||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||
from modules.base.helper.base_reaction_handler import BaseReactionHandler
|
||||
|
||||
|
||||
class BaseOnRawReactionAddEvent(OnRawReactionAddABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: LoggerABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
servers: ServerRepositoryABC,
|
||||
auto_roles: AutoRoleRepositoryABC,
|
||||
reaction_handler: BaseReactionHandler
|
||||
):
|
||||
OnRawReactionAddABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._bot = bot
|
||||
self._servers = servers
|
||||
self._auto_roles = auto_roles
|
||||
self._reaction_handler = reaction_handler
|
||||
|
||||
@EventChecks.check_is_ready()
|
||||
async def on_raw_reaction_add(self, payload: RawReactionActionEvent):
|
||||
self._logger.debug(__name__, f'Module {type(self)} started')
|
||||
|
||||
await self._reaction_handler.handle(payload, 'add')
|
||||
|
||||
self._logger.debug(__name__, f'Module {type(self)} stopped')
|
@@ -0,0 +1,36 @@
|
||||
from cpl_core.logging import LoggerABC
|
||||
from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from discord import RawReactionActionEvent
|
||||
|
||||
from bot_core.helper.event_checks import EventChecks
|
||||
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
|
||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||
from modules.base.helper.base_reaction_handler import BaseReactionHandler
|
||||
|
||||
|
||||
class BaseOnRawReactionRemoveEvent(OnRawReactionRemoveABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: LoggerABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
servers: ServerRepositoryABC,
|
||||
auto_roles: AutoRoleRepositoryABC,
|
||||
reaction_handler: BaseReactionHandler,
|
||||
):
|
||||
OnRawReactionRemoveABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._bot = bot
|
||||
self._servers = servers
|
||||
self._auto_roles = auto_roles
|
||||
self._reaction_handler = reaction_handler
|
||||
|
||||
@EventChecks.check_is_ready()
|
||||
async def on_raw_reaction_remove(self, payload: RawReactionActionEvent):
|
||||
self._logger.debug(__name__, f'Module {type(self)} started')
|
||||
|
||||
await self._reaction_handler.handle(payload, 'remove')
|
||||
|
||||
self._logger.debug(__name__, f'Module {type(self)} stopped')
|
@@ -46,7 +46,7 @@ class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC):
|
||||
mods = [*self._permissions.get_admins(member.guild.id), *self._permissions.get_moderators(member.guild.id)]
|
||||
for a in mods:
|
||||
await self._message_service.send_dm_message(
|
||||
self._t.transform('modules.base.member_joined_help_voice_channel').format(member.name),
|
||||
self._t.transform('modules.base.member_joined_help_voice_channel').format(member.mention),
|
||||
a,
|
||||
)
|
||||
|
||||
|
26
kdb-bot/src/modules/base/helper/__init__.py
Normal file
26
kdb-bot/src/modules/base/helper/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
bot Keksdose bot
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Discord bot for the Keksdose discord Server
|
||||
|
||||
:copyright: (c) 2022 sh-edraft.de
|
||||
:license: MIT, see LICENSE for more details.
|
||||
|
||||
"""
|
||||
|
||||
__title__ = 'modules.auto_role.helper'
|
||||
__author__ = 'Sven Heidemann'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright (c) 2022 sh-edraft.de'
|
||||
__version__ = '0.3.dev25'
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
# imports:
|
||||
|
||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
||||
version_info = VersionInfo(major='0', minor='3', micro='dev25')
|
52
kdb-bot/src/modules/base/helper/base_reaction_handler.py
Normal file
52
kdb-bot/src/modules/base/helper/base_reaction_handler.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from cpl_core.database.context import DatabaseContextABC
|
||||
from cpl_core.logging import LoggerABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from discord import RawReactionActionEvent
|
||||
|
||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
||||
from modules.base.abc.base_helper_abc import BaseHelperABC
|
||||
from modules.base.configuration.base_server_settings import BaseServerSettings
|
||||
|
||||
|
||||
class BaseReactionHandler:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: LoggerABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
servers: ServerRepositoryABC,
|
||||
users: UserRepositoryABC,
|
||||
base_helper: BaseHelperABC,
|
||||
db: DatabaseContextABC,
|
||||
):
|
||||
self._logger = logger
|
||||
self._bot = bot
|
||||
self._servers = servers
|
||||
self._users = users
|
||||
self._base_helper = base_helper
|
||||
self._db = db
|
||||
|
||||
async def handle(self, payload: RawReactionActionEvent, r_type=None) -> None:
|
||||
self._logger.trace(__name__, f'Handle reaction {payload} {r_type}')
|
||||
|
||||
guild = self._bot.get_guild(payload.guild_id)
|
||||
member = guild.get_member(payload.user_id)
|
||||
if member is None:
|
||||
self._logger.warn(__name__, f'User {payload.user_id} in {guild.name} not found - skipping')
|
||||
return
|
||||
|
||||
server = self._servers.get_server_by_discord_id(guild.id)
|
||||
user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id)
|
||||
settings: BaseServerSettings = self._base_helper.get_config(guild.id)
|
||||
|
||||
if r_type == 'add':
|
||||
user.xp += settings.xp_per_reaction
|
||||
self._users.update_user(user)
|
||||
self._db.save_changes()
|
||||
elif r_type == 'remove':
|
||||
user.xp -= settings.xp_per_reaction
|
||||
self._users.update_user(user)
|
||||
self._db.save_changes()
|
||||
else:
|
||||
self._logger.warn(__name__, f'Invalid reaction type {r_type}')
|
1
kdb-bot/src/modules/base/thread/__init__.py
Normal file
1
kdb-bot/src/modules/base/thread/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports
|
@@ -303,7 +303,7 @@ class LevelGroup(DiscordCommandABC):
|
||||
levels = self._levels.get_levels_by_server_id(server.server_id).order_by(lambda l: l.min_xp)
|
||||
|
||||
if level == levels.first():
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.already_first').format(member.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.already_first').format(member.mention))
|
||||
self._logger.trace(__name__, f'Finished command level down')
|
||||
return
|
||||
|
||||
@@ -312,11 +312,11 @@ class LevelGroup(DiscordCommandABC):
|
||||
user.xp = new_level.min_xp
|
||||
self._users.update_user(user)
|
||||
self._db.save_changes()
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.success').format(member.name, new_level.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.success').format(member.mention, new_level.name))
|
||||
await self._level_service.set_level(user)
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot level down {member.name} with level {level.name}', e)
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.failed').format(member.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.failed').format(member.mention))
|
||||
|
||||
self._logger.trace(__name__, f'Finished command level down')
|
||||
|
||||
@@ -336,7 +336,7 @@ class LevelGroup(DiscordCommandABC):
|
||||
levels = self._levels.get_levels_by_server_id(server.server_id).order_by(lambda l: l.min_xp)
|
||||
|
||||
if level.name == levels.last().name:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.already_last').format(member.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.already_last').format(member.mention))
|
||||
self._logger.trace(__name__, f'Finished command level up')
|
||||
return
|
||||
|
||||
@@ -345,11 +345,11 @@ class LevelGroup(DiscordCommandABC):
|
||||
user.xp = new_level.min_xp
|
||||
self._users.update_user(user)
|
||||
self._db.save_changes()
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.success').format(member.name, new_level.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.success').format(member.mention, new_level.name))
|
||||
await self._level_service.set_level(user)
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot level up {member.name} with level {level.name}', e)
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.failed').format(member.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.failed').format(member.mention))
|
||||
|
||||
self._logger.trace(__name__, f'Finished command level up')
|
||||
|
||||
@@ -374,7 +374,7 @@ class LevelGroup(DiscordCommandABC):
|
||||
return
|
||||
|
||||
if current_level.name == level:
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.already_level').format(member.name, level))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.already_level').format(member.mention, level))
|
||||
self._logger.trace(__name__, f'Finished command level set')
|
||||
return
|
||||
|
||||
@@ -382,11 +382,11 @@ class LevelGroup(DiscordCommandABC):
|
||||
user.xp = new_level.min_xp
|
||||
self._users.update_user(user)
|
||||
self._db.save_changes()
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.success').format(member.name, new_level.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.success').format(member.mention, new_level.name))
|
||||
await self._level_service.set_level(user)
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot set level {level} for {member.name}', e)
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.failed').format(member.name))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.failed').format(member.mention))
|
||||
|
||||
self._logger.trace(__name__, f'Finished command level set')
|
||||
|
||||
|
@@ -0,0 +1,23 @@
|
||||
import discord
|
||||
from cpl_discord.events import OnMemberJoinABC
|
||||
|
||||
from bot_core.helper.event_checks import EventChecks
|
||||
from bot_core.logging.message_logger import MessageLogger
|
||||
from modules.level.service.level_service import LevelService
|
||||
|
||||
|
||||
class LevelOnMemberJoinEvent(OnMemberJoinABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: MessageLogger,
|
||||
level: LevelService
|
||||
):
|
||||
OnMemberJoinABC.__init__(self)
|
||||
self._logger = logger
|
||||
self._level = level
|
||||
|
||||
@EventChecks.check_is_ready()
|
||||
async def on_member_join(self, member: discord.Member):
|
||||
self._logger.debug(__name__, f'Module {type(self)} started')
|
||||
await self._level.check_level(member)
|
@@ -22,9 +22,7 @@
|
||||
"cpl-cli>=2022.10.1"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
|
@@ -9,6 +9,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
||||
from bot_core.abc.module_abc import ModuleABC
|
||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
from modules.level.command.level_group import LevelGroup
|
||||
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_voice_state_update_event import LevelOnVoiceStateUpdateEvent
|
||||
from modules.level.level_seeder import LevelSeeder
|
||||
@@ -36,3 +37,4 @@ class LevelModule(ModuleABC):
|
||||
# events
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_message.value, LevelOnMessageEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, LevelOnVoiceStateUpdateEvent)
|
||||
self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, LevelOnMemberJoinEvent)
|
||||
|
@@ -10,16 +10,26 @@ from bot_data.model.level import Level
|
||||
from bot_data.model.server import Server
|
||||
from bot_data.service.level_repository_service import LevelRepositoryService
|
||||
from modules.level.configuration.default_level_settings import DefaultLevelSettings
|
||||
from modules.level.service.level_service import LevelService
|
||||
|
||||
|
||||
class LevelSeeder(DataSeederABC):
|
||||
|
||||
def __init__(self, logger: DatabaseLogger, levels: DefaultLevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC):
|
||||
def __init__(
|
||||
self,
|
||||
logger: DatabaseLogger,
|
||||
levels: DefaultLevelSettings,
|
||||
level_repo: LevelRepositoryService,
|
||||
servers: ServerRepositoryABC,
|
||||
level: LevelService,
|
||||
bot: DiscordBotServiceABC
|
||||
):
|
||||
DataSeederABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._levels = level_repo
|
||||
self._servers = servers
|
||||
self._level = level
|
||||
self._bot = bot
|
||||
|
||||
self._level_header = levels.level_header
|
||||
@@ -87,4 +97,7 @@ class LevelSeeder(DataSeederABC):
|
||||
except Exception as e:
|
||||
self._logger.error(__name__, f'Cannot change position of {role.name}', e)
|
||||
|
||||
for m in guild.members:
|
||||
await self._level.check_level(m)
|
||||
|
||||
self._logger.debug(__name__, f'Checked role order')
|
||||
|
@@ -88,5 +88,6 @@ class LevelService:
|
||||
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id)
|
||||
if user is None:
|
||||
self._logger.warn(__name__, f'User not found {member.guild.name}@{member.name}')
|
||||
return
|
||||
|
||||
await self.set_level(user)
|
||||
|
@@ -128,8 +128,7 @@ class PermissionService(PermissionServiceABC):
|
||||
return member.guild.id in self._admins and member in self._admins[member.guild.id]
|
||||
|
||||
def is_member_moderator(self, member: discord.Member) -> bool:
|
||||
return member.guild.id in self._moderators \
|
||||
and member in self._moderators[member.guild.id] or self.is_member_admin(member)
|
||||
return member.guild.id in self._moderators and member in self._moderators[member.guild.id] or self.is_member_admin(member)
|
||||
|
||||
def is_member_technician(self, member: discord.Member) -> bool:
|
||||
return member in self._technicians
|
||||
|
@@ -22,9 +22,7 @@
|
||||
"cpl-cli>=2022.10.1"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
|
1
kdb-bot/src/modules/technician/__init__.py
Normal file
1
kdb-bot/src/modules/technician/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports:
|
0
kdb-bot/src/modules/technician/command/__init__.py
Normal file
0
kdb-bot/src/modules/technician/command/__init__.py
Normal file
116
kdb-bot/src/modules/technician/command/log_command.py
Normal file
116
kdb-bot/src/modules/technician/command/log_command.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import os
|
||||
from string import Template
|
||||
from zipfile import ZipFile
|
||||
|
||||
import discord
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
from cpl_core.logging import LoggingSettings
|
||||
from cpl_core.time import TimeFormatSettings
|
||||
from cpl_discord.command import DiscordCommandABC
|
||||
from cpl_query.extension import List
|
||||
from cpl_translation import TranslatePipe
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
|
||||
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||
from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
|
||||
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 modules.permission.abc.permission_service_abc import PermissionServiceABC
|
||||
|
||||
|
||||
class LogCommand(DiscordCommandABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: CommandLogger,
|
||||
logging_settings: LoggingSettings,
|
||||
services: ServiceProviderABC,
|
||||
message_service: MessageServiceABC,
|
||||
client_utils: ClientUtilsServiceABC,
|
||||
translate: TranslatePipe,
|
||||
permissions: PermissionServiceABC,
|
||||
time_format: TimeFormatSettings,
|
||||
env: ApplicationEnvironmentABC
|
||||
):
|
||||
DiscordCommandABC.__init__(self)
|
||||
|
||||
self._logger = logger
|
||||
self._logging_settings = logging_settings
|
||||
self._services = services
|
||||
self._message_service = message_service
|
||||
self._client_utils = client_utils
|
||||
self._t = translate
|
||||
self._permissions = permissions
|
||||
|
||||
self._env = env
|
||||
self._log_settings: LoggingSettings = logging_settings
|
||||
self._time_format_settings: TimeFormatSettings = time_format
|
||||
|
||||
self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}')
|
||||
|
||||
def _reduce_path(self, p: str) -> str:
|
||||
if len(p.split('/')) == 1 or p == '':
|
||||
return p
|
||||
|
||||
return self._reduce_path(os.path.dirname(p))
|
||||
|
||||
@commands.hybrid_command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_technician()
|
||||
async def log(self, ctx: Context):
|
||||
self._logger.debug(__name__, f'Received command log {ctx}')
|
||||
|
||||
possible_log_paths = List(str)
|
||||
possible_log_paths.append(self._reduce_path(self._logging_settings.path))
|
||||
|
||||
file_extensions = List(str)
|
||||
if '.' in self._logging_settings.filename:
|
||||
split_filename = self._logging_settings.filename.split(".")
|
||||
file_extensions.append(f'.{split_filename[len(split_filename) - 1]}')
|
||||
|
||||
for subclass in CustomFileLoggerABC.__subclasses__():
|
||||
logger: CustomFileLoggerABC = self._services.get_service(subclass)
|
||||
if logger is None:
|
||||
continue
|
||||
|
||||
path = self._reduce_path(logger.settings.path)
|
||||
if '.' in logger.settings.filename:
|
||||
split_filename = logger.settings.filename.split(".")
|
||||
file_extension = f'.{split_filename[len(split_filename) - 1]}'
|
||||
if file_extension not in file_extensions:
|
||||
file_extensions.append(file_extension)
|
||||
|
||||
if path in possible_log_paths:
|
||||
continue
|
||||
possible_log_paths.append(path)
|
||||
|
||||
files_str = "\n\t".join(possible_log_paths.to_list())
|
||||
self._logger.debug(__name__, f'Possible log files: \n\t{files_str}')
|
||||
|
||||
files = List(str)
|
||||
for possible_path in possible_log_paths:
|
||||
for r, d, f in os.walk(possible_path):
|
||||
for file in f:
|
||||
if '.' not in file:
|
||||
continue
|
||||
|
||||
split_filename = file.split(".")
|
||||
if f'.{split_filename[len(split_filename) - 1]}' not in file_extensions:
|
||||
continue
|
||||
|
||||
files.append(os.path.join(r, file))
|
||||
|
||||
files_str = "\n\t".join(files.to_list())
|
||||
self._logger.debug(__name__, f'Log files: \n\t{files_str}')
|
||||
|
||||
zip_file = ZipFile('logs.zip', 'w')
|
||||
files.for_each(lambda x: zip_file.write(x))
|
||||
zip_file.close()
|
||||
await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.technician.log_message'), file=discord.File(zip_file.filename, 'logs.zip'), ephemeral=True)
|
||||
os.remove(zip_file.filename)
|
||||
|
||||
self._logger.trace(__name__, f'Finished log command')
|
@@ -44,13 +44,13 @@ class RestartCommand(DiscordCommandABC):
|
||||
@commands.hybrid_command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_moderator()
|
||||
@CommandChecks.check_is_member_technician()
|
||||
async def restart(self, ctx: Context):
|
||||
self._logger.debug(__name__, f'Received command restart {ctx}')
|
||||
|
||||
self._config.add_configuration('IS_RESTART', 'true')
|
||||
await self._client_utils.presence_game('common.presence.restart')
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.restart_message'))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.technician.restart_message'))
|
||||
await asyncio.sleep(self._settings.wait_for_restart)
|
||||
await self._bot.stop_async()
|
||||
|
@@ -45,12 +45,12 @@ class ShutdownCommand(DiscordCommandABC):
|
||||
@commands.hybrid_command()
|
||||
@commands.guild_only()
|
||||
@CommandChecks.check_is_ready()
|
||||
@CommandChecks.check_is_member_moderator()
|
||||
@CommandChecks.check_is_member_technician()
|
||||
async def shutdown(self, ctx: Context):
|
||||
self._logger.debug(__name__, f'Received command shutdown {ctx}')
|
||||
|
||||
await self._client_utils.presence_game('common.presence.shutdown')
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.shutdown_message'))
|
||||
await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.technician.shutdown_message'))
|
||||
await asyncio.sleep(self._settings.wait_for_shutdown)
|
||||
await self._bot.stop_async()
|
||||
|
44
kdb-bot/src/modules/technician/technician.json
Normal file
44
kdb-bot/src/modules/technician/technician.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "technician",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0.post7"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.6",
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "library",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "technician.main",
|
||||
"EntryPoint": "technician",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
29
kdb-bot/src/modules/technician/technician_module.py
Normal file
29
kdb-bot/src/modules/technician/technician_module.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
||||
|
||||
from bot_core.abc.module_abc import ModuleABC
|
||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||
from modules.base.abc.base_helper_abc import BaseHelperABC
|
||||
from modules.technician.command.log_command import LogCommand
|
||||
from modules.technician.command.restart_command import RestartCommand
|
||||
from modules.technician.command.shutdown_command import ShutdownCommand
|
||||
from modules.base.service.base_helper_service import BaseHelperService
|
||||
|
||||
|
||||
class TechnicianModule(ModuleABC):
|
||||
|
||||
def __init__(self, dc: DiscordCollectionABC):
|
||||
ModuleABC.__init__(self, dc, FeatureFlagsEnum.base_module)
|
||||
|
||||
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
|
||||
pass
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
services.add_transient(BaseHelperABC, BaseHelperService)
|
||||
# commands
|
||||
self._dc.add_command(RestartCommand)
|
||||
self._dc.add_command(ShutdownCommand)
|
||||
self._dc.add_command(LogCommand)
|
||||
# events
|
1
kdb-bot/test/ui_tests/__init__.py
Normal file
1
kdb-bot/test/ui_tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports:
|
16
kdb-bot/test/ui_tests/config/appsettings.edrafts-pc.json
Normal file
16
kdb-bot/test/ui_tests/config/appsettings.edrafts-pc.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"DatabaseSettings": {
|
||||
"Host": "localhost",
|
||||
"User": "kd_kdb",
|
||||
"Password": "",
|
||||
"Database": "keksdose_bot_dev",
|
||||
"Charset": "utf8mb4",
|
||||
"UseUnicode": "true",
|
||||
"Buffered": "true",
|
||||
"AuthPlugin": "mysql_native_password"
|
||||
},
|
||||
"DiscordBot": {
|
||||
"Token": "",
|
||||
"Prefix": "!kab-e "
|
||||
}
|
||||
}
|
54
kdb-bot/test/ui_tests/config/appsettings.json
Normal file
54
kdb-bot/test/ui_tests/config/appsettings.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"TimeFormatSettings": {
|
||||
"DateFormat": "%Y-%m-%d",
|
||||
"TimeFormat": "%H:%M:%S",
|
||||
"DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
|
||||
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
|
||||
},
|
||||
"LoggingSettings": {
|
||||
"Path": "logs/$date_now/",
|
||||
"Filename": "bot.log",
|
||||
"ConsoleLogLevel": "ERROR",
|
||||
"FileLogLevel": "WARN"
|
||||
},
|
||||
"BotLoggingSettings": {
|
||||
"Api": {
|
||||
"Path": "logs/$date_now/",
|
||||
"Filename": "api.log",
|
||||
"ConsoleLogLevel": "INFO",
|
||||
"FileLogLevel": "DEBUG"
|
||||
},
|
||||
"Command": {
|
||||
"Path": "logs/$date_now/",
|
||||
"Filename": "commands.log",
|
||||
"ConsoleLogLevel": "INFO",
|
||||
"FileLogLevel": "DEBUG"
|
||||
},
|
||||
"Database": {
|
||||
"Path": "logs/$date_now/",
|
||||
"Filename": "database.log",
|
||||
"ConsoleLogLevel": "INFO",
|
||||
"FileLogLevel": "DEBUG"
|
||||
},
|
||||
"Message": {
|
||||
"Path": "logs/$date_now/",
|
||||
"Filename": "message.log",
|
||||
"ConsoleLogLevel": "INFO",
|
||||
"FileLogLevel": "DEBUG"
|
||||
}
|
||||
},
|
||||
"Translation": {
|
||||
"DefaultLanguage": "de",
|
||||
"Languages": [
|
||||
"de"
|
||||
]
|
||||
},
|
||||
"TestSettings": {
|
||||
"LoginUrl": "https://discord.com/login",
|
||||
"MePageUrl": "https://discord.com/channels/@me",
|
||||
"CmdURL": "https://discord.com/channels/910199451145076828/911578636899987526",
|
||||
"GuildId": 910199451145076828,
|
||||
"BotId": 998159802393964594,
|
||||
"TestUserId": 401941112010571777
|
||||
}
|
||||
}
|
34
kdb-bot/test/ui_tests/main.py
Normal file
34
kdb-bot/test/ui_tests/main.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import asyncio
|
||||
|
||||
from cpl_core.application import ApplicationABC
|
||||
from cpl_core.application import ApplicationBuilder
|
||||
|
||||
from startup_test_extension import StartupTestExtension
|
||||
from ui_tests.startup import Startup
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
|
||||
try:
|
||||
from test_application import TestApplication
|
||||
except ImportError:
|
||||
from .test_application import TestApplication
|
||||
|
||||
|
||||
def get_app() -> ApplicationABC:
|
||||
app_builder = ApplicationBuilder(TestApplication) \
|
||||
.use_extension(StartupTestExtension) \
|
||||
.use_startup(Startup)
|
||||
app = app_builder.build()
|
||||
Declarations.app = app
|
||||
return app
|
||||
|
||||
|
||||
async def main():
|
||||
app = get_app()
|
||||
await app.run_async()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
asyncio.run(main())
|
118
kdb-bot/test/ui_tests/startup.py
Normal file
118
kdb-bot/test/ui_tests/startup.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.application import StartupABC
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.database import DatabaseSettings
|
||||
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_core.environment import ApplicationEnvironment
|
||||
from cpl_discord import get_discord_collection
|
||||
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from bot.startup_settings_extension import StartupSettingsExtension
|
||||
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||
from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
|
||||
from bot_core.abc.message_service_abc import MessageServiceABC
|
||||
from bot_core.configuration.bot_logging_settings import BotLoggingSettings
|
||||
from bot_core.logging.command_logger import CommandLogger
|
||||
from bot_core.logging.database_logger import DatabaseLogger
|
||||
from bot_core.logging.message_logger import MessageLogger
|
||||
from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe
|
||||
from bot_core.service.client_utils_service import ClientUtilsService
|
||||
from bot_core.service.message_service import MessageService
|
||||
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
|
||||
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.statistic_repository_abc import StatisticRepositoryABC
|
||||
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
|
||||
from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC
|
||||
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
||||
from bot_data.db_context import DBContext
|
||||
from bot_data.service.auth_user_repository_service import AuthUserRepositoryService
|
||||
from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService
|
||||
from bot_data.service.client_repository_service import ClientRepositoryService
|
||||
from bot_data.service.known_user_repository_service import KnownUserRepositoryService
|
||||
from bot_data.service.level_repository_service import LevelRepositoryService
|
||||
from bot_data.service.seeder_service import SeederService
|
||||
from bot_data.service.server_repository_service import ServerRepositoryService
|
||||
from bot_data.service.statistic_repository_service import StatisticRepositoryService
|
||||
from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService
|
||||
from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService
|
||||
from bot_data.service.user_repository_service import UserRepositoryService
|
||||
from ui_tests.test_on_ready_event import TestOnReadyEvent
|
||||
|
||||
|
||||
class Startup(StartupABC):
|
||||
|
||||
def __init__(self):
|
||||
StartupABC.__init__(self)
|
||||
|
||||
self._config: Optional[ConfigurationABC] = None
|
||||
|
||||
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC:
|
||||
load_dotenv()
|
||||
configuration.add_environment_variables('KDB_TEST_')
|
||||
configuration.add_environment_variables('DISCORD_')
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
configuration.add_json_file(f'{cwd}/config/appsettings.json', optional=False)
|
||||
configuration.add_json_file(f'{cwd}/config/appsettings.{environment.host_name}.json', optional=True)
|
||||
|
||||
StartupSettingsExtension._configure_settings_with_sub_settings(configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key)
|
||||
|
||||
self._config = configuration
|
||||
return configuration
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
|
||||
services.add_logging()
|
||||
services.add_singleton(CustomFileLoggerABC, CommandLogger)
|
||||
services.add_singleton(CustomFileLoggerABC, DatabaseLogger)
|
||||
services.add_singleton(CustomFileLoggerABC, MessageLogger)
|
||||
|
||||
services.add_translation()
|
||||
db_settings: DatabaseSettings = self._config.get_configuration(DatabaseSettings)
|
||||
db_settings_with_pw = DatabaseSettings()
|
||||
pw = self._config.get_configuration('DB_PASSWORD')
|
||||
db_settings_with_pw.from_dict({
|
||||
"Host": db_settings.host,
|
||||
"User": db_settings.user,
|
||||
"Password": '' if pw is None else pw,
|
||||
"Database": db_settings.database,
|
||||
"Charset": db_settings.charset,
|
||||
"UseUnicode": db_settings.use_unicode,
|
||||
"Buffered": db_settings.buffered,
|
||||
"AuthPlugin": db_settings.auth_plugin
|
||||
})
|
||||
services.add_db_context(DBContext, db_settings_with_pw)
|
||||
|
||||
services.add_discord()
|
||||
dc = get_discord_collection(services)
|
||||
dc.add_event(DiscordEventTypesEnum.on_ready.value, TestOnReadyEvent)
|
||||
|
||||
# bot_core stuff
|
||||
services.add_transient(MessageServiceABC, MessageService)
|
||||
services.add_transient(ClientUtilsServiceABC, ClientUtilsService)
|
||||
|
||||
# pipes
|
||||
services.add_transient(DateTimeOffsetPipe)
|
||||
|
||||
# data stuff
|
||||
services.add_transient(AuthUserRepositoryABC, AuthUserRepositoryService)
|
||||
services.add_transient(ServerRepositoryABC, ServerRepositoryService)
|
||||
services.add_transient(UserRepositoryABC, UserRepositoryService)
|
||||
services.add_transient(ClientRepositoryABC, ClientRepositoryService)
|
||||
services.add_transient(KnownUserRepositoryABC, KnownUserRepositoryService)
|
||||
services.add_transient(UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService)
|
||||
services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService)
|
||||
services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService)
|
||||
services.add_transient(LevelRepositoryABC, LevelRepositoryService)
|
||||
services.add_transient(StatisticRepositoryABC, StatisticRepositoryService)
|
||||
|
||||
services.add_transient(SeederService)
|
||||
|
||||
return services.build_service_provider()
|
20
kdb-bot/test/ui_tests/startup_test_extension.py
Normal file
20
kdb-bot/test/ui_tests/startup_test_extension.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
|
||||
from cpl_core.application import StartupExtensionABC
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
|
||||
|
||||
class StartupTestExtension(StartupExtensionABC):
|
||||
|
||||
def __init__(self):
|
||||
StartupExtensionABC.__init__(self)
|
||||
|
||||
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC):
|
||||
# this shit has to be done here because we need settings in subsequent startup extensions
|
||||
environment.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
|
||||
environment.set_working_directory('../../src/bot/')
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
pass
|
64
kdb-bot/test/ui_tests/test_application.py
Normal file
64
kdb-bot/test/ui_tests/test_application.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_discord.application import DiscordBotApplicationABC
|
||||
from cpl_discord.configuration import DiscordBotSettings
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslationSettings, TranslationServiceABC
|
||||
|
||||
from bot_core.configuration.bot_settings import BotSettings
|
||||
|
||||
|
||||
class TestApplication(DiscordBotApplicationABC):
|
||||
|
||||
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
|
||||
DiscordBotApplicationABC.__init__(self, config, services)
|
||||
|
||||
self._config = config
|
||||
self._services = services
|
||||
|
||||
self._bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC)
|
||||
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
|
||||
|
||||
discord_bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings)
|
||||
self._discord_settings = self._get_settings(discord_bot_settings)
|
||||
|
||||
def _get_settings(self, settings_from_config: DiscordBotSettings) -> DiscordBotSettings:
|
||||
new_settings = DiscordBotSettings()
|
||||
token = None if settings_from_config is None else settings_from_config.token
|
||||
prefix = None if settings_from_config is None else settings_from_config.prefix
|
||||
env_token = self._config.get_configuration('TOKEN')
|
||||
env_prefix = self._config.get_configuration('PREFIX')
|
||||
|
||||
new_settings.from_dict({
|
||||
'Token': env_token if token is None or token == '' else token,
|
||||
'Prefix': ('! ' if self._is_string_invalid(env_prefix) else env_prefix) if self._is_string_invalid(prefix) else prefix
|
||||
})
|
||||
if new_settings.token is None or new_settings.token == '':
|
||||
raise Exception('You have to configure discord token by appsettings or environment variables')
|
||||
return new_settings
|
||||
|
||||
@staticmethod
|
||||
def _is_string_invalid(x):
|
||||
return x is None or x == ''
|
||||
|
||||
@property
|
||||
def config(self) -> ConfigurationABC:
|
||||
return self._config
|
||||
|
||||
@property
|
||||
def services(self) -> ServiceProviderABC:
|
||||
return self._services
|
||||
|
||||
async def configure(self):
|
||||
self._translation.load_by_settings(self._configuration.get_configuration(TranslationSettings))
|
||||
|
||||
async def main(self):
|
||||
if self._config.get_configuration('IS_UNITTEST'):
|
||||
await self._bot.login(self._discord_settings.token)
|
||||
self._bot.loop.create_task(self._bot.connect())
|
||||
return
|
||||
|
||||
await self._bot.start_async()
|
||||
|
||||
async def stop_async(self):
|
||||
await self._bot.close()
|
47
kdb-bot/test/ui_tests/test_on_ready_event.py
Normal file
47
kdb-bot/test/ui_tests/test_on_ready_event.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.console import Console, ForegroundColorEnum
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from cpl_core.logging import LoggerABC
|
||||
from cpl_discord.events import OnReadyABC
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
|
||||
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||
|
||||
|
||||
class TestOnReadyEvent(OnReadyABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: ConfigurationABC,
|
||||
bot: DiscordBotServiceABC,
|
||||
services: ServiceProviderABC,
|
||||
client_utils: ClientUtilsServiceABC,
|
||||
t: TranslatePipe
|
||||
):
|
||||
OnReadyABC.__init__(self)
|
||||
|
||||
self._config = config
|
||||
self._bot = bot
|
||||
self._services = services
|
||||
self._client_utils = client_utils
|
||||
self._t = t
|
||||
|
||||
async def on_ready(self):
|
||||
if self._config.get_configuration('IS_UNITTEST'):
|
||||
return
|
||||
|
||||
Console.write_line('\nStarting tests:\n')
|
||||
loader = unittest.TestLoader()
|
||||
path = f'{os.path.dirname(os.path.realpath(__file__))}/../'
|
||||
tests = loader.discover(path, pattern='*_test_case.py')
|
||||
runner = unittest.TextTestRunner()
|
||||
runner.run(tests)
|
||||
# for cls in CommandTestABC.__subclasses__():
|
||||
# service: CommandTestABC = self._services.get_service(cls)
|
||||
# await service.run(self._tests)
|
||||
|
||||
await self._bot.close()
|
49
kdb-bot/test/ui_tests/ui-tests.json
Normal file
49
kdb-bot/test/ui_tests/ui-tests.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "ui-tests",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0.post9",
|
||||
"PyNaCl==1.5.0",
|
||||
"cffi==1.15.1",
|
||||
"pycparser==2.21",
|
||||
"selenium==4.6.1",
|
||||
"webdriver-manager==3.8.5"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "ui_tests_shared.main",
|
||||
"EntryPoint": "ui-tests-shared",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
1
kdb-bot/test/ui_tests_shared/__init__.py
Normal file
1
kdb-bot/test/ui_tests_shared/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports:
|
15
kdb-bot/test/ui_tests_shared/command_selectors_enum.py
Normal file
15
kdb-bot/test/ui_tests_shared/command_selectors_enum.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class CommandSelectorsEnum(Enum):
|
||||
cmd_chat = '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div[2]/main/form/div/div[1]/div/div[3]/div/div[2]/div'
|
||||
cmd_chat_input = '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div/main/form/div/div[2]/div/div[2]/div/div'
|
||||
msg_input = '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div[2]/main/form/div/div[1]/div/div[3]/div/div/div'
|
||||
|
||||
kdb_test = "//*[contains (text(), 'Krümmelmonster-test')]"
|
||||
|
||||
ping = kdb_test
|
||||
info = kdb_test
|
||||
help = kdb_test
|
||||
afk = kdb_test
|
||||
purge = kdb_test
|
62
kdb-bot/test/ui_tests_shared/command_test_case_with_app.py
Normal file
62
kdb-bot/test/ui_tests_shared/command_test_case_with_app.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
from selenium.common import NoSuchElementException, StaleElementReferenceException
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.test_case_with_app import TestCaseWithApp
|
||||
from ui_tests_shared.ui import UI
|
||||
|
||||
|
||||
class CommandTestCaseWithApp(TestCaseWithApp):
|
||||
_cmd: WebElement
|
||||
|
||||
_bot: Optional[DiscordBotServiceABC] = None
|
||||
_t: Optional[TranslatePipe] = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
TestCaseWithApp.setUpClass()
|
||||
cls._bot = cls._services.get_service(DiscordBotServiceABC)
|
||||
cls._t = cls._services.get_service(TranslatePipe)
|
||||
|
||||
@classmethod
|
||||
def send_message(cls, msg: str):
|
||||
if UI.driver.current_url != cls._test_settings.cmd_url:
|
||||
UI.driver.get(cls._test_settings.cmd_url)
|
||||
time.sleep(2)
|
||||
|
||||
cmd_element_ident = (By.XPATH, CommandSelectorsEnum.cmd_chat.value)
|
||||
cmd_element = UI.driver.find_element(*cmd_element_ident)
|
||||
cmd_element.send_keys(msg)
|
||||
time.sleep(2)
|
||||
UI.driver.find_element(By.XPATH, CommandSelectorsEnum.msg_input.value).send_keys(Keys.ENTER)
|
||||
|
||||
@classmethod
|
||||
def send_command(cls, cmd: str, selector: CommandSelectorsEnum, reload=True):
|
||||
if reload or UI.driver.current_url != cls._test_settings.cmd_url:
|
||||
UI.driver.get(cls._test_settings.cmd_url)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
cmd_element_ident = (By.XPATH, CommandSelectorsEnum.cmd_chat.value)
|
||||
cmd_element = UI.driver.find_element(*cmd_element_ident)
|
||||
cmd_element.send_keys(f'/{cmd}')
|
||||
time.sleep(2)
|
||||
|
||||
ignored_exceptions = (NoSuchElementException, StaleElementReferenceException,)
|
||||
WebDriverWait(UI.driver, 20, ignored_exceptions=ignored_exceptions).until(
|
||||
expected_conditions.presence_of_element_located((
|
||||
By.XPATH,
|
||||
selector.value
|
||||
))
|
||||
).click()
|
||||
time.sleep(2)
|
||||
UI.driver.find_element(By.XPATH, CommandSelectorsEnum.cmd_chat_input.value).send_keys(Keys.ENTER)
|
1
kdb-bot/test/ui_tests_shared/configuration/__init__.py
Normal file
1
kdb-bot/test/ui_tests_shared/configuration/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports
|
53
kdb-bot/test/ui_tests_shared/configuration/test_settings.py
Normal file
53
kdb-bot/test/ui_tests_shared/configuration/test_settings.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class TestSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._login_url = ''
|
||||
self._me_page_url = ''
|
||||
self._cmd_url = ''
|
||||
self._guild_id = 0
|
||||
self._bot_id = 0
|
||||
self._test_user_id = 0
|
||||
|
||||
@property
|
||||
def login_url(self) -> str:
|
||||
return self._login_url
|
||||
|
||||
@property
|
||||
def me_page_url(self) -> str:
|
||||
return self._me_page_url
|
||||
|
||||
@property
|
||||
def cmd_url(self) -> str:
|
||||
return self._cmd_url
|
||||
|
||||
@property
|
||||
def guild_id(self) -> int:
|
||||
return self._guild_id
|
||||
|
||||
@property
|
||||
def bot_id(self) -> int:
|
||||
return self._bot_id
|
||||
|
||||
@property
|
||||
def test_user_id(self) -> int:
|
||||
return self._test_user_id
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._login_url = settings['LoginUrl']
|
||||
self._me_page_url = settings['MePageUrl']
|
||||
self._cmd_url = settings['CmdURL']
|
||||
self._guild_id = settings['GuildId']
|
||||
self._bot_id = settings['BotId']
|
||||
self._test_user_id = settings['TestUserId']
|
||||
except Exception as e:
|
||||
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
|
||||
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
|
12
kdb-bot/test/ui_tests_shared/declarations.py
Normal file
12
kdb-bot/test/ui_tests_shared/declarations.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
|
||||
from ui_tests.test_application import TestApplication
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
|
||||
|
||||
class Declarations:
|
||||
app: Optional[TestApplication] = None
|
||||
env: Optional[ApplicationEnvironmentABC] = None
|
||||
test_settings: Optional[TestSettings] = None
|
18
kdb-bot/test/ui_tests_shared/decorators.py
Normal file
18
kdb-bot/test/ui_tests_shared/decorators.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
class Async:
|
||||
_loop = None
|
||||
|
||||
@classmethod
|
||||
def async_func(cls, coro):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
if cls._loop is None:
|
||||
cls._loop = asyncio.get_event_loop()
|
||||
|
||||
return cls._loop.run_until_complete(coro(*args, **kwargs))
|
||||
except Exception as e:
|
||||
return
|
||||
|
||||
return wrapper
|
44
kdb-bot/test/ui_tests_shared/test_case_with_app.py
Normal file
44
kdb-bot/test/ui_tests_shared/test_case_with_app.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import asyncio
|
||||
import time
|
||||
import unittest
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
|
||||
from ui_tests.main import get_app
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
from ui_tests_shared.ui import UI
|
||||
|
||||
|
||||
class TestCaseWithApp(unittest.TestCase):
|
||||
_config: Optional[ConfigurationABC] = None
|
||||
_services: Optional[ServiceProviderABC] = None
|
||||
_test_settings: Optional[TestSettings] = None
|
||||
_loop = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
if Declarations.app is None:
|
||||
app = get_app()
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
if cls._loop is None:
|
||||
cls._loop = asyncio.get_event_loop()
|
||||
|
||||
cls._loop.run_until_complete(app.run_async())
|
||||
# asyncio.run(app.run_async())
|
||||
|
||||
cls._config = Declarations.app.config
|
||||
cls._services = Declarations.app.services
|
||||
cls._test_settings: TestSettings = cls._config.get_configuration(TestSettings)
|
||||
UI.set_settings(cls._test_settings)
|
||||
UI.login(cls._config.get_configuration('DISCORD_MAIL'), cls._config.get_configuration('DISCORD_PASSWORD'))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if cls._config.get_configuration('IS_UNITTEST'):
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(Declarations.app.stop_async())
|
44
kdb-bot/test/ui_tests_shared/ui-tests-shared.json
Normal file
44
kdb-bot/test/ui_tests_shared/ui-tests-shared.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "ui-tests-shared",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0.post9"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "ui_tests_shared.main",
|
||||
"EntryPoint": "ui-tests-shared",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
69
kdb-bot/test/ui_tests_shared/ui.py
Normal file
69
kdb-bot/test/ui_tests_shared/ui.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import time
|
||||
import unittest
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from ui_tests.main import get_app
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
|
||||
|
||||
class UI:
|
||||
_test_settings: Optional[TestSettings] = None
|
||||
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_argument("user-data-dir=selenium-data")
|
||||
options.add_experimental_option('useAutomationExtension', False)
|
||||
options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
options.add_experimental_option("prefs", {
|
||||
"profile.default_content_setting_values.media_stream_mic": 1,
|
||||
"profile.default_content_setting_values.media_stream_camera": 1,
|
||||
"profile.default_content_setting_values.geolocation": 1,
|
||||
"profile.default_content_setting_values.notifications": 1
|
||||
})
|
||||
driver = webdriver.Chrome(options=options)
|
||||
|
||||
_is_logged_in = False
|
||||
|
||||
@classmethod
|
||||
def set_settings(cls, test_settings: TestSettings):
|
||||
cls._test_settings = test_settings
|
||||
|
||||
@classmethod
|
||||
def login(cls, mail: str, password: str):
|
||||
if cls._is_logged_in:
|
||||
return
|
||||
|
||||
# use full xpath: https://stackoverflow.com/questions/71179006/how-can-selenium-python-chrome-find-web-elements-visible-in-dev-tools-but-no
|
||||
|
||||
cls.driver.get(cls._test_settings.login_url)
|
||||
|
||||
try:
|
||||
WebDriverWait(cls.driver, 10).until(expected_conditions.url_matches(cls._test_settings.me_page_url))
|
||||
cls._is_logged_in = True
|
||||
return
|
||||
except Exception as e:
|
||||
WebDriverWait(cls.driver, 20).until(expected_conditions.presence_of_element_located((By.NAME, 'email')))
|
||||
|
||||
mail_element = cls.driver.find_element(By.NAME, 'email')
|
||||
mail_element.clear()
|
||||
mail_element.send_keys(mail)
|
||||
mail_element.send_keys(Keys.RETURN)
|
||||
|
||||
pw_element = cls.driver.find_element(By.NAME, 'password')
|
||||
pw_element.clear()
|
||||
pw_element.send_keys(password)
|
||||
pw_element.send_keys(Keys.RETURN)
|
||||
time.sleep(1)
|
||||
pw_element.send_keys(Keys.RETURN)
|
||||
|
||||
WebDriverWait(cls.driver, 20).until(expected_conditions.url_matches(cls._test_settings.me_page_url))
|
||||
time.sleep(4)
|
||||
cls._is_logged_in = True
|
1
kdb-bot/test/ui_tests_tests/__init__.py
Normal file
1
kdb-bot/test/ui_tests_tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# imports:
|
15
kdb-bot/test/ui_tests_tests/appsettings.json
Normal file
15
kdb-bot/test/ui_tests_tests/appsettings.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"TimeFormatSettings": {
|
||||
"DateFormat": "%Y-%m-%d",
|
||||
"TimeFormat": "%H:%M:%S",
|
||||
"DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
|
||||
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
|
||||
},
|
||||
|
||||
"LoggingSettings": {
|
||||
"Path": "logs/",
|
||||
"Filename": "log_$start_time.log",
|
||||
"ConsoleLogLevel": "ERROR",
|
||||
"FileLogLevel": "WARN"
|
||||
}
|
||||
}
|
0
kdb-bot/test/ui_tests_tests/commands/__init__.py
Normal file
0
kdb-bot/test/ui_tests_tests/commands/__init__.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import discord
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
from ui_tests_shared.decorators import Async
|
||||
from ui_tests_shared.ui import UI
|
||||
|
||||
|
||||
class AFKCommandTestCase(CommandTestCaseWithApp):
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
btn = UI.driver.find_elements(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[1]/section/div[1]/div/div[1]/div[2]/button')
|
||||
if len(btn) == 0:
|
||||
return
|
||||
|
||||
btn[0].click()
|
||||
|
||||
@Async.async_func
|
||||
async def test_error_message(self):
|
||||
correct_response = self._t.transform('modules.base.afk_command_channel_missing_message')
|
||||
self.assertIsNotNone(correct_response)
|
||||
self.send_command('afk', CommandSelectorsEnum.afk, reload=False)
|
||||
|
||||
def check(m: discord.Message):
|
||||
return m.author.id == self._test_settings.bot_id
|
||||
|
||||
response = await self._bot.wait_for('message', check=check, timeout=10)
|
||||
self.assertEqual(response.content, correct_response)
|
||||
|
||||
@Async.async_func
|
||||
async def test_move(self):
|
||||
# correct_response = self._t.transform('modules.base.pong')
|
||||
# self.assertIsNotNone(correct_response)
|
||||
UI.driver.find_element(By.XPATH, '//*[@id="channels"]/ul/li[20]/div/div/div/a').click()
|
||||
def check1(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
|
||||
return member.id == self._test_settings.test_user_id and after is not None and after.channel.id == 911578760476762153
|
||||
res = await self._bot.wait_for('voice_state_update', check=check1, timeout=10)
|
||||
|
||||
self.send_command('afk', CommandSelectorsEnum.afk, reload=False)
|
||||
|
||||
def check2(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
|
||||
return member.id == self._test_settings.test_user_id and after is not None and after.channel.id == 910199452915093594
|
||||
member, before, after = await self._bot.wait_for('voice_state_update', check=check2, timeout=10)
|
||||
self.assertIsNotNone(after.channel)
|
||||
self.assertEqual(after.channel.id, 910199452915093594)
|
||||
# self.assertEqual(response.content, correct_response)
|
@@ -0,0 +1,20 @@
|
||||
import discord
|
||||
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
from ui_tests_shared.decorators import Async
|
||||
|
||||
|
||||
class HelpCommandTestCase(CommandTestCaseWithApp):
|
||||
|
||||
@Async.async_func
|
||||
async def test_help(self):
|
||||
correct_response = 'https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle'
|
||||
self.send_command('help', CommandSelectorsEnum.help)
|
||||
|
||||
def check(m: discord.Message):
|
||||
return m.author.id == self._test_settings.bot_id
|
||||
|
||||
response = await self._bot.wait_for('message', check=check, timeout=10)
|
||||
self.assertEqual(response.content, correct_response)
|
||||
await self._bot.close()
|
@@ -0,0 +1,53 @@
|
||||
from datetime import datetime
|
||||
from unittest import skip
|
||||
|
||||
import discord
|
||||
|
||||
import bot
|
||||
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
from ui_tests_shared.decorators import Async
|
||||
|
||||
|
||||
class InfoCommandTestCase(CommandTestCaseWithApp):
|
||||
|
||||
def get_embed(self) -> discord.Embed:
|
||||
client_utils: ClientUtilsServiceABC = self._services.get_service(ClientUtilsServiceABC)
|
||||
client = client_utils.get_client(self._test_settings.bot_id, self._test_settings.guild_id)
|
||||
embed = discord.Embed(
|
||||
title=self._t.transform('modules.base.info.title'),
|
||||
description=self._t.transform('modules.base.info.description'),
|
||||
color=int('ef9d0d', 16)
|
||||
)
|
||||
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.version'), value=bot.__version__)
|
||||
# start_time = self._config.get_configuration('Bot_StartTime')
|
||||
start_time = str(datetime.now())
|
||||
ontime = round((datetime.now() - datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S.%f')).total_seconds() / 3600, 2)
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.ontime'), value=f'{ontime}h')
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.sent_message_count'), value=client.sent_message_count, inline=False)
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.received_message_count'), value=client.received_message_count)
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.deleted_message_count'), value=client.deleted_message_count, inline=False)
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.received_command_count'), value=client.received_command_count)
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.moved_users_count'), value=client.moved_users_count)
|
||||
from bot.module_list import ModuleList
|
||||
modules = ModuleList.get_modules()
|
||||
modules = modules.select(lambda x: x.__name__.replace('Module', ''))
|
||||
embed.add_field(name=self._t.transform('modules.base.info.fields.modules'), value='\n'.join(modules), inline=False)
|
||||
return embed
|
||||
|
||||
@skip('stage db not ready yet')
|
||||
@Async.async_func
|
||||
async def test_info(self):
|
||||
correct_response = self.get_embed()
|
||||
self.send_command('info', CommandSelectorsEnum.info)
|
||||
|
||||
def check(m: discord.Message):
|
||||
return m.embeds[0] == correct_response and m.author.id == self._test_settings.bot_id
|
||||
|
||||
response: discord.Message = await self._bot.wait_for('message', check=check, timeout=10)
|
||||
self.assertEqual(len(response.embeds), 1)
|
||||
|
||||
self.assertEqual(response.embeds[0], correct_response)
|
||||
await self._bot.close()
|
@@ -0,0 +1,20 @@
|
||||
import discord
|
||||
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
from ui_tests_shared.decorators import Async
|
||||
|
||||
|
||||
class PingCommandTestCase(CommandTestCaseWithApp):
|
||||
|
||||
@Async.async_func
|
||||
async def test_ping(self):
|
||||
correct_response = self._t.transform('modules.base.pong')
|
||||
self.assertIsNotNone(correct_response)
|
||||
self.send_command('ping', CommandSelectorsEnum.ping)
|
||||
|
||||
def check(m: discord.Message):
|
||||
return m.author.id == self._test_settings.bot_id
|
||||
|
||||
response = await self._bot.wait_for('message', check=check, timeout=10)
|
||||
self.assertEqual(response.content, correct_response)
|
@@ -0,0 +1,34 @@
|
||||
import time
|
||||
|
||||
import discord
|
||||
|
||||
from ui_tests_shared.command_selectors_enum import CommandSelectorsEnum
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
from ui_tests_shared.decorators import Async
|
||||
|
||||
|
||||
class PurgeCommandTestCase(CommandTestCaseWithApp):
|
||||
|
||||
def setUp(self):
|
||||
self.send_message('test nachricht 1')
|
||||
self.send_message('test nachricht 2')
|
||||
self.send_message('test nachricht 3')
|
||||
self.send_message('test nachricht 4')
|
||||
|
||||
@Async.async_func
|
||||
async def test_purge(self):
|
||||
correct_response = self._t.transform('modules.moderator.purge_message')
|
||||
self.assertIsNotNone(correct_response)
|
||||
self.send_command('purge', CommandSelectorsEnum.purge)
|
||||
|
||||
def check(m: discord.Message):
|
||||
return m.author.id == self._test_settings.bot_id
|
||||
|
||||
response = await self._bot.wait_for('message', check=check, timeout=10)
|
||||
self.assertEqual(response.content, correct_response)
|
||||
time.sleep(20)
|
||||
channel = self._bot.get_channel(911578680998895687)
|
||||
message_count = 0
|
||||
async for _ in channel.history(limit=None):
|
||||
message_count += 1
|
||||
self.assertEqual(message_count, 0)
|
44
kdb-bot/test/ui_tests_tests/ui-tests-tests.json
Normal file
44
kdb-bot/test/ui_tests_tests/ui-tests-tests.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "ui-tests-tests",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0.post9"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "ui_tests__tests.main",
|
||||
"EntryPoint": "ui-tests_tests",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
@@ -46,7 +46,7 @@ class Application(ApplicationABC):
|
||||
return
|
||||
|
||||
if len(args) == 1:
|
||||
suffix = f'.{args[0]}'
|
||||
suffix = args[0]
|
||||
|
||||
try:
|
||||
branch = self._git_service.get_active_branch_name()
|
||||
@@ -67,8 +67,12 @@ class Application(ApplicationABC):
|
||||
version[VersionSettingsNameEnum.major.value] = branch.split('.')[0]
|
||||
version[VersionSettingsNameEnum.minor.value] = branch.split('.')[1]
|
||||
if len(branch.split('.')) == 2:
|
||||
version[VersionSettingsNameEnum.micro.value] = f'0{suffix}'
|
||||
if suffix == '':
|
||||
suffix = '0'
|
||||
version[VersionSettingsNameEnum.micro.value] = f'{suffix}'
|
||||
else:
|
||||
if not suffix.startswith('.') and suffix != '':
|
||||
suffix = f'.{suffix}'
|
||||
version[VersionSettingsNameEnum.micro.value] = f'{branch.split(".")[2]}{suffix}'
|
||||
except Exception as e:
|
||||
Console.error(f'Branch {branch} does not contain valid version')
|
||||
|
134
kdb-web/package-lock.json
generated
134
kdb-web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "kdb-web",
|
||||
"version": "0.3.dev70",
|
||||
"version": "0.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kdb-web",
|
||||
"version": "0.3.dev70",
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.0.0",
|
||||
"@angular/common": "^14.0.0",
|
||||
@@ -25,7 +25,6 @@
|
||||
"primeng": "^14.1.2",
|
||||
"rxjs": "~7.5.0",
|
||||
"socket.io-client": "^4.5.3",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -33,14 +32,14 @@
|
||||
"@angular/cli": "~14.0.0",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/node": "^18.8.3",
|
||||
"@types/node": "^18.11.9",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "~4.7.2"
|
||||
}
|
||||
},
|
||||
@@ -222,6 +221,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@angular-devkit/build-webpack": {
|
||||
"version": "0.1402.6",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.6.tgz",
|
||||
@@ -3200,9 +3205,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz",
|
||||
"integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==",
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
@@ -3705,12 +3710,6 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
@@ -4982,15 +4981,6 @@
|
||||
"integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@@ -7881,12 +7871,6 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/make-fetch-happen": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
|
||||
@@ -11730,32 +11714,10 @@
|
||||
"tree-kill": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
|
||||
"integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.21.3",
|
||||
@@ -12510,15 +12472,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/zone.js": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz",
|
||||
@@ -12659,6 +12612,12 @@
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14719,9 +14678,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz",
|
||||
"integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==",
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/parse-json": {
|
||||
@@ -15153,12 +15112,6 @@
|
||||
"readable-stream": "^3.6.0"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
@@ -16095,12 +16048,6 @@
|
||||
"integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@@ -18187,12 +18134,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"make-fetch-happen": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
|
||||
@@ -20975,23 +20916,10 @@
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
|
||||
"integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.21.3",
|
||||
@@ -21518,12 +21446,6 @@
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"zone.js": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz",
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "kdb-web",
|
||||
"version": "0.3.dev70",
|
||||
"version": "0.3.dev128",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"update-version": "ts-node -O '{\"module\": \"commonjs\"}' update-version.ts",
|
||||
"update-version": "ts-node-esm update-version.ts",
|
||||
"prestart": "npm run update-version",
|
||||
"start": "ng serve",
|
||||
"prebuild": "npm run update-version",
|
||||
@@ -33,7 +33,6 @@
|
||||
"primeng": "^14.1.2",
|
||||
"rxjs": "~7.5.0",
|
||||
"socket.io-client": "^4.5.3",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -41,14 +40,14 @@
|
||||
"@angular/cli": "~14.0.0",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/node": "^18.8.3",
|
||||
"@types/node": "^18.11.9",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "~4.7.2"
|
||||
}
|
||||
}
|
@@ -1,60 +1,65 @@
|
||||
<section class="login-wrapper">
|
||||
<div class="login-form-wrapper">
|
||||
<div class="login-form">
|
||||
<form [formGroup]="loginForm">
|
||||
<h1>{{'auth.header' | translate}}</h1>
|
||||
<div class="input-field">
|
||||
<input type="email" pInputText formControlName="email" placeholder="{{'auth.login.e_mail' | translate}}" [ngClass]="{ 'invalid-feedback-input': submitted && (
|
||||
<div class="login-form-wrapper">
|
||||
<div class="login-form">
|
||||
<form [formGroup]="loginForm">
|
||||
<h1>{{'auth.header' | translate}}</h1>
|
||||
<div class="input-field">
|
||||
<input type="email" pInputText formControlName="email" placeholder="{{'auth.login.e_mail' | translate}}" [ngClass]="{ 'invalid-feedback-input': submitted && (
|
||||
(loginForm.controls.email.errors && loginForm.controls.email.errors['required'] || authUserAtrErrors.email.required) ||
|
||||
(authUserAtrErrors.email.wrongData) ||
|
||||
(authUserAtrErrors.email.notConfirmed)
|
||||
)}" autocomplete="username email">
|
||||
<div *ngIf="submitted" class="invalid-feedback">
|
||||
<div
|
||||
*ngIf="loginForm.controls.email.errors && loginForm.controls.email.errors['required'] || authUserAtrErrors.email.required">
|
||||
{{'auth.login.e_mail_required' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.email.wrongData">{{'auth.login.user_not_found' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.email.notConfirmed">{{'auth.login.e_mail_not_confirmed' | translate}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<!--
|
||||
!! WARNING !!
|
||||
Bugfix from https://github.com/primefaces/primeng/issues/10788
|
||||
styleClass="p-password p-component p-inputwrapper p-input-icon-right"
|
||||
Remove after update!
|
||||
-->
|
||||
<p-password type="password" formControlName="password" placeholder="{{'auth.login.password' | translate}}" [ngClass]="{ 'invalid-feedback-input': submitted && (
|
||||
<div *ngIf="submitted" class="invalid-feedback">
|
||||
<div
|
||||
*ngIf="loginForm.controls.email.errors && loginForm.controls.email.errors['required'] || authUserAtrErrors.email.required">
|
||||
{{'auth.login.e_mail_required' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.email.wrongData">{{'auth.login.user_not_found' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.email.notConfirmed">{{'auth.login.e_mail_not_confirmed' | translate}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<!--
|
||||
!! WARNING !!
|
||||
Bugfix from https://github.com/primefaces/primeng/issues/10788
|
||||
styleClass="p-password p-component p-inputwrapper p-input-icon-right"
|
||||
Remove after update!
|
||||
-->
|
||||
<p-password type="password" formControlName="password" placeholder="{{'auth.login.password' | translate}}" [ngClass]="{ 'invalid-feedback-input': submitted && (
|
||||
(loginForm.controls.password.errors && loginForm.controls.password.errors['required'] || authUserAtrErrors.password.required) ||
|
||||
(authUserAtrErrors.password.wrongData)
|
||||
)}" autocomplete="current-password" [toggleMask]="true" [feedback]="false"
|
||||
styleClass="p-password p-component p-inputwrapper p-input-icon-right"
|
||||
></p-password>
|
||||
<div *ngIf="submitted" class="invalid-feedback">
|
||||
<div
|
||||
*ngIf="loginForm.controls.password.errors && loginForm.controls.password.errors['required'] || authUserAtrErrors.password.required">
|
||||
{{'auth.login.password_required' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.password.wrongData">{{'auth.login.wrong_password' | translate}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form-submit">
|
||||
<button pButton label="{{'auth.login.login' | translate}}" class="btn login-form-submit-btn" (click)="login()"
|
||||
[disabled]="loginForm.invalid"></button>
|
||||
</div>
|
||||
<div class="login-form-sub-button-wrapper">
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.login.register' | translate}}" class="btn login-form-sub-btn"
|
||||
(click)="register()"></button>
|
||||
</div>
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.login.forgot_password' | translate}}"
|
||||
class="btn login-form-sub-btn login-form-sub-login-btn p-button-text"
|
||||
(click)="forgotPassword()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
styleClass="p-password p-component p-inputwrapper p-input-icon-right"
|
||||
></p-password>
|
||||
<div *ngIf="submitted" class="invalid-feedback">
|
||||
<div
|
||||
*ngIf="loginForm.controls.password.errors && loginForm.controls.password.errors['required'] || authUserAtrErrors.password.required">
|
||||
{{'auth.login.password_required' | translate}}</div>
|
||||
<div *ngIf="authUserAtrErrors.password.wrongData">{{'auth.login.wrong_password' | translate}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form-submit">
|
||||
<button pButton label="{{'auth.login.login' | translate}}" class="btn login-form-submit-btn" (click)="login()"
|
||||
[disabled]="loginForm.invalid"></button>
|
||||
</div>
|
||||
<div class="login-form-sub-button-wrapper" *ngIf="!code && !state">
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.login.login_with_discord' | translate}}" class="btn login-form-sub-btn" (click)="discordLogin()"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form-sub-button-wrapper">
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.login.register' | translate}}" class="btn login-form-sub-btn"
|
||||
(click)="register()"></button>
|
||||
</div>
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.login.forgot_password' | translate}}"
|
||||
class="btn login-form-sub-btn login-form-sub-login-btn p-button-text"
|
||||
(click)="forgotPassword()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-auth-header></app-auth-header>
|
||||
</section>
|
||||
<app-auth-header></app-auth-header>
|
||||
</section>
|
||||
|
@@ -1,20 +1,21 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { AuthService } from 'src/app/services/auth/auth.service';
|
||||
import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto';
|
||||
import { Router } from '@angular/router';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ErrorDTO } from 'src/app/models/error/error-dto';
|
||||
import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum';
|
||||
import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum';
|
||||
import { AuthUserAtrErrors } from 'src/app/models/auth/auth-user-atr-errors';
|
||||
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
|
||||
import { ThemeService } from 'src/app/services/theme/theme.service';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { AuthService } from "src/app/services/auth/auth.service";
|
||||
import { AuthUserDTO } from "src/app/models/auth/auth-user.dto";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { catchError } from "rxjs/operators";
|
||||
import { ErrorDTO } from "src/app/models/error/error-dto";
|
||||
import { AuthErrorMessages } from "src/app/models/auth/auth-error-messages.enum";
|
||||
import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
|
||||
import { AuthUserAtrErrors } from "src/app/models/auth/auth-user-atr-errors";
|
||||
import { SpinnerService } from "src/app/services/spinner/spinner.service";
|
||||
import { ThemeService } from "src/app/services/theme/theme.service";
|
||||
import { throwError } from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
selector: "app-login",
|
||||
templateUrl: "./login.component.html",
|
||||
styleUrls: ["./login.component.scss"]
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
|
||||
@@ -25,45 +26,83 @@ export class LoginComponent implements OnInit {
|
||||
submitted = false;
|
||||
|
||||
authUserAtrErrors!: AuthUserAtrErrors;
|
||||
code!: string;
|
||||
state!: string;
|
||||
user!: AuthUserDTO;
|
||||
oAuthId!: string;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private spinnerService: SpinnerService,
|
||||
private themeService: ThemeService
|
||||
) { }
|
||||
private themeService: ThemeService,
|
||||
private route: ActivatedRoute
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.spinnerService.showSpinner();
|
||||
this.authService.isUserLoggedInAsync().then(result => {
|
||||
if (result) {
|
||||
this.router.navigate(['/dashboard']);
|
||||
this.router.navigate(["/dashboard"]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkDiscordLogin();
|
||||
this.initLoginForm();
|
||||
this.resetStateFlags();
|
||||
this.spinnerService.hideSpinner();
|
||||
});
|
||||
}
|
||||
|
||||
checkDiscordLogin() {
|
||||
this.route.queryParams.pipe(catchError(err => {
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["auth", "login"]).then(() => {
|
||||
});
|
||||
return throwError(() => err);
|
||||
})).subscribe(params => {
|
||||
if (!params["code"] || !params["state"]) {
|
||||
this.spinnerService.hideSpinner();
|
||||
return;
|
||||
}
|
||||
|
||||
this.code = params["code"];
|
||||
this.state = params["state"];
|
||||
this.authService.discordLogin(this.code, this.state).pipe(catchError(err => {
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["auth", "login"]).then(() => {
|
||||
});
|
||||
return throwError(() => err);
|
||||
})).subscribe(token => {
|
||||
this.authService.saveToken(token);
|
||||
this.themeService.loadTheme();
|
||||
this.themeService.loadMenu();
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["/dashboard"]);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
resetStateFlags(): void {
|
||||
this.authUserAtrErrors = new AuthUserAtrErrors();
|
||||
}
|
||||
|
||||
initLoginForm(): void {
|
||||
this.loginForm = this.formBuilder.group({
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]]
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
password: ["", [Validators.required, Validators.minLength(8)]]
|
||||
});
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.router.navigate(['/auth/register']);
|
||||
this.router.navigate(["/auth/register"]);
|
||||
}
|
||||
|
||||
forgotPassword(): void {
|
||||
this.router.navigate(['/auth/forgot-password']);
|
||||
this.router.navigate(["/auth/forgot-password"]);
|
||||
}
|
||||
|
||||
login(): void {
|
||||
@@ -76,8 +115,8 @@ export class LoginComponent implements OnInit {
|
||||
|
||||
this.spinnerService.showSpinner();
|
||||
const user: AuthUserDTO = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: this.loginForm.value.email ?? null,
|
||||
password: this.loginForm.value.password ?? null
|
||||
};
|
||||
@@ -107,8 +146,14 @@ export class LoginComponent implements OnInit {
|
||||
this.themeService.loadTheme();
|
||||
this.themeService.loadMenu();
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(['/dashboard']);
|
||||
this.router.navigate(["/dashboard"]);
|
||||
});
|
||||
}
|
||||
|
||||
discordLogin() {
|
||||
this.authService.getDiscordAuthURL().subscribe(url => {
|
||||
window.location.href = url.loginUrl;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -72,12 +72,6 @@
|
||||
[disabled]="loginForm.invalid"></button>
|
||||
</div>
|
||||
|
||||
<div class="login-form-sub-button-wrapper" *ngIf="!code && !state">
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.register.register_with_discord' | translate}}" class="btn login-form-sub-btn" (click)="discordLogin()"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="login-form-sub-button-wrapper">
|
||||
<div class="login-form-sub-btn-wrapper">
|
||||
<button pButton label="{{'auth.register.login' | translate}}" class="btn login-form-sub-btn" (click)="login()"></button>
|
||||
|
@@ -34,14 +34,6 @@ export class RegistrationComponent implements OnInit {
|
||||
password: false
|
||||
};
|
||||
|
||||
showEMailConfirmation = false;
|
||||
showEMailConfirmationError = false;
|
||||
|
||||
code!: string;
|
||||
state!: string;
|
||||
user!: AuthUserDTO;
|
||||
oAuthId!: string;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private formBuilder: FormBuilder,
|
||||
@@ -62,48 +54,7 @@ export class RegistrationComponent implements OnInit {
|
||||
this.confirmEMail();
|
||||
this.initData();
|
||||
this.spinnerService.showSpinner();
|
||||
this.route.queryParams.pipe(catchError(err => {
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["auth", "login"]).then(() => {
|
||||
});
|
||||
return throwError(() => err);
|
||||
})).subscribe(params => {
|
||||
if (!params["code"] || !params["state"]) {
|
||||
this.spinnerService.hideSpinner();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user) {
|
||||
this.spinnerService.hideSpinner();
|
||||
return;
|
||||
}
|
||||
|
||||
this.code = params["code"];
|
||||
this.state = params["state"];
|
||||
this.authService.discordCreateUser(this.code, this.state).pipe(catchError(err => {
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["auth", "login"]).then(() => {
|
||||
});
|
||||
return throwError(() => err);
|
||||
})).subscribe(oAuthDTO => {
|
||||
if (oAuthDTO) {
|
||||
this.user = oAuthDTO.user;
|
||||
this.oAuthId = oAuthDTO.oAuthId;
|
||||
}
|
||||
if (this.user && !this.oAuthId) {
|
||||
this.router.navigate(["auth", "login"]).then(() => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.router.navigate(["auth", "register"]).then(() => {
|
||||
});
|
||||
this.initData();
|
||||
}
|
||||
);
|
||||
this.initData();
|
||||
}
|
||||
);
|
||||
this.initData();
|
||||
}
|
||||
|
||||
initData() {
|
||||
@@ -126,18 +77,13 @@ export class RegistrationComponent implements OnInit {
|
||||
|
||||
initLoginForm(): void {
|
||||
this.loginForm = this.formBuilder.group({
|
||||
firstName: [this.user ? this.user.firstName : "", Validators.required],
|
||||
lastName: [this.user ? this.user.lastName : "", Validators.required],
|
||||
email: [this.user ? this.user.email : "", [Validators.required, Validators.email]],
|
||||
emailRepeat: [this.user ? this.user.email : "", [Validators.required, Validators.email]],
|
||||
password: [this.user ? this.user.password : "", [Validators.required, Validators.minLength(8)]],
|
||||
passwordRepeat: [this.user ? this.user.password : "", [Validators.required, Validators.minLength(8)]]
|
||||
firstName: ["", Validators.required],
|
||||
lastName: ["", Validators.required],
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
emailRepeat: ["", [Validators.required, Validators.email]],
|
||||
password: ["", [Validators.required, Validators.minLength(8)]],
|
||||
passwordRepeat: ["", [Validators.required, Validators.minLength(8)]]
|
||||
});
|
||||
|
||||
if (this.user) {
|
||||
this.loginForm.controls.email.disable();
|
||||
this.loginForm.controls.emailRepeat.disable();
|
||||
}
|
||||
}
|
||||
|
||||
register(): void {
|
||||
@@ -160,42 +106,10 @@ export class RegistrationComponent implements OnInit {
|
||||
}
|
||||
|
||||
this.spinnerService.showSpinner();
|
||||
if (this.user && this.oAuthId) {
|
||||
this.registerWithOAuth();
|
||||
return;
|
||||
}
|
||||
|
||||
this.registerLocal();
|
||||
}
|
||||
|
||||
private registerWithOAuth() {
|
||||
const oAuthDTO: OAuthDTO = {
|
||||
user: {
|
||||
firstName: this.loginForm.value.firstName ?? this.user.firstName,
|
||||
lastName: this.loginForm.value.lastName ?? this.user.lastName,
|
||||
email: this.loginForm.value.email ?? this.user.email,
|
||||
password: this.loginForm.value.password ?? null
|
||||
},
|
||||
oAuthId: this.oAuthId
|
||||
};
|
||||
this.authService.discordRegister(oAuthDTO)
|
||||
.pipe(catchError(error => {
|
||||
if (error.error !== null) {
|
||||
const err: ErrorDTO = error.error;
|
||||
|
||||
if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.UserAlreadyExists) {
|
||||
this.authUserAtrErrors.email.wrongData = true;
|
||||
}
|
||||
}
|
||||
this.spinnerService.hideSpinner();
|
||||
throw error;
|
||||
}))
|
||||
.subscribe(resp => {
|
||||
this.spinnerService.hideSpinner();
|
||||
this.router.navigate(["/auth/login"]);
|
||||
});
|
||||
}
|
||||
|
||||
private registerLocal() {
|
||||
const user: AuthUserDTO = {
|
||||
firstName: this.loginForm.value.firstName ?? null,
|
||||
@@ -237,10 +151,4 @@ export class RegistrationComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
discordLogin() {
|
||||
this.authService.getDiscordAuthURL().subscribe(url => {
|
||||
window.location.href = url.loginUrl;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
import { MembersComponent } from "./component/members/members.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: MembersComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class MembersRoutingModule { }
|
@@ -1,9 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { ServerDashboardComponent } from './server-dashboard/server-dashboard.component';
|
||||
import { AuthGuard } from "../../shared/guards/auth/auth.guard";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: ServerDashboardComponent }
|
||||
{ path: '', component: ServerDashboardComponent },
|
||||
{ path: 'members', loadChildren: () => import('./members/members.module').then(m => m.MembersModule), canActivate: [AuthGuard] },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -167,6 +167,7 @@ export class AuthService {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
discordCreateUser(code: string, state: string) {
|
||||
return this.http.get<OAuthDTO>(`${this.appsettings.getApiURL()}/api/auth/discord/create-user?code=${code}&state=${state}`, {
|
||||
headers: new HttpHeaders({
|
||||
@@ -175,6 +176,14 @@ export class AuthService {
|
||||
});
|
||||
}
|
||||
|
||||
discordLogin(code: string, state: string): Observable<TokenDTO> {
|
||||
return this.http.get<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/discord/login?code=${code}&state=${state}`, {
|
||||
headers: new HttpHeaders({
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// /api/auth/discord/register?code=
|
||||
discordRegister(oAuthDTO: OAuthDTO) {
|
||||
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/discord/register`, oAuthDTO, {
|
||||
|
@@ -3,7 +3,7 @@
|
||||
"WebVersion": {
|
||||
"Major": "0",
|
||||
"Minor": "3",
|
||||
"Micro": "dev70"
|
||||
"Micro": "dev128"
|
||||
},
|
||||
"Themes": [
|
||||
{
|
||||
|
@@ -93,6 +93,7 @@
|
||||
"e_mail": "E-Mail",
|
||||
"password": "Passwort",
|
||||
"login": "Einloggen",
|
||||
"login_with_discord": "Mit Discord Einloggen",
|
||||
"register": "Registrieren",
|
||||
"forgot_password": "Passwort vergessen?",
|
||||
"e_mail_required": "E-Mail benötigt",
|
||||
|
@@ -373,7 +373,7 @@ footer {
|
||||
.login-form-wrapper,
|
||||
.auth-header {
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
height: 450px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@@ -1,5 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS"
|
||||
}
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
|
Reference in New Issue
Block a user