160 Commits

Author SHA1 Message Date
1b5f87f869 Merge pull request '0.3.1' (#187) from 0.3.1 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#187
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
2023-01-14 10:39:27 +01:00
ed83826234 Merge branch 'master' into 0.3.1 2023-01-14 01:35:36 +01:00
96b54fdbc9 Fixed ontime calculation 2023-01-14 01:31:35 +01:00
9658a5c238 Fixed ontime round 2023-01-14 00:27:08 +01:00
6dd9b9c302 Resettet python version 2023-01-14 00:18:50 +01:00
29ee85bd93 Merge pull request 'Formatted files with black' (#186) from formatting/black into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#186
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
2023-01-14 00:01:00 +01:00
e969926769 Set version and copyright 2023-01-13 23:59:38 +01:00
d596d7465e Formatted files with black 2023-01-13 23:54:55 +01:00
ea266cc5be Formatted files with black 2023-01-13 23:54:06 +01:00
e2b2fb5abb Formatted files with black 2023-01-13 23:43:37 +01:00
37ec0cf0c7 Merge pull request '/user add atr value member #179' (#182) from #179 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#182
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #179
2023-01-13 23:28:36 +01:00
460bb42028 Fixed /user reset command #179 2023-01-13 23:26:38 +01:00
0379c3ed9b Removed unused imports #179 2023-01-13 23:17:06 +01:00
fecb82bf36 Refixed migration #179 2023-01-13 23:15:06 +01:00
2401e58827 Fixed client utils & Added add, remove and reset commands #179 2023-01-13 23:12:00 +01:00
5d3ceff3bf Renamed remove command #179 2023-01-13 23:10:33 +01:00
e481958bad Added logic to handle max message xp per hour #168 2023-01-13 23:09:54 +01:00
0c76269e40 Merge pull request 'XP abziehen bei löschen einer Nachricht #178' (#180) from #178 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#180
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #178
2023-01-13 23:06:22 +01:00
17b336623d Renamed function #178 2023-01-13 22:59:48 +01:00
00b4e2ab66 Updated cpl-discord #178 2023-01-13 22:47:42 +01:00
1b15e54199 Improved formatting #178 2023-01-13 22:47:42 +01:00
c9aeb684d6 Added support to remove xp on message delete #178 2023-01-13 22:47:42 +01:00
3811cf7d74 Added support to set cached max_message by config #178 2023-01-13 22:47:39 +01:00
2a0e3d77b7 Merge pull request 'Vergebene XP für Nachrichten und Reaktionen pro Stunde begrenzen #168' (#176) from #168 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#176
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #168
2023-01-13 22:47:12 +01:00
59fc1e1442 Fixed naming error #168 2023-01-13 22:43:30 +01:00
84f8690bdf Updated migration #168 2023-01-13 22:35:46 +01:00
d91ff392ca Fixed is_message_xp_count_by_hour_higher_that_max_message_count_per_hour #168 2023-01-13 22:27:05 +01:00
1ff70af72b Added logic to handle max message xp per hour #168 2023-01-13 22:17:16 +01:00
a216506a37 Added UserMessageCountPerHour model #168 2023-01-13 22:16:05 +01:00
3d01c9f798 Added max_message_xp_per_hour #168 2023-01-13 22:16:00 +01:00
624625d4b5 Merge pull request 'Levelsystem per Befehl neuladen #164' (#175) from #164 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#175
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #164
2023-01-13 22:15:11 +01:00
f9593b5f44 Merge pull request 'Ontime berechnung zentralisieren #166' (#172) from #166 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#172
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #166
2023-01-13 22:14:57 +01:00
34ebb48c83 Centralised ontime calculation #166 2023-01-13 12:23:49 +01:00
849a92125a Improved formatting #164 2023-01-12 07:33:53 +01:00
5d6c55fc86 Improved formatting #164 2023-01-12 07:29:45 +01:00
3cf0fe3479 Improved formatting #166 2023-01-12 07:20:00 +01:00
b80958e3ab Fixed vc_state_update #166 2023-01-12 07:14:44 +01:00
b89fa12ec6 Fixed level service #166 2023-01-12 07:14:44 +01:00
706b6732eb Fixed ontime calculation #166 2023-01-12 07:14:40 +01:00
053c190c78 Merge branch '0.3.1' into #164 2023-01-12 07:12:04 +01:00
c443d108dc Merge pull request 'Added optional feature-flag files' (#177) from internal/feature-flags into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#177
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2023-01-12 07:11:32 +01:00
09fbc27981 Added optional feature-flag files 2023-01-12 07:09:40 +01:00
242ffd1550 Merge pull request 'Reaktion ins message.log #169' (#174) from #169 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#174
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #169
2023-01-12 07:05:50 +01:00
fb4be18ef2 Merge pull request 'VoiceStateUpdate speichern nach channel wechsel #165' (#173) from #165 into 0.3.1
Reviewed-on: sh-edraft.de/kd_discord_bot#173
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #165
2023-01-12 07:05:32 +01:00
89ee16d1b5 Merge pull request '0.3' (#146) from 0.3 into master
Reviewed-on: sh-edraft.de/kd_discord_bot#146
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
2023-01-12 07:04:39 +01:00
8b7efed257 Added level reload command & improved delete_message guild id handling #164 2023-01-11 18:23:00 +01:00
d10c33c6c2 Added logic to log reactions #169 2023-01-11 17:53:21 +01:00
3abaee3e71 Added logic to save vc_state_update when changing channel #165 2023-01-11 17:25:51 +01:00
4c577f6a7b Set config 2023-01-10 20:05:17 +01:00
f50a763539 Set docker 2023-01-10 20:05:03 +01:00
9ff9cc672e Improved docker stuff 2023-01-09 10:23:06 +01:00
9d4cf5a769 Set version for frontend 2023-01-09 08:31:31 +01:00
df1d62f569 Build version 0.3.0 2023-01-09 08:06:28 +01:00
4201510e9f Set version 0.3.0 2023-01-09 08:05:50 +01:00
394428a81a Updated config for build 2023-01-09 08:04:57 +01:00
4733f84ec6 Updated config for build 2023-01-09 08:04:45 +01:00
9c466733fb Reset config 2023-01-05 13:33:24 +01:00
bd62618fdf Build rc5 version 2023-01-04 17:20:26 +01:00
a78d5f0fcb Fixed #153 again 2023-01-04 17:19:36 +01:00
661630bb37 Build rc4 version 2023-01-04 17:07:53 +01:00
15f041a2da Set rc4 version 2023-01-04 17:07:30 +01:00
1ac5d982ed Fixed #152 2023-01-04 17:06:46 +01:00
1337ef35dd Fixed #153 2023-01-04 17:05:32 +01:00
986d7c4562 Merge remote-tracking branch 'origin/0.3' into 0.3 2023-01-04 17:03:33 +01:00
7fcb4084d2 Reactivated web api & set frontend version 2023-01-04 12:23:14 +01:00
fbac0d3d02 Reactivated web api & set frontend version 2023-01-04 12:21:31 +01:00
9626de2b27 Build new rc version 2023-01-04 12:14:39 +01:00
1d74f5e67c Set new rc version 2023-01-04 12:14:18 +01:00
6949db10f8 Merge pull request '/auto-role rule add failed (#151)' (#161) from #151 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#161
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #151
2023-01-04 12:09:14 +01:00
ba5d897662 Merge pull request 'Fixed autocompletes (#150)' (#160) from #150 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#160
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #150
2023-01-04 11:30:22 +01:00
47415af868 Merge pull request 'Fixed user autocompletes (#153)' (#159) from #153 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#159
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #153
2023-01-04 11:30:11 +01:00
6353c7ca86 Merge pull request 'Fixed user info command (#152)' (#158) from #152 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#158
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #152
2023-01-04 11:30:00 +01:00
440689653d Merge pull request 'Added logic to count moved users to mass move (#156)' (#157) from #156 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#157
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #156
2023-01-04 11:29:49 +01:00
844a818aa0 Fixed auto-role add #151 2022-12-30 13:05:49 +01:00
33fb973f21 Fixed autocompletes #150 2022-12-30 13:05:30 +01:00
7646335d03 Fixed autocompletes #150 2022-12-30 12:18:56 +01:00
04c905d287 Fixed user autocompletes #153 2022-12-30 11:45:41 +01:00
c7d8508173 Fixed user info command #152 2022-12-30 11:34:47 +01:00
f3eff97780 Added logic to count moved users to mass move #156 2022-12-30 11:26:29 +01:00
476db0ed33 Build rc2 2022-12-28 20:13:48 +01:00
9c369b911a Fixed user group 2022-12-28 20:10:20 +01:00
6dfd476bce Merge pull request 'Improved translations #64' (#149) from #64 into 0.3
closes #64
2022-12-28 19:48:49 +01:00
f669410b2a Improved translations #64 2022-12-28 19:45:50 +01:00
a46122243f Merge pull request 'Added logic to handle public and private messages #147' (#148) from #147 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#148
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #147
2022-12-28 18:59:47 +01:00
3b7345b404 Added logic to handle public and private messages #147 2022-12-27 19:02:59 +01:00
59d38f8f2a Merge pull request 'Build 0.3.0.rc1' (#145) from 0.3.0.rc1 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#145
Closes #145
2022-12-27 18:54:29 +01:00
612430d3e0 Fixed dockerfile for releases 2022-12-23 16:41:28 +01:00
e01290db9b Build 0.3.0.rc1 2022-12-23 16:12:35 +01:00
6273ce9cba Merge pull request 'Added user remove command #23' (#144) from #23 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#144
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #23
2022-12-23 15:50:07 +01:00
5c923d8bd8 Merge branch '0.3' into #23 2022-12-23 15:48:46 +01:00
6c6169f7ee Merge pull request 'Added user set command #22' (#143) from #22 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#143
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #22
2022-12-23 15:48:36 +01:00
ffd5105154 Removed error handling for database errors, replaced match-case-statements with if-else-statements and removed unused variables from the language file #23 2022-12-22 21:02:05 +01:00
9040ab6fca Improved error handling and changed attribute names from constants to variables which are located in the language file #23 2022-12-22 20:41:50 +01:00
9d89135b4c Added SQL command to delete all records by user id in "userjoinedvoicechannel"-table #23 2022-12-22 20:39:39 +01:00
71899346b2 Added translation for user remove command #23 2022-12-22 20:39:39 +01:00
d197a6e158 Added user remove command #23 2022-12-22 20:39:39 +01:00
40e53de3f2 Removed error handling for database errors and replaced match-case-statements with if-else-statements #22 2022-12-22 20:38:59 +01:00
1b9553e63b Improved error handling and changed attribute names from constants to variables which are located in the language file #22 2022-12-18 22:44:07 +01:00
5447d502cc Merge branch '0.3' into #22 2022-12-18 11:02:07 +01:00
5a4c2901f5 Fixed not checking level after new XP is assinged to user #22 2022-12-17 19:21:04 +01:00
bcf71a26f0 Fixed cpl query update 2022-12-17 16:17:14 +01:00
b25b75e382 Fixed frontend 2022-12-17 15:26:48 +01:00
f21b4f9881 Merge pull request 'Updated CPL to 2022.12.0 (#140)' (#141) from #140 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#141
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #140
2022-12-12 08:13:14 +01:00
8705904882 Merge branch '0.3' into #140 2022-12-12 08:13:00 +01:00
cf610b770b Added translation for user set command 2022-12-11 21:19:08 +01:00
ec30069ff5 Added user set command 2022-12-11 21:18:51 +01:00
7026b3abac Merge pull request 'Added user get command #21' (#142) from #21 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#142
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #21
2022-12-11 19:27:35 +01:00
31464df3f6 Fixed ontime rounding #21 2022-12-11 19:26:48 +01:00
eb9f5b83d5 Changed cpl-query to "2022.12.1.post1" in "kdb-bot/src/bot/bot.json" 2022-12-11 03:03:12 +01:00
fdd8357729 Added user get command #21 2022-12-11 00:22:34 +01:00
7c744f0e65 Updated CPL to 2022.12.0 #140 2022-12-04 18:38:58 +01:00
4a0f5c28c1 Merge pull request '0.3 - Bei message Member.name -> Member.mentions oder so (#100)' (#137) from #100 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#137
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #100
2022-11-22 18:16:38 +01:00
d2f99003ff Merge pull request '0.3 - Nachrichten sollen länger gezeigt werden (#135)' (#138) from #135 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#138
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #135
2022-11-22 18:16:21 +01:00
9dd3fd4b8e Updated configs #135 2022-11-21 20:35:55 +01:00
d18500b96c Changed .name -> .mention #100 2022-11-21 20:29:50 +01:00
91fdf34d32 Merge pull request 'Added mass-move command' (#136) from #20 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#136
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #136
2022-11-21 19:00:34 +01:00
12369cdbe3 Removed unused code for mass-move #20 2022-11-21 18:20:23 +01:00
25c698273a Fixed sending message with translation pipe #20 2022-11-21 00:30:49 +01:00
2868b1afe2 Added messaging to mass-move #20 2022-11-20 23:27:22 +01:00
0d1c15b31d Added mass-move command #20 2022-11-20 19:18:17 +01:00
840da350e4 Merge pull request '0.3 - Login per Discord (#128)' (#129) from #128 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#129
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #128
2022-11-20 16:54:03 +01:00
bd94c42eae Added discord login & removed discord register #128 2022-11-20 16:09:20 +01:00
c7a925b997 Merge pull request 'Added presence command #18' (#126) from #18 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#126
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #126
2022-11-20 15:41:26 +01:00
7fb6d22c3f Added requested changes to presence command #18 2022-11-20 15:39:34 +01:00
c5b5297058 Added presence command #18 2022-11-20 06:21:51 +01:00
9ed66c2560 Merge branch '0.3' of https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot into 0.3
 Conflicts:
	kdb-bot/src/modules/technician/command/log_command.py
2022-11-18 15:39:36 +01:00
6e6157ccf2 Fixed log command 2022-11-18 15:34:25 +01:00
f136d6164e Fixed log command 2022-11-18 15:05:39 +01:00
9b5033b80e Fixed some on member join stuff 2022-11-18 14:33:54 +01:00
f5a71a8450 Fixed some on member join stuff 2022-11-18 14:14:01 +01:00
d3279eb7c7 Updated config 2022-11-18 10:23:40 +01:00
ec7aeb8712 Added icmplib 2022-11-18 10:16:59 +01:00
fd609eb923 Merge remote-tracking branch 'origin/0.3' into 0.3
# Conflicts:
#	kdb-bot/cpl-workspace.json
2022-11-18 09:58:28 +01:00
a7dbc75d2e Updated configs 2022-11-18 09:58:05 +01:00
b0459567f4 Fixed workspace 2022-11-18 09:52:27 +01:00
25b7b18013 Fixed workspace 2022-11-18 09:51:06 +01:00
87350cba1a Moved dockerfile 2022-11-18 09:50:06 +01:00
864d181de0 Fixed project files 2022-11-18 09:33:50 +01:00
90011be760 Updated api config ? 2022-11-18 09:30:44 +01:00
47dd6fdc2d Improved build version stuff 2022-11-18 09:30:29 +01:00
8445c23e7f Merge pull request '0.3 - Log Befehl (#44)' (#125) from #44 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#125
Reviewed-by: Sven Heidemann <sven.heidemann@sh-edraft.de>
Closes #44
2022-11-17 23:03:33 +01:00
e6fc41090a Refactored code #44 2022-11-17 23:02:27 +01:00
7c79c6f992 Merge branch '0.3' into #44
# Conflicts:
#	kdb-bot/src/bot/config
2022-11-17 22:54:01 +01:00
7b8dca64bf Finished log command #44 2022-11-17 22:45:10 +01:00
549b05087f Merge pull request '0.3 - /user info für alle (#119)' (#122) from #119 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#122
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #119
2022-11-17 20:40:55 +01:00
83d18da58f Merge pull request '0.3 - level check for all members on seed (#123)' (#124) from #123 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#124
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #123
2022-11-17 20:40:37 +01:00
18e4465b17 Merge branch '0.3' into #123 2022-11-17 20:01:44 +01:00
2e20bb12de Merge branch '0.3' into #119 2022-11-17 20:01:31 +01:00
4f4e80fb6b Merge pull request '0.3 - Xp für Reaction (#118)' (#121) from #118 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#121
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #121
2022-11-17 20:01:12 +01:00
fd1245fb4f Merge remote-tracking branch 'origin/#118' into #118 2022-11-17 19:59:01 +01:00
fdb358c45e Removed xp when remove reaction #118 2022-11-17 19:58:51 +01:00
c438a91b87 Added logic to add xp on reaction #118 2022-11-17 19:58:51 +01:00
a46fbcd9fc Removed xp when remove reaction #118 2022-11-17 19:57:28 +01:00
91285540c6 Merge pull request '0.3 - /ping pings to urls (#117)' (#120) from #117 into 0.3
Reviewed-on: sh-edraft.de/kd_discord_bot#120
Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com>
Closes #117
2022-11-17 19:54:17 +01:00
ab2145d5df Check level for each member after changes #123 2022-11-17 19:31:57 +01:00
63fe566044 Improved /user info command #119 2022-11-17 16:49:33 +01:00
d45d787cea Added logic to add xp on reaction #118 2022-11-17 16:35:47 +01:00
442170eca9 Added pings to servers to ping command #117 2022-11-17 16:16:28 +01:00
2c7f4647af [WIP] Added log command #44 2022-11-16 21:04:07 +01:00
53604706c2 Added technician module #44 2022-11-14 22:29:43 +01:00
305 changed files with 6693 additions and 3812 deletions

View File

@@ -13,19 +13,22 @@
"level": "src/modules/level/level.json", "level": "src/modules/level/level.json",
"permission": "src/modules/permission/permission.json", "permission": "src/modules/permission/permission.json",
"stats": "src/modules/stats/stats.json", "stats": "src/modules/stats/stats.json",
"technician": "src/modules/technician/technician.json",
"get-version": "tools/get_version/get-version.json", "get-version": "tools/get_version/get-version.json",
"post-build": "tools/post_build/post-build.json", "post-build": "tools/post_build/post-build.json",
"set-version": "tools/set_version/set-version.json" "set-version": "tools/set_version/set-version.json"
}, },
"Scripts": { "Scripts": {
"sv": "cpl set-version", "format": "black ./",
"set-version": "cpl run set-version $ARGS; echo '';",
"sv": "cpl set-version $ARGS",
"set-version": "cpl run set-version $ARGS --dev; echo '';",
"gv": "cpl get-version", "gv": "cpl get-version",
"get-version": "export VERSION=$(cpl run get-version); echo $VERSION;", "get-version": "export VERSION=$(cpl run get-version --dev); echo $VERSION;",
"pre-build": "cpl set-version $ARGS", "pre-build": "cpl set-version $ARGS; black ./;",
"post-build": "cpl run post-build", "post-build": "cpl run post-build --dev; black ./;",
"pre-prod": "cpl build", "pre-prod": "cpl build",
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
@@ -36,9 +39,10 @@
"pre-dev": "cpl build", "pre-dev": "cpl build",
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", "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-build": "cpl build $ARGS; docker build -t kdb-bot/kdb-bot:$(cpl gv) .;",
"docker-compose": "docker-compose up -d", "dc-up": "docker-compose up -d",
"docker": "cpl docker-build; cpl docker-compose;" "dc-down": "docker-compose down",
"docker": "cpl dc-down; cpl docker-build; cpl dc-up;"
} }
} }
} }

18
kdb-bot/dockerfile Normal file
View File

@@ -0,0 +1,18 @@
# syntax=docker/dockerfile:1
FROM python:3.10.4-alpine
WORKDIR /app
COPY ./dist/bot/build/kdb-bot/ .
COPY ./dist/bot/build/requirements.txt .
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"]

2
kdb-bot/pyproject.toml Normal file
View File

@@ -0,0 +1,2 @@
[tool.black]
line-length = 120

View File

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

View File

@@ -13,7 +13,6 @@ from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
class Application(DiscordBotApplicationABC): class Application(DiscordBotApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
DiscordBotApplicationABC.__init__(self, config, services) DiscordBotApplicationABC.__init__(self, config, services)
@@ -42,18 +41,22 @@ class Application(DiscordBotApplicationABC):
async def main(self): async def main(self):
try: try:
self._logger.debug(__name__, f'Starting...') self._logger.debug(__name__, f"Starting...")
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module) and self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development': if (
self._feature_flags.get_flag(FeatureFlagsEnum.api_module)
and self._feature_flags.get_flag(FeatureFlagsEnum.api_only)
and self._environment.environment_name == "development"
):
self._api.start() self._api.start()
self._api.join() self._api.join()
return return
self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}') self._logger.trace(__name__, f"Try to start {DiscordBotService.__name__}")
await self._bot.start_async() await self._bot.start_async()
await self._bot.stop_async() await self._bot.stop_async()
except Exception as e: except Exception as e:
self._logger.error(__name__, 'Start failed', e) self._logger.error(__name__, "Start failed", e)
async def stop_async(self): async def stop_async(self):
if self._is_stopping: if self._is_stopping:
@@ -61,13 +64,13 @@ class Application(DiscordBotApplicationABC):
self._is_stopping = True self._is_stopping = True
try: try:
self._logger.trace(__name__, f'Try to stop {DiscordBotService.__name__}') self._logger.trace(__name__, f"Try to stop {DiscordBotService.__name__}")
await self._bot.close() await self._bot.close()
self._logger.trace(__name__, f'Stopped {DiscordBotService.__name__}') self._logger.trace(__name__, f"Stopped {DiscordBotService.__name__}")
except Exception as e: except Exception as e:
self._logger.error(__name__, 'stop failed', e) self._logger.error(__name__, "stop failed", e)
Console.write_line() Console.write_line()
def is_restart(self): def is_restart(self):
return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False # return True if self._configuration.get_configuration("IS_RESTART") == "true" else False #

View File

@@ -4,33 +4,34 @@
"Version": { "Version": {
"Major": "0", "Major": "0",
"Minor": "3", "Minor": "3",
"Micro": "dev25" "Micro": "1"
}, },
"Author": "Sven Heidemann", "Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de", "AuthorEmail": "sven.heidemann@sh-edraft.de",
"Description": "Keksdose bot", "Description": "Keksdose bot",
"LongDescription": "Discord bot for the Keksdose discord Server", "LongDescription": "Discord bot for the Keksdose discord Server",
"URL": "https://www.sh-edraft.de", "URL": "https://www.sh-edraft.de",
"CopyrightDate": "2022", "CopyrightDate": "2022 - 2023",
"CopyrightName": "sh-edraft.de", "CopyrightName": "sh-edraft.de",
"LicenseName": "MIT", "LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.", "LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [ "Dependencies": [
"cpl-core==2022.10.0.post7", "cpl-core==2022.12.1.post2",
"cpl-translation==2022.10.0.post2", "cpl-translation==2022.12.1",
"cpl-query==2022.10.0.post2", "cpl-query==2022.12.2.post1",
"cpl-discord==2022.10.0.post6", "cpl-discord==2022.12.1.post2",
"Flask==2.2.2", "Flask==2.2.2",
"Flask-Classful==0.14.2", "Flask-Classful==0.14.2",
"Flask-Cors==3.0.10", "Flask-Cors==3.0.10",
"PyJWT==2.6.0", "PyJWT==2.6.0",
"waitress==2.1.2", "waitress==2.1.2",
"Flask-SocketIO==5.3.1", "Flask-SocketIO==5.3.2",
"eventlet==0.33.1", "eventlet==0.33.2",
"requests-oauthlib==1.3.1" "requests-oauthlib==1.3.1",
"icmplib==3.0.3"
], ],
"DevDependencies": [ "DevDependencies": [
"cpl-cli==2022.10.0" "cpl-cli==2022.12.1.post2"
], ],
"PythonVersion": ">=3.10.4", "PythonVersion": ">=3.10.4",
"PythonPath": {}, "PythonPath": {},
@@ -59,9 +60,9 @@
"../modules/boot_log/boot-log.json", "../modules/boot_log/boot-log.json",
"../modules/database/database.json", "../modules/database/database.json",
"../modules/level/level.json", "../modules/level/level.json",
"../modules/permission/level.json",
"../modules/permission/permission.json", "../modules/permission/permission.json",
"../modules/permission/stats.json" "../modules/stats/stats.json",
"../modules/technician/technician.json"
] ]
} }
} }

View File

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

View File

@@ -0,0 +1,16 @@
from cpl_core.application import ApplicationExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_core.configuration.bot_settings import BotSettings
class InitBotExtension(ApplicationExtensionABC):
def __init__(self):
ApplicationExtensionABC.__init__(self)
async def run(self, config: ConfigurationABC, services: ServiceProviderABC):
settings = config.get_configuration(BotSettings)
bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC, max_messages=settings.cache_max_messages)

View File

@@ -6,6 +6,7 @@ from cpl_core.application import ApplicationBuilder
from cpl_core.console import Console from cpl_core.console import Console
from bot.application import Application from bot.application import Application
from bot.extension.init_bot_extension import InitBotExtension
from bot.startup import Startup from bot.startup import Startup
from bot.startup_discord_extension import StartupDiscordExtension from bot.startup_discord_extension import StartupDiscordExtension
from bot.startup_migration_extension import StartupMigrationExtension from bot.startup_migration_extension import StartupMigrationExtension
@@ -18,22 +19,24 @@ from modules.database.database_extension import DatabaseExtension
class Program: class Program:
def __init__(self): def __init__(self):
self.app: Optional[Application] = None self.app: Optional[Application] = None
async def start(self): async def start(self):
# discord extension has to be loaded before modules (modules depends on discord stuff) # discord extension has to be loaded before modules (modules depends on discord stuff)
app_builder = ApplicationBuilder(Application) \ app_builder = (
.use_extension(StartupSettingsExtension) \ ApplicationBuilder(Application)
.use_extension(StartupDiscordExtension) \ .use_extension(StartupSettingsExtension)
.use_extension(StartupModuleExtension) \ .use_extension(StartupDiscordExtension)
.use_extension(StartupMigrationExtension) \ .use_extension(StartupModuleExtension)
.use_extension(BootLogExtension) \ .use_extension(StartupMigrationExtension)
.use_extension(DatabaseExtension) \ .use_extension(InitBotExtension)
.use_extension(AppApiExtension) \ .use_extension(BootLogExtension)
.use_extension(CoreExtension) \ .use_extension(DatabaseExtension)
.use_extension(AppApiExtension)
.use_extension(CoreExtension)
.use_startup(Startup) .use_startup(Startup)
)
self.app: Application = await app_builder.build_async() self.app: Application = await app_builder.build_async()
await self.app.run_async() await self.app.run_async()
@@ -50,19 +53,25 @@ def main():
except KeyboardInterrupt: except KeyboardInterrupt:
asyncio.run(program.stop()) asyncio.run(program.stop())
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot start the bot', f'{e} -> {traceback.format_exc()}') Console.error(
f"[ ERROR ] [ {__name__} ]: Cannot start the bot",
f"{e} -> {traceback.format_exc()}",
)
finally: finally:
try: try:
asyncio.run(program.stop()) asyncio.run(program.stop())
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Cannot stop the bot', f'{e} -> {traceback.format_exc()}') Console.error(
f"[ ERROR ] [ {__name__} ]: Cannot stop the bot",
f"{e} -> {traceback.format_exc()}",
)
if program.app is not None and program.app.is_restart(): if program.app is not None and program.app.is_restart():
del program del program
main() main()
if __name__ == '__main__': if __name__ == "__main__":
main() main()
# (( # ((

View File

@@ -11,24 +11,28 @@ from modules.database.database_module import DatabaseModule
from modules.level.level_module import LevelModule from modules.level.level_module import LevelModule
from modules.permission.permission_module import PermissionModule from modules.permission.permission_module import PermissionModule
from modules.stats.stats_module import StatsModule from modules.stats.stats_module import StatsModule
from modules.technician.technician_module import TechnicianModule
class ModuleList: class ModuleList:
@staticmethod @staticmethod
def get_modules(): def get_modules():
# core modules (modules out of modules folder) should be loaded first! # core modules (modules out of modules folder) should be loaded first!
return List(type, [ return List(
CoreModule, # has to be first! type,
DataModule, [
AutoRoleModule, CoreModule, # has to be first!
BaseModule, DataModule,
DatabaseModule, PermissionModule,
LevelModule, DatabaseModule,
PermissionModule, AutoRoleModule,
ApiModule, BaseModule,
StatsModule, LevelModule,
# has to be last! ApiModule,
BootLogModule, StatsModule,
CoreExtensionModule, TechnicianModule,
]) # has to be last!
BootLogModule,
CoreExtensionModule,
],
)

View File

@@ -20,7 +20,6 @@ from bot_data.db_context import DBContext
class Startup(StartupABC): class Startup(StartupABC):
def __init__(self): def __init__(self):
StartupABC.__init__(self) StartupABC.__init__(self)
self._start_time = datetime.now() self._start_time = datetime.now()
@@ -28,12 +27,16 @@ class Startup(StartupABC):
self._config: Optional[ConfigurationABC] = None self._config: Optional[ConfigurationABC] = None
self._feature_flags: Optional[FeatureFlagsSettings] = None self._feature_flags: Optional[FeatureFlagsSettings] = None
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC: def configure_configuration(
self, configuration: ConfigurationABC, environment: ApplicationEnvironment
) -> ConfigurationABC:
self._config = configuration self._config = configuration
self._feature_flags = configuration.get_configuration(FeatureFlagsSettings) self._feature_flags = configuration.get_configuration(FeatureFlagsSettings)
return configuration return configuration
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC: def configure_services(
self, services: ServiceCollectionABC, environment: ApplicationEnvironment
) -> ServiceProviderABC:
services.add_logging() services.add_logging()
if self._feature_flags.get_flag(FeatureFlagsEnum.core_module): if self._feature_flags.get_flag(FeatureFlagsEnum.core_module):
# custom logging # custom logging
@@ -52,9 +55,11 @@ class Startup(StartupABC):
for c in CustomFileLoggerABC.__subclasses__(): for c in CustomFileLoggerABC.__subclasses__():
i: LoggerABC = provider.get_service(c) i: LoggerABC = provider.get_service(c)
logger: LoggerABC = provider.get_service(LoggerABC) logger: LoggerABC = provider.get_service(LoggerABC)
for flag in [f for f in FeatureFlagsEnum]: for flag in [f for f in FeatureFlagsEnum]:
logger.debug(__name__, f'Loaded feature-flag: {flag} = {self._feature_flags.get_flag(flag)}') logger.debug(
__name__,
f"Loaded feature-flag: {flag} = {self._feature_flags.get_flag(flag)}",
)
return provider return provider

View File

@@ -6,7 +6,6 @@ from cpl_discord import get_discord_collection
class StartupDiscordExtension(StartupExtensionABC): class StartupDiscordExtension(StartupExtensionABC):
def __init__(self): def __init__(self):
pass pass

View File

@@ -5,15 +5,18 @@ from cpl_core.environment import ApplicationEnvironmentABC
from bot_data.abc.migration_abc import MigrationABC from bot_data.abc.migration_abc import MigrationABC
from bot_data.migration.api_migration import ApiMigration from bot_data.migration.api_migration import ApiMigration
from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration
from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.auto_role_migration import AutoRoleMigration
from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.initial_migration import InitialMigration
from bot_data.migration.level_migration import LevelMigration from bot_data.migration.level_migration import LevelMigration
from bot_data.migration.stats_migration import StatsMigration from bot_data.migration.stats_migration import StatsMigration
from bot_data.migration.user_message_count_per_hour_migration import (
UserMessageCountPerHourMigration,
)
from bot_data.service.migration_service import MigrationService from bot_data.service.migration_service import MigrationService
class StartupMigrationExtension(StartupExtensionABC): class StartupMigrationExtension(StartupExtensionABC):
def __init__(self): def __init__(self):
pass pass
@@ -27,3 +30,5 @@ class StartupMigrationExtension(StartupExtensionABC):
services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0 services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0
services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0 services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0
services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0 services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0
services.add_transient(MigrationABC, AutoRoleFix1Migration) # 30.12.2022 #151 - 0.3.0
services.add_transient(MigrationABC, UserMessageCountPerHourMigration) # 11.01.2023 #168 - 0.3.1

View File

@@ -12,7 +12,6 @@ from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
class StartupModuleExtension(StartupExtensionABC): class StartupModuleExtension(StartupExtensionABC):
def __init__(self): def __init__(self):
self._config: Optional[ConfigurationABC] = None self._config: Optional[ConfigurationABC] = None
self._feature_flags: Optional[FeatureFlagsSettings] = None self._feature_flags: Optional[FeatureFlagsSettings] = None
@@ -33,7 +32,7 @@ class StartupModuleExtension(StartupExtensionABC):
continue continue
Console.set_foreground_color(ForegroundColorEnum.green) Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line(f'[{__name__}] Loaded module: {module_type}') Console.write_line(f"[{__name__}] Loaded module: {module_type}")
Console.color_reset() Console.color_reset()
module.configure_configuration(self._config, env) module.configure_configuration(self._config, env)
module.configure_services(services, env) module.configure_services(services, env)

View File

@@ -16,38 +16,45 @@ from modules.permission.configuration.permission_settings import PermissionSetti
class StartupSettingsExtension(StartupExtensionABC): class StartupSettingsExtension(StartupExtensionABC):
def __init__(self): def __init__(self):
self._start_time = datetime.now() self._start_time = datetime.now()
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC): def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC):
# this shit has to be done here because we need settings in subsequent startup extensions # 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(os.path.dirname(os.path.realpath(__file__)))
configuration.add_environment_variables('KDB_') configuration.add_environment_variables("KDB_")
configuration.add_environment_variables('DISCORD_') configuration.add_environment_variables("DISCORD_")
configuration.add_json_file(f'config/appsettings.json', optional=False) configuration.add_json_file(f"config/appsettings.json", optional=False)
configuration.add_json_file(f'config/appsettings.{environment.environment_name}.json', optional=True) configuration.add_json_file(f"config/appsettings.{environment.environment_name}.json", optional=True)
configuration.add_json_file(f'config/appsettings.{environment.host_name}.json', optional=True) configuration.add_json_file(f"config/appsettings.{environment.host_name}.json", optional=True)
# load feature-flags # load feature-flags
configuration.add_json_file(f'config/feature-flags.json', optional=False) configuration.add_json_file(f"config/feature-flags.json", optional=False)
configuration.add_json_file(f"config/feature-flags.{environment.environment_name}.json", optional=True)
configuration.add_json_file(f"config/feature-flags.{environment.host_name}.json", optional=True)
configuration.add_configuration('Startup_StartTime', str(self._start_time)) configuration.add_configuration("Startup_StartTime", str(self._start_time))
self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, BaseSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BaseSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, BootLogSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BootLogSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, LevelSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, LevelSettings, lambda x: x.servers, lambda x: x.id)
self._configure_settings_with_sub_settings(configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(
self._configure_settings_with_sub_settings(configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key) configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id
)
self._configure_settings_with_sub_settings(
configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key
)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
pass pass
@staticmethod @staticmethod
def _configure_settings_with_sub_settings(config: ConfigurationABC, settings: Type, list_atr: Callable, atr: Callable): def _configure_settings_with_sub_settings(
config: ConfigurationABC, settings: Type, list_atr: Callable, atr: Callable
):
settings: Optional[settings] = config.get_configuration(settings) settings: Optional[settings] = config.get_configuration(settings)
if settings is None: if settings is None:
return return
for sub_settings in list_atr(settings): for sub_settings in list_atr(settings):
config.add_configuration(f'{type(sub_settings).__name__}_{atr(sub_settings)}', sub_settings) config.add_configuration(f"{type(sub_settings).__name__}_{atr(sub_settings)}", sub_settings)

View File

@@ -6,7 +6,7 @@
"not_implemented_yet": "Ey Alter, das kann ich noch nicht...", "not_implemented_yet": "Ey Alter, das kann ich noch nicht...",
"presence": { "presence": {
"booting": "{} Ich fahre gerade hoch...", "booting": "{} Ich fahre gerade hoch...",
"running": "{} Behalte Ruhe und iss Kekse :D", "running": "{} Ich esse Kekse :D",
"restart": "{} Muss neue Kekse holen...", "restart": "{} Muss neue Kekse holen...",
"shutdown": "{} Ich werde bestimmt wieder kommen..." "shutdown": "{} Ich werde bestimmt wieder kommen..."
}, },
@@ -72,11 +72,6 @@
} }
}, },
"modules": { "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": { "auto_role": {
"list": { "list": {
"title": "Beobachtete Nachrichten:", "title": "Beobachtete Nachrichten:",
@@ -113,7 +108,7 @@
"error": { "error": {
"not_found": "Regel für auto-role {} nicht gefunden!", "not_found": "Regel für auto-role {} nicht gefunden!",
"emoji_not_found": "Emoji {} für auto-role Regel {} nicht gefunden!", "emoji_not_found": "Emoji {} für auto-role Regel {} nicht gefunden!",
"rule_not_found": "Rolle {} für auto-role Regel {} nicht gefunden!", "role_not_found": "Rolle {} für auto-role Regel {} nicht gefunden!",
"already_exists": "Regel für auto-role {} existiert bereits!" "already_exists": "Regel für auto-role {} existiert bereits!"
} }
}, },
@@ -132,17 +127,17 @@
"purge_message": "Na gut..., ich lösche alle Nachrichten wenns sein muss." "purge_message": "Na gut..., ich lösche alle Nachrichten wenns sein muss."
}, },
"base": { "base": {
"technician_error_message": "Es gab ein Fehler mit dem Event: {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.UUID: {}", "technician_error_message": "Es gab ein Fehler mit dem Event: {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.\nUUID: {}",
"technician_command_error_message": "Es gab ein Fehler mit dem Befehl: {} ausgelöst von {} -> {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.UUID: {}", "technician_command_error_message": "Es gab ein Fehler mit dem Befehl: {} ausgelöst von {} -> {}\nDatum und Zeit: {}\nSchau bitte ins log für Details.\nUUID: {}",
"welcome_message": "Hello There!\nIch heiße dich bei {} herzlichst willkommen!", "welcome_message": "Hello There!\nIch heiße dich bei {} herzlichst Willkommen!",
"welcome_message_for_team": "{} hat gerade das Irrenhaus betreten.", "welcome_message_for_team": "{} hat gerade das Irrenhaus betreten.",
"goodbye_message": "Schade das du uns so schnell verlässt :(", "goodbye_message": "Schade, dass du uns so schnell verlässt :(",
"afk_command_channel_missing_message": "Zu unfähig einem Sprachkanal beizutreten?", "afk_command_channel_missing_message": "Zu unfähig einem Sprachkanal beizutreten?",
"afk_command_move_message": "Ich verschiebe dich ja schon... (◔_◔)", "afk_command_move_message": "Ich verschiebe dich ja schon... (◔_◔)",
"member_joined_help_voice_channel": "{} braucht hilfe, bitte kümmer dich drum :D", "member_joined_help_voice_channel": "{} braucht hilfe, bitte kümmer dich drum :D",
"pong": "Pong", "pong": "Pong",
"info": { "info": {
"title": "Gismo", "title": "Krümmelmonster",
"description": "Informationen über mich", "description": "Informationen über mich",
"fields": { "fields": {
"version": "Version", "version": "Version",
@@ -156,8 +151,17 @@
}, },
"footer": "" "footer": ""
}, },
"user_info": { "mass_move": {
"fields": { "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": {
"atr": {
"id": "Id", "id": "Id",
"name": "Name", "name": "Name",
"discord_join": "Discord beigetreten am", "discord_join": "Discord beigetreten am",
@@ -169,27 +173,53 @@
"lefts": "Abgänge", "lefts": "Abgänge",
"warnings": "Verwarnungen" "warnings": "Verwarnungen"
}, },
"footer": "" "info": {
"footer": ""
},
"get": {
"xp": "{} hat {} xp",
"ontime": "{} war insgesamt {} Stunden aktiv in einem Sprachkanal"
},
"set": {
"xp": "{} hat nun {} xp",
"error": {
"value_type_not_numeric": "Der angegebende Wert ist keine Ganzzahl! :(",
"type_error": "Der angegebene Wert ist keine Zahl! :("
}
},
"add": {
"xp": "Die {} von {} wurden um {} erhöht"
},
"remove": {
"xp": "Die {} von {} wurden um {} verringert"
},
"reset": {
"xp": "Die {} von {} wurden entfernt",
"ontime": "Die {} von {} wurden entfernt"
},
"error": {
"atr_not_found": "Das Attribut {} konnte nicht gefunden werden :("
}
} }
}, },
"boot_log": { "boot_log": {
"login_message": "Ich bin on the line :D\nDer Scheiß hat {} Sekunden gedauert" "login_message": "Ich bin on the line :D\nDer Scheiß hat {} Sekunden gedauert"
}, },
"level": { "level": {
"new_level_message": "<@{}> ist nun Level {}", "new_level_message": "{} ist nun Level {}",
"seeding_started": "Levelsystem wird neu geladen.", "seeding_started": "Levelsystem wird neu geladen...",
"seeding_failed": "Levelsystem konnte nicht neu geladen werden.", "seeding_failed": "Levelsystem konnte nicht neu geladen werden :(",
"seeding_finished": "Levelsystem wurde Erfolgreich neu geladen.", "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen :)",
"error": { "error": {
"nothing_found": "Keine Level Einträge gefunden.", "nothing_found": "Keine Level Einträge gefunden.",
"level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!",
"level_with_xp_already_exists": "Das Level {} hat bereits die Mindest XP {}!" "level_with_xp_already_exists": "Das Level {} hat bereits die Mindest-XP {}!"
}, },
"list": { "list": {
"title": "Level:", "title": "Level:",
"description": "Konfigurierte Level:", "description": "Konfigurierte Level:",
"name": "Name", "name": "Name",
"min_xp": "Mindest XP", "min_xp": "Mindest-XP",
"permission_int": "Berechtigungen" "permission_int": "Berechtigungen"
}, },
"create": { "create": {
@@ -249,6 +279,11 @@
"failed": "Statistik kann nicht gelöscht werden :(", "failed": "Statistik kann nicht gelöscht werden :(",
"success": "Statistik wurde gelöscht :D" "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": { "api": {

View File

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

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_api.service' __title__ = "bot_api.abc"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -15,75 +15,102 @@ from bot_data.model.auth_user import AuthUser
class AuthServiceABC(ABC): class AuthServiceABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod @abstractmethod
def __init__(self): pass def generate_token(self, user: AuthUser) -> str:
pass
@abstractmethod @abstractmethod
def generate_token(self, user: AuthUser) -> str: pass def decode_token(self, token: str) -> dict:
pass
@abstractmethod @abstractmethod
def decode_token(self, token: str) -> dict: pass def get_decoded_token_from_request(self) -> dict:
pass
@abstractmethod @abstractmethod
def get_decoded_token_from_request(self) -> dict: pass def find_decoded_token_from_request(self) -> Optional[dict]:
pass
@abstractmethod @abstractmethod
def find_decoded_token_from_request(self) -> Optional[dict]: pass async def get_all_auth_users_async(self) -> List[AuthUserDTO]:
pass
@abstractmethod @abstractmethod
async def get_all_auth_users_async(self) -> List[AuthUserDTO]: pass async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO:
pass
@abstractmethod @abstractmethod
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO:
pass
@abstractmethod @abstractmethod
async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO: pass async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO:
pass
@abstractmethod @abstractmethod
async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass async def add_auth_user_async(self, user_dto: AuthUserDTO):
pass
@abstractmethod @abstractmethod
async def add_auth_user_async(self, user_dto: AuthUserDTO): pass async def add_auth_user_by_oauth_async(self, dto: OAuthDTO):
pass
@abstractmethod @abstractmethod
async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): pass async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO:
pass
@abstractmethod @abstractmethod
async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO: pass async def update_user_async(self, update_user_dto: UpdateAuthUserDTO):
pass
@abstractmethod @abstractmethod
async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO):
pass
@abstractmethod @abstractmethod
async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): pass async def delete_auth_user_by_email_async(self, email: str):
pass
@abstractmethod @abstractmethod
async def delete_auth_user_by_email_async(self, email: str): pass async def delete_auth_user_async(self, user_dto: AuthUserDTO):
pass
@abstractmethod @abstractmethod
async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass async def verify_login(self, token_str: str) -> bool:
pass
@abstractmethod @abstractmethod
async def verify_login(self, token_str: str) -> bool: pass async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO:
pass
@abstractmethod @abstractmethod
async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass async def login_discord_async(self, oauth_dto: AuthUserDTO) -> TokenDTO:
pass
@abstractmethod @abstractmethod
async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO:
pass
@abstractmethod @abstractmethod
async def revoke_async(self, token_dto: TokenDTO): pass async def revoke_async(self, token_dto: TokenDTO):
pass
@abstractmethod @abstractmethod
async def confirm_email_async(self, id: str) -> bool: pass async def confirm_email_async(self, id: str) -> bool:
pass
@abstractmethod @abstractmethod
async def forgot_password_async(self, email: str): pass async def forgot_password_async(self, email: str):
pass
@abstractmethod @abstractmethod
async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: pass async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO:
pass
@abstractmethod @abstractmethod
async def reset_password_async(self, rp_dto: ResetPasswordDTO): pass async def reset_password_async(self, rp_dto: ResetPasswordDTO):
pass

View File

@@ -2,12 +2,14 @@ from abc import ABC, abstractmethod
class DtoABC(ABC): class DtoABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod @abstractmethod
def __init__(self): pass def from_dict(self, values: dict):
pass
@abstractmethod @abstractmethod
def from_dict(self, values: dict): pass def to_dict(self) -> dict:
pass
@abstractmethod
def to_dict(self) -> dict: pass

View File

@@ -2,15 +2,8 @@ from abc import ABC, abstractmethod
class SelectCriteriaABC(ABC): class SelectCriteriaABC(ABC):
@abstractmethod @abstractmethod
def __init__( def __init__(self, page_index: int, page_size: int, sort_direction: str, sort_column: str):
self,
page_index: int,
page_size: int,
sort_direction: str,
sort_column: str
):
self.page_index = page_index self.page_index = page_index
self.page_size = page_size self.page_size = page_size
self.sort_direction = sort_direction self.sort_direction = sort_direction

View File

@@ -6,11 +6,12 @@ from bot_api.abc.dto_abc import DtoABC
class TransformerABC: class TransformerABC:
@staticmethod
@abstractmethod
def to_db(dto: DtoABC) -> TableABC:
pass
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def to_db(dto: DtoABC) -> TableABC: pass def to_dto(db: TableABC) -> DtoABC:
pass
@staticmethod
@abstractmethod
def to_dto(db: TableABC) -> DtoABC: pass

View File

@@ -25,18 +25,18 @@ from bot_api.route.route import Route
class Api(Flask): class Api(Flask):
def __init__( def __init__(
self, self,
logger: ApiLogger, logger: ApiLogger,
services: ServiceProviderABC, services: ServiceProviderABC,
api_settings: ApiSettings, api_settings: ApiSettings,
frontend_settings: FrontendSettings, frontend_settings: FrontendSettings,
auth_settings: AuthenticationSettings, auth_settings: AuthenticationSettings,
*args, **kwargs *args,
**kwargs,
): ):
if not args: if not args:
kwargs.setdefault('import_name', __name__) kwargs.setdefault("import_name", __name__)
Flask.__init__(self, *args, **kwargs) Flask.__init__(self, *args, **kwargs)
@@ -56,17 +56,21 @@ class Api(Flask):
self.register_error_handler(exc_class, self.handle_exception) self.register_error_handler(exc_class, self.handle_exception)
# websockets # websockets
self._socketio = SocketIO(self, cors_allowed_origins='*', path='/api/socket.io') self._socketio = SocketIO(self, cors_allowed_origins="*", path="/api/socket.io")
self._socketio.on_event('connect', self.on_connect) self._socketio.on_event("connect", self.on_connect)
self._socketio.on_event('disconnect', self.on_disconnect) self._socketio.on_event("disconnect", self.on_disconnect)
self._requests = {} self._requests = {}
@staticmethod @staticmethod
def _get_methods_from_registered_route() -> Union[list[str], str]: def _get_methods_from_registered_route() -> Union[list[str], str]:
methods = ['Unknown'] methods = ["Unknown"]
if request.path in Route.registered_routes and len(Route.registered_routes[request.path]) >= 1 and 'methods' in Route.registered_routes[request.path][1]: if (
methods = Route.registered_routes[request.path][1]['methods'] request.path in Route.registered_routes
and len(Route.registered_routes[request.path]) >= 1
and "methods" in Route.registered_routes[request.path][1]
):
methods = Route.registered_routes[request.path][1]["methods"]
if len(methods) == 1: if len(methods) == 1:
return methods[0] return methods[0]
@@ -77,7 +81,7 @@ class Api(Flask):
route = f[0] route = f[0]
kwargs = f[1] kwargs = f[1]
cls = None cls = None
qual_name_split = route.__qualname__.split('.') qual_name_split = route.__qualname__.split(".")
if len(qual_name_split) > 0: if len(qual_name_split) > 0:
cls_type = vars(sys.modules[route.__module__])[qual_name_split[0]] cls_type = vars(sys.modules[route.__module__])[qual_name_split[0]]
cls = self._services.get_service(cls_type) cls = self._services.get_service(cls_type)
@@ -87,7 +91,7 @@ class Api(Flask):
self.route(path, **kwargs)(partial_f) self.route(path, **kwargs)(partial_f)
def handle_exception(self, e: Exception): def handle_exception(self, e: Exception):
self._logger.error(__name__, f'Caught error', e) self._logger.error(__name__, f"Caught error", e)
if isinstance(e, ServiceException): if isinstance(e, ServiceException):
ex: ServiceException = e ex: ServiceException = e
@@ -100,7 +104,7 @@ class Api(Flask):
return jsonify(error.to_dict()), 404 return jsonify(error.to_dict()), 404
else: else:
tracking_id = uuid.uuid4() tracking_id = uuid.uuid4()
user_message = f'Tracking Id: {tracking_id}' user_message = f"Tracking Id: {tracking_id}"
self._logger.error(__name__, user_message, e) self._logger.error(__name__, user_message, e)
error = ErrorDTO(None, user_message) error = ErrorDTO(None, user_message)
return jsonify(error.to_dict()), 400 return jsonify(error.to_dict()), 400
@@ -110,34 +114,42 @@ class Api(Flask):
self._requests[request] = request_id self._requests[request] = request_id
method = request.access_control_request_method method = request.access_control_request_method
self._logger.info(__name__, f'Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}') self._logger.info(
__name__,
f"Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}",
)
headers = str(request.headers).replace('\n', '\n\t\t') headers = str(request.headers).replace("\n", "\n\t\t")
data = request.get_data() data = request.get_data()
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8")) data = "" if len(data) == 0 else str(data.decode(encoding="utf-8"))
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}') text = textwrap.dedent(
f"Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}"
)
self._logger.trace(__name__, text) self._logger.trace(__name__, text)
def after_request_hook(self, response: Response): def after_request_hook(self, response: Response):
method = request.access_control_request_method method = request.access_control_request_method
request_id = f'{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}' request_id = f"{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}"
if request in self._requests: if request in self._requests:
request_id = self._requests[request] request_id = self._requests[request]
self._logger.info(__name__, f'Answered {request_id}') self._logger.info(__name__, f"Answered {request_id}")
headers = str(request.headers).replace('\n', '\n\t\t') headers = str(request.headers).replace("\n", "\n\t\t")
data = request.get_data() data = request.get_data()
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8")) data = "" if len(data) == 0 else str(data.decode(encoding="utf-8"))
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}') text = textwrap.dedent(f"Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}")
self._logger.trace(__name__, text) self._logger.trace(__name__, text)
return response return response
def start(self): def start(self):
self._logger.info(__name__, f'Starting API {self._api_settings.host}:{self._api_settings.port}') self._logger.info(
__name__,
f"Starting API {self._api_settings.host}:{self._api_settings.port}",
)
self._register_routes() self._register_routes()
self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key) self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key)
# from waitress import serve # from waitress import serve
@@ -146,11 +158,11 @@ class Api(Flask):
wsgi.server( wsgi.server(
eventlet.listen((self._api_settings.host, self._api_settings.port)), eventlet.listen((self._api_settings.host, self._api_settings.port)),
self, self,
log_output=False log_output=False,
) )
def on_connect(self): def on_connect(self):
self._logger.info(__name__, f'Client connected') self._logger.info(__name__, f"Client connected")
def on_disconnect(self): def on_disconnect(self):
self._logger.info(__name__, f'Client disconnected') self._logger.info(__name__, f"Client disconnected")

View File

@@ -23,16 +23,15 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
class ApiModule(ModuleABC): class ApiModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC): def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module) ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module)
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
cwd = env.working_directory cwd = env.working_directory
env.set_working_directory(os.path.dirname(os.path.realpath(__file__))) env.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
config.add_json_file(f'config/apisettings.json', optional=False) config.add_json_file(f"config/apisettings.json", optional=False)
config.add_json_file(f'config/apisettings.{env.environment_name}.json', optional=True) config.add_json_file(f"config/apisettings.{env.environment_name}.json", optional=True)
config.add_json_file(f'config/apisettings.{env.host_name}.json', optional=True) config.add_json_file(f"config/apisettings.{env.host_name}.json", optional=True)
env.set_working_directory(cwd) env.set_working_directory(cwd)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):

View File

@@ -5,12 +5,7 @@ from bot_api.logging.api_logger import ApiLogger
class ApiThread(threading.Thread): class ApiThread(threading.Thread):
def __init__(self, logger: ApiLogger, api: Api):
def __init__(
self,
logger: ApiLogger,
api: Api
):
threading.Thread.__init__(self, daemon=True) threading.Thread.__init__(self, daemon=True)
self._logger = logger self._logger = logger
@@ -18,7 +13,7 @@ class ApiThread(threading.Thread):
def run(self) -> None: def run(self) -> None:
try: try:
self._logger.trace(__name__, f'Try to start {type(self._api).__name__}') self._logger.trace(__name__, f"Try to start {type(self._api).__name__}")
self._api.start() self._api.start()
except Exception as e: except Exception as e:
self._logger.error(__name__, 'Start failed', e) self._logger.error(__name__, "Start failed", e)

View File

@@ -11,7 +11,6 @@ from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
class AppApiExtension(ApplicationExtensionABC): class AppApiExtension(ApplicationExtensionABC):
def __init__(self): def __init__(self):
ApplicationExtensionABC.__init__(self) ApplicationExtensionABC.__init__(self)

View File

@@ -4,7 +4,7 @@
"Version": { "Version": {
"Major": "0", "Major": "0",
"Minor": "3", "Minor": "3",
"Micro": "dev70" "Micro": "1"
}, },
"Author": "", "Author": "",
"AuthorEmail": "", "AuthorEmail": "",
@@ -16,10 +16,10 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"cpl-core==2022.10.0.post7" "cpl-core==2022.12.0"
], ],
"DevDependencies": [ "DevDependencies": [
"cpl-cli==2022.10.0" "cpl-cli==2022.12.0"
], ],
"PythonVersion": ">=3.10.4", "PythonVersion": ">=3.10.4",
"PythonPath": {}, "PythonPath": {},

View File

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

View File

@@ -5,12 +5,11 @@ from cpl_core.console import Console
class ApiSettings(ConfigurationModelABC): class ApiSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._port = 80 self._port = 80
self._host = '' self._host = ""
self._redirect_to_https = False self._redirect_to_https = False
@property @property
@@ -27,9 +26,9 @@ class ApiSettings(ConfigurationModelABC):
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._port = int(settings['Port']) self._port = int(settings["Port"])
self._host = settings['Host'] self._host = settings["Host"]
self._redirect_to_https = bool(settings['RedirectToHTTPS']) self._redirect_to_https = bool(settings["RedirectToHTTPS"])
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -6,13 +6,12 @@ from cpl_core.console import Console
class AuthenticationSettings(ConfigurationModelABC): class AuthenticationSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._secret_key = '' self._secret_key = ""
self._issuer = '' self._issuer = ""
self._audience = '' self._audience = ""
self._token_expire_time = 0 self._token_expire_time = 0
self._refresh_token_expire_time = 0 self._refresh_token_expire_time = 0
@@ -38,11 +37,11 @@ class AuthenticationSettings(ConfigurationModelABC):
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._secret_key = settings['SecretKey'] self._secret_key = settings["SecretKey"]
self._issuer = settings['Issuer'] self._issuer = settings["Issuer"]
self._audience = settings['Audience'] self._audience = settings["Audience"]
self._token_expire_time = int(settings['TokenExpireTime']) self._token_expire_time = int(settings["TokenExpireTime"])
self._refresh_token_expire_time = int(settings['RefreshTokenExpireTime']) self._refresh_token_expire_time = int(settings["RefreshTokenExpireTime"])
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -6,15 +6,14 @@ from cpl_query.extension import List
class DiscordAuthenticationSettings(ConfigurationModelABC): class DiscordAuthenticationSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._client_secret = '' self._client_secret = ""
self._redirect_url = '' self._redirect_url = ""
self._scope = List() self._scope = List()
self._token_url = '' self._token_url = ""
self._auth_url = '' self._auth_url = ""
@property @property
def client_secret(self) -> str: def client_secret(self) -> str:
@@ -38,11 +37,11 @@ class DiscordAuthenticationSettings(ConfigurationModelABC):
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._client_secret = settings['ClientSecret'] self._client_secret = settings["ClientSecret"]
self._redirect_url = settings['RedirectURL'] self._redirect_url = settings["RedirectURL"]
self._scope = List(str, settings['Scope']) self._scope = List(str, settings["Scope"])
self._token_url = settings['TokenURL'] self._token_url = settings["TokenURL"]
self._auth_url = settings['AuthURL'] self._auth_url = settings["AuthURL"]
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -5,11 +5,10 @@ from cpl_core.console import Console
class FrontendSettings(ConfigurationModelABC): class FrontendSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._url = '' self._url = ""
@property @property
def url(self) -> str: def url(self) -> str:
@@ -17,7 +16,7 @@ class FrontendSettings(ConfigurationModelABC):
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._url = settings['URL'] self._url = settings["URL"]
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -5,13 +5,7 @@ from cpl_cli.configuration.version_settings_name_enum import VersionSettingsName
class VersionSettings(ConfigurationModelABC): class VersionSettings(ConfigurationModelABC):
def __init__(self, major: str = None, minor: str = None, micro: str = None):
def __init__(
self,
major: str = None,
minor: str = None,
micro: str = None
):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._major: Optional[str] = major self._major: Optional[str] = major
@@ -32,15 +26,15 @@ class VersionSettings(ConfigurationModelABC):
def to_str(self) -> str: def to_str(self) -> str:
if self._micro is None: if self._micro is None:
return f'{self._major}.{self._minor}' return f"{self._major}.{self._minor}"
else: else:
return f'{self._major}.{self._minor}.{self._micro}' return f"{self._major}.{self._minor}.{self._micro}"
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
self._major = settings[VersionSettingsNameEnum.major.value] self._major = settings[VersionSettingsNameEnum.major.value]
self._minor = settings[VersionSettingsNameEnum.minor.value] self._minor = settings[VersionSettingsNameEnum.minor.value]
micro = settings[VersionSettingsNameEnum.micro.value] micro = settings[VersionSettingsNameEnum.micro.value]
if micro != '': if micro != "":
self._micro = micro self._micro = micro
def to_dict(self) -> dict: def to_dict(self) -> dict:

View File

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

View File

@@ -20,18 +20,18 @@ from bot_data.model.auth_role_enum import AuthRoleEnum
class AuthController: class AuthController:
BasePath = '/api/auth' BasePath = "/api/auth"
def __init__( def __init__(
self, self,
config: ConfigurationABC, config: ConfigurationABC,
env: ApplicationEnvironmentABC, env: ApplicationEnvironmentABC,
logger: ApiLogger, logger: ApiLogger,
t: TranslatePipe, t: TranslatePipe,
api: Api, api: Api,
mail_settings: EMailClientSettings, mail_settings: EMailClientSettings,
mailer: EMailClientABC, mailer: EMailClientABC,
auth_service: AuthServiceABC auth_service: AuthServiceABC,
): ):
self._config = config self._config = config
self._env = env self._env = env
@@ -42,55 +42,57 @@ class AuthController:
self._mailer = mailer self._mailer = mailer
self._auth_service = auth_service self._auth_service = auth_service
@Route.get(f'{BasePath}/users') @Route.get(f"{BasePath}/users")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def get_all_users(self) -> Response: async def get_all_users(self) -> Response:
result = await self._auth_service.get_all_auth_users_async() result = await self._auth_service.get_all_auth_users_async()
return jsonify(result.select(lambda x: x.to_dict())) return jsonify(result.select(lambda x: x.to_dict()).to_list())
@Route.post(f'{BasePath}/users/get/filtered') @Route.post(f"{BasePath}/users/get/filtered")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def get_filtered_users(self) -> Response: async def get_filtered_users(self) -> Response:
dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) dto: AuthUserSelectCriteria = JSONProcessor.process(
AuthUserSelectCriteria, request.get_json(force=True, silent=True)
)
result = await self._auth_service.get_filtered_auth_users_async(dto) result = await self._auth_service.get_filtered_auth_users_async(dto)
result.result = result.result.select(lambda x: x.to_dict()) result.result = result.result.select(lambda x: x.to_dict()).to_list()
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.get(f'{BasePath}/users/get/<email>') @Route.get(f"{BasePath}/users/get/<email>")
@Route.authorize @Route.authorize
async def get_user_from_email(self, email: str) -> Response: async def get_user_from_email(self, email: str) -> Response:
result = await self._auth_service.get_auth_user_by_email_async(email) result = await self._auth_service.get_auth_user_by_email_async(email)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.get(f'{BasePath}/users/find/<email>') @Route.get(f"{BasePath}/users/find/<email>")
@Route.authorize @Route.authorize
async def find_user_from_email(self, email: str) -> Response: async def find_user_from_email(self, email: str) -> Response:
result = await self._auth_service.find_auth_user_by_email_async(email) result = await self._auth_service.find_auth_user_by_email_async(email)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.post(f'{BasePath}/register') @Route.post(f"{BasePath}/register")
async def register(self): async def register(self):
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
await self._auth_service.add_auth_user_async(dto) await self._auth_service.add_auth_user_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/register-by-id/<id>') @Route.post(f"{BasePath}/register-by-id/<id>")
async def register_id(self, id: str): async def register_id(self, id: str):
result = await self._auth_service.confirm_email_async(id) result = await self._auth_service.confirm_email_async(id)
return jsonify(result) return jsonify(result)
@Route.post(f'{BasePath}/login') @Route.post(f"{BasePath}/login")
async def login(self) -> Response: async def login(self) -> Response:
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
result = await self._auth_service.login_async(dto) result = await self._auth_service.login_async(dto)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.get(f'{BasePath}/verify-login') @Route.get(f"{BasePath}/verify-login")
async def verify_login(self): async def verify_login(self):
token = None token = None
result = False result = False
if 'Authorization' in request.headers: if "Authorization" in request.headers:
bearer = request.headers.get('Authorization') bearer = request.headers.get("Authorization")
token = bearer.split()[1] token = bearer.split()[1]
if token is not None: if token is not None:
@@ -98,58 +100,58 @@ class AuthController:
return jsonify(result) return jsonify(result)
@Route.post(f'{BasePath}/forgot-password/<email>') @Route.post(f"{BasePath}/forgot-password/<email>")
async def forgot_password(self, email: str): async def forgot_password(self, email: str):
await self._auth_service.forgot_password_async(email) await self._auth_service.forgot_password_async(email)
return '', 200 return "", 200
@Route.post(f'{BasePath}/confirm-forgot-password/<id>') @Route.post(f"{BasePath}/confirm-forgot-password/<id>")
async def confirm_forgot_password(self, id: str): async def confirm_forgot_password(self, id: str):
result = await self._auth_service.confirm_forgot_password_async(id) result = await self._auth_service.confirm_forgot_password_async(id)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.post(f'{BasePath}/reset-password') @Route.post(f"{BasePath}/reset-password")
async def reset_password(self): async def reset_password(self):
dto: ResetPasswordDTO = JSONProcessor.process(ResetPasswordDTO, request.get_json(force=True, silent=True)) dto: ResetPasswordDTO = JSONProcessor.process(ResetPasswordDTO, request.get_json(force=True, silent=True))
await self._auth_service.reset_password_async(dto) await self._auth_service.reset_password_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/update-user') @Route.post(f"{BasePath}/update-user")
@Route.authorize @Route.authorize
async def update_user(self): async def update_user(self):
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
await self._auth_service.update_user_async(dto) await self._auth_service.update_user_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/update-user-as-admin') @Route.post(f"{BasePath}/update-user-as-admin")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def update_user_as_admin(self): async def update_user_as_admin(self):
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
await self._auth_service.update_user_as_admin_async(dto) await self._auth_service.update_user_as_admin_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/refresh') @Route.post(f"{BasePath}/refresh")
@Route.authorize @Route.authorize
async def refresh(self) -> Response: async def refresh(self) -> Response:
dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True))
result = await self._auth_service.refresh_async(dto) result = await self._auth_service.refresh_async(dto)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.post(f'{BasePath}/revoke') @Route.post(f"{BasePath}/revoke")
async def revoke(self): async def revoke(self):
dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True))
await self._auth_service.revoke_async(dto) await self._auth_service.revoke_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/delete-user') @Route.post(f"{BasePath}/delete-user")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def delete_user(self): async def delete_user(self):
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
await self._auth_service.delete_auth_user_async(dto) await self._auth_service.delete_auth_user_async(dto)
return '', 200 return "", 200
@Route.post(f'{BasePath}/delete-user-by-mail/<email>') @Route.post(f"{BasePath}/delete-user-by-mail/<email>")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def delete_user_by_mail(self, email: str): async def delete_user_by_mail(self, email: str):
await self._auth_service.delete_auth_user_by_email_async(email) await self._auth_service.delete_auth_user_by_email_async(email)
return '', 200 return "", 200

View File

@@ -13,7 +13,9 @@ from requests_oauthlib import OAuth2Session
from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.abc.auth_service_abc import AuthServiceABC
from bot_api.api import Api from bot_api.api import Api
from bot_api.configuration.discord_authentication_settings import DiscordAuthenticationSettings from bot_api.configuration.discord_authentication_settings import (
DiscordAuthenticationSettings,
)
from bot_api.json_processor import JSONProcessor from bot_api.json_processor import JSONProcessor
from bot_api.logging.api_logger import ApiLogger from bot_api.logging.api_logger import ApiLogger
from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_dto import AuthUserDTO
@@ -22,24 +24,24 @@ from bot_api.route.route import Route
from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_role_enum import AuthRoleEnum
# Disable SSL requirement # Disable SSL requirement
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
class AuthDiscordController: class AuthDiscordController:
BasePath = '/api/auth/discord' BasePath = "/api/auth/discord"
def __init__( def __init__(
self, self,
auth_settings: DiscordAuthenticationSettings, auth_settings: DiscordAuthenticationSettings,
config: ConfigurationABC, config: ConfigurationABC,
env: ApplicationEnvironmentABC, env: ApplicationEnvironmentABC,
logger: ApiLogger, logger: ApiLogger,
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
t: TranslatePipe, t: TranslatePipe,
api: Api, api: Api,
mail_settings: EMailClientSettings, mail_settings: EMailClientSettings,
mailer: EMailClientABC, mailer: EMailClientABC,
auth_service: AuthServiceABC auth_service: AuthServiceABC,
): ):
self._auth_settings = auth_settings self._auth_settings = auth_settings
self._config = config self._config = config
@@ -53,37 +55,59 @@ class AuthDiscordController:
self._auth_service = auth_service self._auth_service = auth_service
def _get_user_from_discord_response(self) -> dict: def _get_user_from_discord_response(self) -> dict:
discord = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, state=request.args.get('state'), scope=self._auth_settings.scope) discord = OAuth2Session(
self._bot.user.id,
redirect_uri=self._auth_settings.redirect_url,
state=request.args.get("state"),
scope=self._auth_settings.scope,
)
token = discord.fetch_token( token = discord.fetch_token(
self._auth_settings.token_url, self._auth_settings.token_url,
client_secret=CredentialManager.decrypt(self._auth_settings.client_secret), client_secret=CredentialManager.decrypt(self._auth_settings.client_secret),
authorization_response=request.url, authorization_response=request.url,
) )
discord = OAuth2Session(self._bot.user.id, token=token) discord = OAuth2Session(self._bot.user.id, token=token)
return discord.get('https://discordapp.com/api' + '/users/@me').json() return discord.get("https://discordapp.com/api" + "/users/@me").json()
@Route.get(f'{BasePath}/get-url') @Route.get(f"{BasePath}/get-url")
async def get_url(self): async def get_url(self):
oauth = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, scope=self._auth_settings.scope) oauth = OAuth2Session(
self._bot.user.id,
redirect_uri=self._auth_settings.redirect_url,
scope=self._auth_settings.scope,
)
login_url, state = oauth.authorization_url(self._auth_settings.auth_url) login_url, state = oauth.authorization_url(self._auth_settings.auth_url)
return jsonify({'loginUrl': login_url}) return jsonify({"loginUrl": login_url})
@Route.get(f'{BasePath}/create-user') @Route.get(f"{BasePath}/create-user")
async def discord_create_user(self) -> Response: async def discord_create_user(self) -> Response:
response = self._get_user_from_discord_response() response = self._get_user_from_discord_response()
result = await self._auth_service.add_auth_user_by_discord_async(AuthUserDTO( result = await self._auth_service.add_auth_user_by_discord_async(
0, AuthUserDTO(
response['username'], 0,
response['discriminator'], response["username"],
response['email'], response["discriminator"],
str(uuid.uuid4()), response["email"],
None, str(uuid.uuid4()),
AuthRoleEnum.normal None,
), response['id']) AuthRoleEnum.normal,
),
response["id"],
)
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.post(f'{BasePath}/register') @Route.get(f"{BasePath}/login")
async def discord_register(self): async def discord_login(self) -> Response:
dto: OAuthDTO = JSONProcessor.process(OAuthDTO, request.get_json(force=True, silent=True)) response = self._get_user_from_discord_response()
await self._auth_service.add_auth_user_by_oauth_async(dto) dto = AuthUserDTO(
return '', 200 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())

View File

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

View File

@@ -14,18 +14,18 @@ from bot_data.model.auth_role_enum import AuthRoleEnum
class ServerController: class ServerController:
BasePath = f'/api/discord/server' BasePath = f"/api/discord/server"
def __init__( def __init__(
self, self,
config: ConfigurationABC, config: ConfigurationABC,
env: ApplicationEnvironmentABC, env: ApplicationEnvironmentABC,
logger: ApiLogger, logger: ApiLogger,
t: TranslatePipe, t: TranslatePipe,
api: Api, api: Api,
mail_settings: EMailClientSettings, mail_settings: EMailClientSettings,
mailer: EMailClientABC, mailer: EMailClientABC,
discord_service: DiscordService discord_service: DiscordService,
): ):
self._config = config self._config = config
self._env = env self._env = env
@@ -36,30 +36,32 @@ class ServerController:
self._mailer = mailer self._mailer = mailer
self._discord_service = discord_service self._discord_service = discord_service
@Route.get(f'{BasePath}/get/servers') @Route.get(f"{BasePath}/get/servers")
@Route.authorize(role=AuthRoleEnum.admin) @Route.authorize(role=AuthRoleEnum.admin)
async def get_all_servers(self) -> Response: async def get_all_servers(self) -> Response:
result = await self._discord_service.get_all_servers() result = await self._discord_service.get_all_servers()
result = result.select(lambda x: x.to_dict()) result = result.select(lambda x: x.to_dict()).to_list()
return jsonify(result) return jsonify(result)
@Route.get(f'{BasePath}/get/servers-by-user') @Route.get(f"{BasePath}/get/servers-by-user")
@Route.authorize @Route.authorize
async def get_all_servers_by_user(self) -> Response: async def get_all_servers_by_user(self) -> Response:
result = await self._discord_service.get_all_servers_by_user() result = await self._discord_service.get_all_servers_by_user()
result = result.select(lambda x: x.to_dict()) result = result.select(lambda x: x.to_dict()).to_list()
return jsonify(result) return jsonify(result)
@Route.post(f'{BasePath}/get/filtered') @Route.post(f"{BasePath}/get/filtered")
@Route.authorize @Route.authorize
async def get_filtered_servers(self) -> Response: async def get_filtered_servers(self) -> Response:
dto: ServerSelectCriteria = JSONProcessor.process(ServerSelectCriteria, request.get_json(force=True, silent=True)) dto: ServerSelectCriteria = JSONProcessor.process(
ServerSelectCriteria, request.get_json(force=True, silent=True)
)
result = await self._discord_service.get_filtered_servers_async(dto) result = await self._discord_service.get_filtered_servers_async(dto)
result.result = result.result.select(lambda x: x.to_dict()) result.result = result.result.select(lambda x: x.to_dict()).to_list()
return jsonify(result.to_dict()) return jsonify(result.to_dict())
@Route.get(f'{BasePath}/get/<id>') @Route.get(f"{BasePath}/get/<id>")
@Route.authorize @Route.authorize
async def get_server_by_id(self, id: int) -> Response: async def get_server_by_id(self, id: int) -> Response:
result = await self._discord_service.get_server_by_id_async(id) result = await self._discord_service.get_server_by_id_async(id).to_list()
return jsonify(result.to_dict()) return jsonify(result.to_dict())

View File

@@ -15,18 +15,18 @@ from bot_api.route.route import Route
class GuiController: class GuiController:
BasePath = f'/api/gui' BasePath = f"/api/gui"
def __init__( def __init__(
self, self,
config: ConfigurationABC, config: ConfigurationABC,
env: ApplicationEnvironmentABC, env: ApplicationEnvironmentABC,
logger: ApiLogger, logger: ApiLogger,
t: TranslatePipe, t: TranslatePipe,
api: Api, api: Api,
mail_settings: EMailClientSettings, mail_settings: EMailClientSettings,
mailer: EMailClientABC, mailer: EMailClientABC,
auth_settings: AuthenticationSettings auth_settings: AuthenticationSettings,
): ):
self._config = config self._config = config
self._env = env self._env = env
@@ -37,42 +37,48 @@ class GuiController:
self._mailer = mailer self._mailer = mailer
self._auth_settings = auth_settings self._auth_settings = auth_settings
@Route.get(f'{BasePath}/api-version') @Route.get(f"{BasePath}/api-version")
async def api_version(self): async def api_version(self):
import bot_api import bot_api
version = bot_api.version_info version = bot_api.version_info
return VersionDTO(version.major, version.minor, version.micro).to_dict() return VersionDTO(version.major, version.minor, version.micro).to_dict()
@Route.get(f'{BasePath}/settings') @Route.get(f"{BasePath}/settings")
@Route.authorize @Route.authorize
async def settings(self): async def settings(self):
import bot_api import bot_api
version = bot_api.version_info version = bot_api.version_info
return jsonify(SettingsDTO( return jsonify(
'', SettingsDTO(
VersionDTO(version.major, version.minor, version.micro), "",
os.path.abspath(os.path.join(self._env.working_directory, 'config')), VersionDTO(version.major, version.minor, version.micro),
'/', os.path.abspath(os.path.join(self._env.working_directory, "config")),
'/', "/",
self._auth_settings.token_expire_time, "/",
self._auth_settings.refresh_token_expire_time, self._auth_settings.token_expire_time,
self._mail_settings.user_name, self._auth_settings.refresh_token_expire_time,
self._mail_settings.port, self._mail_settings.user_name,
self._mail_settings.host, self._mail_settings.port,
self._mail_settings.user_name, self._mail_settings.host,
self._mail_settings.user_name, self._mail_settings.user_name,
).to_dict()) self._mail_settings.user_name,
).to_dict()
)
@Route.post(f'{BasePath}/send-test-mail/<email>') @Route.post(f"{BasePath}/send-test-mail/<email>")
@Route.authorize @Route.authorize
async def send_test_mail(self, email: str): async def send_test_mail(self, email: str):
mail = EMail() mail = EMail()
mail.add_header('Mime-Version: 1.0') mail.add_header("Mime-Version: 1.0")
mail.add_header('Content-Type: text/plain; charset=utf-8') mail.add_header("Content-Type: text/plain; charset=utf-8")
mail.add_header('Content-Transfer-Encoding: quoted-printable') mail.add_header("Content-Transfer-Encoding: quoted-printable")
mail.add_receiver(email) mail.add_receiver(email)
mail.subject = self._t.transform('api.api.test_mail.subject') mail.subject = self._t.transform("api.api.test_mail.subject")
mail.body = self._t.transform('api.api.test_mail.message').format(self._env.host_name, self._env.environment_name) mail.body = self._t.transform("api.api.test_mail.message").format(
self._env.host_name, self._env.environment_name
)
self._mailer.send_mail(mail) self._mailer.send_mail(mail)
return '', 200 return "", 200

View File

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

View File

@@ -4,7 +4,6 @@ from bot_api.api_thread import ApiThread
class BotApiOnReadyEvent(OnReadyABC): class BotApiOnReadyEvent(OnReadyABC):
def __init__(self, api: ApiThread): def __init__(self, api: ApiThread):
OnReadyABC.__init__(self) OnReadyABC.__init__(self)
self._api = api self._api = api

View File

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

View File

@@ -2,7 +2,6 @@ from bot_api.exception.service_error_code_enum import ServiceErrorCode
class ServiceException(Exception): class ServiceException(Exception):
def __init__(self, error_code: ServiceErrorCode, message: str, *args): def __init__(self, error_code: ServiceErrorCode, message: str, *args):
Exception.__init__(self, *args) Exception.__init__(self, *args)
@@ -10,4 +9,4 @@ class ServiceException(Exception):
self.message = message self.message = message
def get_detailed_message(self) -> str: def get_detailed_message(self) -> str:
return f'ServiceException - ErrorCode: {self.error_code} - ErrorMessage: {self.message}' return f"ServiceException - ErrorCode: {self.error_code} - ErrorMessage: {self.message}"

View File

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

View File

@@ -2,18 +2,16 @@ from bot_api.abc.select_criteria_abc import SelectCriteriaABC
class AuthUserSelectCriteria(SelectCriteriaABC): class AuthUserSelectCriteria(SelectCriteriaABC):
def __init__( def __init__(
self, self,
page_index: int, page_index: int,
page_size: int, page_size: int,
sort_direction: str, sort_direction: str,
sort_column: str, sort_column: str,
first_name: str,
first_name: str, last_name: str,
last_name: str, email: str,
email: str, auth_role: int,
auth_role: int
): ):
SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column) SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column)

View File

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

View File

@@ -2,15 +2,13 @@ from bot_api.abc.select_criteria_abc import SelectCriteriaABC
class ServerSelectCriteria(SelectCriteriaABC): class ServerSelectCriteria(SelectCriteriaABC):
def __init__( def __init__(
self, self,
page_index: int, page_index: int,
page_size: int, page_size: int,
sort_direction: str, sort_direction: str,
sort_column: str, sort_column: str,
name: str,
name: str,
): ):
SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column) SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column)

View File

@@ -5,7 +5,6 @@ from cpl_core.utils import String
class JSONProcessor: class JSONProcessor:
@staticmethod @staticmethod
def process(_t: type, values: dict) -> object: def process(_t: type, values: dict) -> object:
args = [] args = []
@@ -13,14 +12,14 @@ class JSONProcessor:
sig = signature(_t.__init__) sig = signature(_t.__init__)
for param in sig.parameters.items(): for param in sig.parameters.items():
parameter = param[1] parameter = param[1]
if parameter.name == 'self' or parameter.annotation == Parameter.empty: if parameter.name == "self" or parameter.annotation == Parameter.empty:
continue continue
name = String.convert_to_camel_case(parameter.name) name = String.convert_to_camel_case(parameter.name)
name = name.replace('Dto', 'DTO') name = name.replace("Dto", "DTO")
name_first_lower = String.first_to_lower(name) name_first_lower = String.first_to_lower(name)
if name in values or name_first_lower in values: if name in values or name_first_lower in values:
value = '' value = ""
if name in values: if name in values:
value = values[name] value = values[name]
else: else:

View File

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

View File

@@ -6,6 +6,10 @@ from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC
class ApiLogger(CustomFileLoggerABC): class ApiLogger(CustomFileLoggerABC):
def __init__(
def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC): self,
CustomFileLoggerABC.__init__(self, 'Api', config, time_format, env) config: ConfigurationABC,
time_format: TimeFormatSettings,
env: ApplicationEnvironmentABC,
):
CustomFileLoggerABC.__init__(self, "Api", config, time_format, env)

View File

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

View File

@@ -5,16 +5,15 @@ from bot_data.model.auth_role_enum import AuthRoleEnum
class AuthUserDTO(DtoABC): class AuthUserDTO(DtoABC):
def __init__( def __init__(
self, self,
id: int = None, id: int = None,
first_name: str = None, first_name: str = None,
last_name: str = None, last_name: str = None,
email: str = None, email: str = None,
password: str = None, password: str = None,
confirmation_id: Optional[str] = None, confirmation_id: Optional[str] = None,
auth_role: AuthRoleEnum = None, auth_role: AuthRoleEnum = None,
): ):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -79,21 +78,21 @@ class AuthUserDTO(DtoABC):
self._auth_role = value self._auth_role = value
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._id = values['id'] self._id = values["id"]
self._first_name = values['firstName'] self._first_name = values["firstName"]
self._last_name = values['lastName'] self._last_name = values["lastName"]
self._email = values['email'] self._email = values["email"]
self._password = values['password'] self._password = values["password"]
self._is_confirmed = values['isConfirmed'] self._is_confirmed = values["isConfirmed"]
self._auth_role = AuthRoleEnum(values['authRole']) self._auth_role = AuthRoleEnum(values["authRole"])
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
'id': self._id, "id": self._id,
'firstName': self._first_name, "firstName": self._first_name,
'lastName': self._last_name, "lastName": self._last_name,
'email': self._email, "email": self._email,
'password': self._password, "password": self._password,
'isConfirmed': self._is_confirmed, "isConfirmed": self._is_confirmed,
'authRole': self._auth_role.value, "authRole": self._auth_role.value,
} }

View File

@@ -5,17 +5,13 @@ from bot_data.filtered_result import FilteredResult
class AuthUserFilteredResultDTO(DtoABC, FilteredResult): class AuthUserFilteredResultDTO(DtoABC, FilteredResult):
def __init__(self, result: List = None, total_count: int = 0): def __init__(self, result: List = None, total_count: int = 0):
DtoABC.__init__(self) DtoABC.__init__(self)
FilteredResult.__init__(self, result, total_count) FilteredResult.__init__(self, result, total_count)
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._result = values['users'] self._result = values["users"]
self._total_count = values['totalCount'] self._total_count = values["totalCount"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"users": self.result, "totalCount": self.total_count}
'users': self.result,
'totalCount': self.total_count
}

View File

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

View File

@@ -4,15 +4,13 @@ from bot_api.abc.dto_abc import DtoABC
class ServerDTO(DtoABC): class ServerDTO(DtoABC):
def __init__( def __init__(
self, self,
server_id: int, server_id: int,
discord_id: int, discord_id: int,
name: str, name: str,
member_count: int, member_count: int,
icon_url: Optional[str] icon_url: Optional[str],
): ):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -21,19 +19,19 @@ class ServerDTO(DtoABC):
self._name = name self._name = name
self._member_count = member_count self._member_count = member_count
self._icon_url = icon_url self._icon_url = icon_url
@property @property
def server_id(self) -> int: def server_id(self) -> int:
return self._server_id return self._server_id
@property @property
def discord_id(self) -> int: def discord_id(self) -> int:
return self._discord_id return self._discord_id
@property @property
def name(self) -> str: def name(self) -> str:
return self._name return self._name
@property @property
def member_count(self) -> int: def member_count(self) -> int:
return self._member_count return self._member_count
@@ -43,16 +41,16 @@ class ServerDTO(DtoABC):
return self._icon_url return self._icon_url
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._server_id = int(values['serverId']) self._server_id = int(values["serverId"])
self._discord_id = int(values['discordId']) self._discord_id = int(values["discordId"])
self._name = values['name'] self._name = values["name"]
self._icon_url = values['iconURL'] self._icon_url = values["iconURL"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
'serverId': self._server_id, "serverId": self._server_id,
'discordId': self._discord_id, "discordId": self._discord_id,
'name': self._name, "name": self._name,
'memberCount': self._member_count, "memberCount": self._member_count,
'iconURL': self._icon_url, "iconURL": self._icon_url,
} }

View File

@@ -5,17 +5,13 @@ from bot_data.filtered_result import FilteredResult
class ServerFilteredResultDTO(DtoABC, FilteredResult): class ServerFilteredResultDTO(DtoABC, FilteredResult):
def __init__(self, result: List = None, total_count: int = 0): def __init__(self, result: List = None, total_count: int = 0):
DtoABC.__init__(self) DtoABC.__init__(self)
FilteredResult.__init__(self, result, total_count) FilteredResult.__init__(self, result, total_count)
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._result = values['servers'] self._result = values["servers"]
self._total_count = values['totalCount'] self._total_count = values["totalCount"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"servers": self.result, "totalCount": self.total_count}
'servers': self.result,
'totalCount': self.total_count
}

View File

@@ -6,16 +6,13 @@ from bot_api.abc.dto_abc import DtoABC
class EMailStringDTO(DtoABC): class EMailStringDTO(DtoABC):
def __init__(self, email: str): def __init__(self, email: str):
DtoABC.__init__(self) DtoABC.__init__(self)
self._email = email self._email = email
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._email = values['email'] self._email = values["email"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"email": self._email}
'email': self._email
}

View File

@@ -8,7 +8,6 @@ from bot_api.exception.service_error_code_enum import ServiceErrorCode
class ErrorDTO(DtoABC): class ErrorDTO(DtoABC):
def __init__(self, error_code: Optional[ServiceErrorCode], message: str): def __init__(self, error_code: Optional[ServiceErrorCode], message: str):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -24,11 +23,8 @@ class ErrorDTO(DtoABC):
return self._message return self._message
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._error_code = values['ErrorCode'] self._error_code = values["ErrorCode"]
self._message = values['Message'] self._message = values["Message"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"errorCode": int(self._error_code.value), "message": self._message}
'errorCode': int(self._error_code.value),
'message': self._message
}

View File

@@ -6,11 +6,10 @@ from bot_data.model.auth_role_enum import AuthRoleEnum
class OAuthDTO(DtoABC): class OAuthDTO(DtoABC):
def __init__( def __init__(
self, self,
user: AuthUserDTO, user: AuthUserDTO,
o_auth_id: Optional[str], o_auth_id: Optional[str],
): ):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -34,11 +33,8 @@ class OAuthDTO(DtoABC):
self._oauth_id = value self._oauth_id = value
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._user = AuthUserDTO().from_dict(values['user']) self._user = AuthUserDTO().from_dict(values["user"])
self._oauth_id = values['oAuthId'] self._oauth_id = values["oAuthId"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"user": self._user.to_dict(), "oAuthId": self._oauth_id}
'user': self._user.to_dict(),
'oAuthId': self._oauth_id
}

View File

@@ -6,7 +6,6 @@ from bot_api.abc.dto_abc import DtoABC
class ResetPasswordDTO(DtoABC): class ResetPasswordDTO(DtoABC):
def __init__(self, id: str, password: str): def __init__(self, id: str, password: str):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -22,11 +21,8 @@ class ResetPasswordDTO(DtoABC):
return self._password return self._password
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._id = values['id'] self._id = values["id"]
self._password = values['password'] self._password = values["password"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"id": self._id, "password": self._password}
'id': self._id,
'password': self._password
}

View File

@@ -3,21 +3,20 @@ from bot_api.model.version_dto import VersionDTO
class SettingsDTO(DtoABC): class SettingsDTO(DtoABC):
def __init__( def __init__(
self, self,
web_version: str, web_version: str,
api_version: VersionDTO, api_version: VersionDTO,
config_path: str, config_path: str,
web_base_url: str, web_base_url: str,
api_base_url: str, api_base_url: str,
token_expire_time: int, token_expire_time: int,
refresh_token_expire_time: int, refresh_token_expire_time: int,
mail_user: str, mail_user: str,
mail_port: int, mail_port: int,
mail_host: str, mail_host: str,
mail_transceiver: str, mail_transceiver: str,
mail_transceiver_address: str, mail_transceiver_address: str,
): ):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -37,31 +36,31 @@ class SettingsDTO(DtoABC):
self._mail_transceiver_address = mail_transceiver_address self._mail_transceiver_address = mail_transceiver_address
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._web_version = values['webVersion'] self._web_version = values["webVersion"]
self._api_version.from_dict(values['apiVersion']) self._api_version.from_dict(values["apiVersion"])
self._config_path = values['configPath'] self._config_path = values["configPath"]
self._web_base_url = values['webBaseURL'] self._web_base_url = values["webBaseURL"]
self._api_base_url = values['apiBaseURL'] self._api_base_url = values["apiBaseURL"]
self._token_expire_time = values['tokenExpireTime'] self._token_expire_time = values["tokenExpireTime"]
self._refresh_token_expire_time = values['refreshTokenExpireTime'] self._refresh_token_expire_time = values["refreshTokenExpireTime"]
self._mail_user = values['mailUser'] self._mail_user = values["mailUser"]
self._mail_port = values['mailPort'] self._mail_port = values["mailPort"]
self._mail_host = values['mailHost'] self._mail_host = values["mailHost"]
self._mail_transceiver = values['mailTransceiver'] self._mail_transceiver = values["mailTransceiver"]
self._mail_transceiver_address = values['mailTransceiverAddress'] self._mail_transceiver_address = values["mailTransceiverAddress"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
'webVersion': self._web_version, "webVersion": self._web_version,
'apiVersion': self._api_version.str, "apiVersion": self._api_version.str,
'configPath': self._config_path, "configPath": self._config_path,
'webBaseURL': self._web_base_url, "webBaseURL": self._web_base_url,
'apiBaseURL': self._api_base_url, "apiBaseURL": self._api_base_url,
'tokenExpireTime': self._token_expire_time, "tokenExpireTime": self._token_expire_time,
'refreshTokenExpireTime': self._refresh_token_expire_time, "refreshTokenExpireTime": self._refresh_token_expire_time,
'mailUser': self._mail_user, "mailUser": self._mail_user,
'mailPort': self._mail_port, "mailPort": self._mail_port,
'mailHost': self._mail_host, "mailHost": self._mail_host,
'mailTransceiver': self._mail_transceiver, "mailTransceiver": self._mail_transceiver,
'mailTransceiverAddress': self._mail_transceiver_address, "mailTransceiverAddress": self._mail_transceiver_address,
} }

View File

@@ -6,27 +6,23 @@ from bot_api.abc.dto_abc import DtoABC
class TokenDTO(DtoABC): class TokenDTO(DtoABC):
def __init__(self, token: str, refresh_token: str): def __init__(self, token: str, refresh_token: str):
DtoABC.__init__(self) DtoABC.__init__(self)
self._token = token self._token = token
self._refresh_token = refresh_token self._refresh_token = refresh_token
@property @property
def token(self) -> str: def token(self) -> str:
return self._token return self._token
@property @property
def refresh_token(self) -> str: def refresh_token(self) -> str:
return self._refresh_token return self._refresh_token
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._token = values['token'] self._token = values["token"]
self._refresh_token = values['refreshToken'] self._refresh_token = values["refreshToken"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {"token": self._token, "refreshToken": self._refresh_token}
'token': self._token,
'refreshToken': self._refresh_token
}

View File

@@ -7,12 +7,11 @@ from bot_api.model.auth_user_dto import AuthUserDTO
class UpdateAuthUserDTO(DtoABC): class UpdateAuthUserDTO(DtoABC):
def __init__( def __init__(
self, self,
auth_user_dto: AuthUserDTO, auth_user_dto: AuthUserDTO,
new_auth_user_dto: AuthUserDTO, new_auth_user_dto: AuthUserDTO,
change_password: bool = False change_password: bool = False,
): ):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -33,13 +32,13 @@ class UpdateAuthUserDTO(DtoABC):
return self._change_password return self._change_password
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._auth_user = AuthUserDTO().from_dict(values['authUser']) self._auth_user = AuthUserDTO().from_dict(values["authUser"])
self._new_auth_user = AuthUserDTO().from_dict(values['newAuthUser']) self._new_auth_user = AuthUserDTO().from_dict(values["newAuthUser"])
self._change_password = False if 'changePassword' not in values else bool(values['changePassword']) self._change_password = False if "changePassword" not in values else bool(values["changePassword"])
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
'authUser': self._auth_user, "authUser": self._auth_user,
'newAuthUser': self._new_auth_user, "newAuthUser": self._new_auth_user,
'changePassword': self._change_password "changePassword": self._change_password,
} }

View File

@@ -6,7 +6,6 @@ from bot_api.abc.dto_abc import DtoABC
class VersionDTO(DtoABC): class VersionDTO(DtoABC):
def __init__(self, major: str = None, minor: str = None, micro: str = None): def __init__(self, major: str = None, minor: str = None, micro: str = None):
DtoABC.__init__(self) DtoABC.__init__(self)
@@ -28,16 +27,16 @@ class VersionDTO(DtoABC):
@property @property
def str(self) -> str: def str(self) -> str:
return f'{self._major}.{self._minor}.{self._micro}' return f"{self._major}.{self._minor}.{self._micro}"
def from_dict(self, values: dict): def from_dict(self, values: dict):
self._major = values['major'] self._major = values["major"]
self._minor = values['minor'] self._minor = values["minor"]
self._micro = values['micro'] self._micro = values["micro"]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
'major': self._major, "major": self._major,
'minor': self._minor, "minor": self._minor,
'micro': self._micro, "micro": self._micro,
} }

View File

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

View File

@@ -32,39 +32,39 @@ class Route:
@wraps(f) @wraps(f)
async def decorator(*args, **kwargs): async def decorator(*args, **kwargs):
token = None token = None
if 'Authorization' in request.headers: if "Authorization" in request.headers:
bearer = request.headers.get('Authorization') bearer = request.headers.get("Authorization")
token = bearer.split()[1] token = bearer.split()[1]
if token is None: if token is None:
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token not set') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Token not set")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401 return jsonify(error.to_dict()), 401
if cls._auth_users is None or cls._auth is None: if cls._auth_users is None or cls._auth is None:
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Authorize is not initialized') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Authorize is not initialized")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401 return jsonify(error.to_dict()), 401
if not cls._auth.verify_login(token): if not cls._auth.verify_login(token):
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token expired') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Token expired")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401 return jsonify(error.to_dict()), 401
token = cls._auth.decode_token(token) token = cls._auth.decode_token(token)
if token is None or 'email' not in token: if token is None or "email" not in token:
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Token invalid")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401 return jsonify(error.to_dict()), 401
user = cls._auth_users.get_auth_user_by_email(token['email']) user = cls._auth_users.get_auth_user_by_email(token["email"])
if user is None: if user is None:
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Token invalid")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401 return jsonify(error.to_dict()), 401
if role is not None and user.auth_role.value < role.value: if role is not None and user.auth_role.value < role.value:
ex = ServiceException(ServiceErrorCode.Unauthorized, f'Role {role} required') ex = ServiceException(ServiceErrorCode.Unauthorized, f"Role {role} required")
error = ErrorDTO(ex.error_code, ex.message) error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 403 return jsonify(error.to_dict()), 403
@@ -84,20 +84,20 @@ class Route:
@classmethod @classmethod
def get(cls, path=None, **kwargs): def get(cls, path=None, **kwargs):
return cls.route(path, methods=['GET'], **kwargs) return cls.route(path, methods=["GET"], **kwargs)
@classmethod @classmethod
def post(cls, path=None, **kwargs): def post(cls, path=None, **kwargs):
return cls.route(path, methods=['POST'], **kwargs) return cls.route(path, methods=["POST"], **kwargs)
@classmethod @classmethod
def head(cls, path=None, **kwargs): def head(cls, path=None, **kwargs):
return cls.route(path, methods=['HEAD'], **kwargs) return cls.route(path, methods=["HEAD"], **kwargs)
@classmethod @classmethod
def put(cls, path=None, **kwargs): def put(cls, path=None, **kwargs):
return cls.route(path, methods=['PUT'], **kwargs) return cls.route(path, methods=["PUT"], **kwargs)
@classmethod @classmethod
def delete(cls, path=None, **kwargs): def delete(cls, path=None, **kwargs):
return cls.route(path, methods=['DELETE'], **kwargs) return cls.route(path, methods=["DELETE"], **kwargs)

View File

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

View File

@@ -38,26 +38,24 @@ from bot_data.model.auth_role_enum import AuthRoleEnum
from bot_data.model.auth_user import AuthUser from bot_data.model.auth_user import AuthUser
from bot_data.model.auth_user_users_relation import AuthUserUsersRelation from bot_data.model.auth_user_users_relation import AuthUserUsersRelation
_email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' _email_regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
class AuthService(AuthServiceABC): class AuthService(AuthServiceABC):
def __init__( def __init__(
self, self,
env: ApplicationEnvironmentABC, env: ApplicationEnvironmentABC,
logger: ApiLogger, logger: ApiLogger,
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
db: DatabaseContextABC, db: DatabaseContextABC,
auth_users: AuthUserRepositoryABC, auth_users: AuthUserRepositoryABC,
users: UserRepositoryABC, users: UserRepositoryABC,
servers: ServerRepositoryABC, servers: ServerRepositoryABC,
# mailer: MailThread, # mailer: MailThread,
mailer: EMailClientABC, mailer: EMailClientABC,
t: TranslatePipe, t: TranslatePipe,
auth_settings: AuthenticationSettings, auth_settings: AuthenticationSettings,
frontend_settings: FrontendSettings, frontend_settings: FrontendSettings,
): ):
AuthServiceABC.__init__(self) AuthServiceABC.__init__(self)
@@ -75,7 +73,7 @@ class AuthService(AuthServiceABC):
@staticmethod @staticmethod
def _hash_sha256(password: str, salt: str) -> str: def _hash_sha256(password: str, salt: str) -> str:
return hashlib.sha256(f'{password}{salt}'.encode('utf-8')).hexdigest() return hashlib.sha256(f"{password}{salt}".encode("utf-8")).hexdigest()
@staticmethod @staticmethod
def _is_email_valid(email: str) -> bool: def _is_email_valid(email: str) -> bool:
@@ -87,14 +85,14 @@ class AuthService(AuthServiceABC):
def generate_token(self, user: AuthUser) -> str: def generate_token(self, user: AuthUser) -> str:
token = jwt.encode( token = jwt.encode(
payload={ payload={
'user_id': user.id, "user_id": user.id,
'email': user.email, "email": user.email,
'role': user.auth_role.value, "role": user.auth_role.value,
'exp': datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time), "exp": datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time),
'iss': self._auth_settings.issuer, "iss": self._auth_settings.issuer,
'aud': self._auth_settings.audience "aud": self._auth_settings.audience,
}, },
key=CredentialManager.decrypt(self._auth_settings.secret_key) key=CredentialManager.decrypt(self._auth_settings.secret_key),
) )
return token return token
@@ -105,39 +103,43 @@ class AuthService(AuthServiceABC):
key=CredentialManager.decrypt(self._auth_settings.secret_key), key=CredentialManager.decrypt(self._auth_settings.secret_key),
issuer=self._auth_settings.issuer, issuer=self._auth_settings.issuer,
audience=self._auth_settings.audience, audience=self._auth_settings.audience,
algorithms=['HS256'] algorithms=["HS256"],
) )
def get_decoded_token_from_request(self) -> dict: def get_decoded_token_from_request(self) -> dict:
token = None token = None
if 'Authorization' in request.headers: if "Authorization" in request.headers:
bearer = request.headers.get('Authorization') bearer = request.headers.get("Authorization")
token = bearer.split()[1] token = bearer.split()[1]
if token is None: if token is None:
raise ServiceException(ServiceErrorCode.Unauthorized, f'Token not set') raise ServiceException(ServiceErrorCode.Unauthorized, f"Token not set")
return jwt.decode( return jwt.decode(
token, token,
key=CredentialManager.decrypt(self._auth_settings.secret_key), key=CredentialManager.decrypt(self._auth_settings.secret_key),
issuer=self._auth_settings.issuer, issuer=self._auth_settings.issuer,
audience=self._auth_settings.audience, audience=self._auth_settings.audience,
algorithms=['HS256'] algorithms=["HS256"],
) )
def find_decoded_token_from_request(self) -> Optional[dict]: def find_decoded_token_from_request(self) -> Optional[dict]:
token = None token = None
if 'Authorization' in request.headers: if "Authorization" in request.headers:
bearer = request.headers.get('Authorization') bearer = request.headers.get("Authorization")
token = bearer.split()[1] token = bearer.split()[1]
return jwt.decode( return (
token, jwt.decode(
key=CredentialManager.decrypt(self._auth_settings.secret_key), token,
issuer=self._auth_settings.issuer, key=CredentialManager.decrypt(self._auth_settings.secret_key),
audience=self._auth_settings.audience, issuer=self._auth_settings.issuer,
algorithms=['HS256'] audience=self._auth_settings.audience,
) if token is not None else None algorithms=["HS256"],
)
if token is not None
else None
)
def _create_and_save_refresh_token(self, user: AuthUser) -> str: def _create_and_save_refresh_token(self, user: AuthUser) -> str:
token = str(uuid.uuid4()) token = str(uuid.uuid4())
@@ -149,58 +151,56 @@ class AuthService(AuthServiceABC):
def _send_link_mail(self, email: str, subject: str, message: str): def _send_link_mail(self, email: str, subject: str, message: str):
url = self._frontend_settings.url url = self._frontend_settings.url
if not url.endswith('/'): if not url.endswith("/"):
url = f'{url}/' url = f"{url}/"
self._mailer.connect() self._mailer.connect()
mail = EMail() mail = EMail()
mail.add_header('Mime-Version: 1.0') mail.add_header("Mime-Version: 1.0")
mail.add_header('Content-Type: text/plain; charset=utf-8') mail.add_header("Content-Type: text/plain; charset=utf-8")
mail.add_header('Content-Transfer-Encoding: quoted-printable') mail.add_header("Content-Transfer-Encoding: quoted-printable")
mail.add_receiver(str(email)) mail.add_receiver(str(email))
mail.subject = subject mail.subject = subject
mail.body = textwrap.dedent(f"""{message} mail.body = textwrap.dedent(
f"""{message}
{self._t.transform('api.mail.automatic_mail').format(self._environment.application_name, self._environment.environment_name, self._environment.host_name)} {self._t.transform('api.mail.automatic_mail').format(self._environment.application_name, self._environment.environment_name, self._environment.host_name)}
""") """
)
thr = Thread(target=self._mailer.send_mail, args=[mail]) thr = Thread(target=self._mailer.send_mail, args=[mail])
thr.start() thr.start()
def _send_confirmation_id_to_user(self, user: AuthUser): def _send_confirmation_id_to_user(self, user: AuthUser):
url = self._frontend_settings.url url = self._frontend_settings.url
if not url.endswith('/'): if not url.endswith("/"):
url = f'{url}/' url = f"{url}/"
self._send_link_mail( self._send_link_mail(
user.email, user.email,
self._t.transform('api.auth.confirmation.subject').format(user.first_name, user.last_name), self._t.transform("api.auth.confirmation.subject").format(user.first_name, user.last_name),
self._t.transform('api.auth.confirmation.message').format(url, user.confirmation_id) self._t.transform("api.auth.confirmation.message").format(url, user.confirmation_id),
) )
def _send_forgot_password_id_to_user(self, user: AuthUser): def _send_forgot_password_id_to_user(self, user: AuthUser):
url = self._frontend_settings.url url = self._frontend_settings.url
if not url.endswith('/'): if not url.endswith("/"):
url = f'{url}/' url = f"{url}/"
self._send_link_mail( self._send_link_mail(
user.email, user.email,
self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name), self._t.transform("api.auth.forgot_password.subject").format(user.first_name, user.last_name),
self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id) self._t.transform("api.auth.forgot_password.message").format(url, user.forgot_password_id),
) )
async def get_all_auth_users_async(self) -> List[AuthUserDTO]: async def get_all_auth_users_async(self) -> List[AuthUserDTO]:
result = self._auth_users.get_all_auth_users() \ result = self._auth_users.get_all_auth_users().select(lambda x: AUT.to_dto(x))
.select(lambda x: AUT.to_dto(x))
return List(AuthUserDTO, result) return List(AuthUserDTO, result)
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO:
users = self._auth_users.get_filtered_auth_users(criteria) users = self._auth_users.get_filtered_auth_users(criteria)
result = users.result.select(lambda x: AUT.to_dto(x)) result = users.result.select(lambda x: AUT.to_dto(x))
return AuthUserFilteredResultDTO( return AuthUserFilteredResultDTO(List(AuthUserDTO, result), users.total_count)
List(AuthUserDTO, result),
users.total_count
)
async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO: async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO:
try: try:
@@ -208,8 +208,8 @@ class AuthService(AuthServiceABC):
user = self._auth_users.get_auth_user_by_email(email) user = self._auth_users.get_auth_user_by_email(email)
return AUT.to_dto(user, password=user.password if with_password else None) return AUT.to_dto(user, password=user.password if with_password else None)
except Exception as e: except Exception as e:
self._logger.error(__name__, f'AuthUser not found', e) self._logger.error(__name__, f"AuthUser not found", e)
raise ServiceException(ServiceErrorCode.InvalidData, f'User not found {email}') raise ServiceException(ServiceErrorCode.InvalidData, f"User not found {email}")
async def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: async def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]:
user = self._auth_users.find_auth_user_by_email(email) user = self._auth_users.find_auth_user_by_email(email)
@@ -218,7 +218,7 @@ class AuthService(AuthServiceABC):
async def add_auth_user_async(self, user_dto: AuthUserDTO): async def add_auth_user_async(self, user_dto: AuthUserDTO):
db_user = self._auth_users.find_auth_user_by_email(user_dto.email) db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
if db_user is not None: if db_user is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user = AUT.to_db(user_dto) user = AUT.to_db(user_dto)
if self._auth_users.get_all_auth_users().count() == 0: if self._auth_users.get_all_auth_users().count() == 0:
@@ -227,26 +227,26 @@ class AuthService(AuthServiceABC):
user.password_salt = uuid.uuid4() user.password_salt = uuid.uuid4()
user.password = self._hash_sha256(user_dto.password, user.password_salt) user.password = self._hash_sha256(user_dto.password, user.password_salt)
if not self._is_email_valid(user.email): if not self._is_email_valid(user.email):
raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address') raise ServiceException(ServiceErrorCode.InvalidData, "Invalid E-Mail address")
try: try:
user.confirmation_id = uuid.uuid4() user.confirmation_id = uuid.uuid4()
self._auth_users.add_auth_user(user) self._auth_users.add_auth_user(user)
self._send_confirmation_id_to_user(user) self._send_confirmation_id_to_user(user)
self._db.save_changes() self._db.save_changes()
self._logger.info(__name__, f'Added auth user with E-Mail: {user_dto.email}') self._logger.info(__name__, f"Added auth user with E-Mail: {user_dto.email}")
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Cannot add user with E-Mail {user_dto.email}', e) self._logger.error(__name__, f"Cannot add user with E-Mail {user_dto.email}", e)
raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail")
async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): async def add_auth_user_by_oauth_async(self, dto: OAuthDTO):
db_user = self._auth_users.find_auth_user_by_email(dto.user.email) db_user = self._auth_users.find_auth_user_by_email(dto.user.email)
if db_user is None: if db_user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') raise ServiceException(ServiceErrorCode.InvalidUser, "User not found")
if db_user.oauth_id != dto.oauth_id: if db_user.oauth_id != dto.oauth_id:
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong OAuthId') raise ServiceException(ServiceErrorCode.InvalidUser, "Wrong OAuthId")
try: try:
db_user.first_name = dto.user.first_name db_user.first_name = dto.user.first_name
@@ -257,9 +257,9 @@ class AuthService(AuthServiceABC):
db_user.confirmation_id = uuid.uuid4() db_user.confirmation_id = uuid.uuid4()
self._send_confirmation_id_to_user(db_user) self._send_confirmation_id_to_user(db_user)
self._auth_users.update_auth_user(db_user) self._auth_users.update_auth_user(db_user)
self._logger.info(__name__, f'Added auth user with E-Mail: {dto.user.email}') self._logger.info(__name__, f"Added auth user with E-Mail: {dto.user.email}")
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Cannot add user with E-Mail {dto.user.email}', e) self._logger.error(__name__, f"Cannot add user with E-Mail {dto.user.email}", e)
raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail")
self._db.save_changes() self._db.save_changes()
@@ -270,23 +270,23 @@ class AuthService(AuthServiceABC):
# user exists # user exists
if db_auth_user is not None and db_auth_user.users.count() > 0: if db_auth_user is not None and db_auth_user.users.count() > 0:
# raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') # raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists')
self._logger.debug(__name__, f'Discord user already exists') self._logger.debug(__name__, f"Discord user already exists")
return OAuthDTO(AUT.to_dto(db_auth_user), None) return OAuthDTO(AUT.to_dto(db_auth_user), None)
# user exists but discord user id not set # user exists but discord user id not set
elif db_auth_user is not None and db_auth_user.users.count() == 0: elif db_auth_user is not None and db_auth_user.users.count() == 0:
self._logger.debug(__name__, f'Auth user exists but not linked with discord') self._logger.debug(__name__, f"Auth user exists but not linked with discord")
# users = self._users.get_users_by_discord_id(user_dto.user_id) # users = self._users.get_users_by_discord_id(user_dto.user_id)
# add auth_user to user refs # add auth_user to user refs
db_auth_user.oauth_id = None db_auth_user.oauth_id = None
else: else:
# user does not exists # user does not exists
self._logger.debug(__name__, f'Auth user does not exist') self._logger.debug(__name__, f"Auth user does not exist")
try: try:
user_dto.user_id = self._users.get_users_by_discord_id(user_dto.user_id).single().user_id user_dto.user_id = self._users.get_users_by_discord_id(user_dto.user_id).single().user_id
except Exception as e: except Exception as e:
self._logger.error(__name__, f'User not found') self._logger.error(__name__, f"User not found")
user_dto.user_id = None user_dto.user_id = None
await self.add_auth_user_async(user_dto) await self.add_auth_user_async(user_dto)
@@ -311,46 +311,61 @@ class AuthService(AuthServiceABC):
async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): async def update_user_async(self, update_user_dto: UpdateAuthUserDTO):
if update_user_dto is None: if update_user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"User is empty")
if update_user_dto.auth_user is None: if update_user_dto.auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"Existing user is empty")
if update_user_dto.new_auth_user is None: if update_user_dto.new_auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"New user is empty")
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(
raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail') update_user_dto.new_auth_user.email
):
raise ServiceException(ServiceErrorCode.InvalidData, f"Invalid E-Mail")
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
if user is None: if user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') raise ServiceException(ServiceErrorCode.InvalidUser, "User not found")
if user.confirmation_id is not None: if user.confirmation_id is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') raise ServiceException(ServiceErrorCode.InvalidUser, "E-Mail not confirmed")
# update first name # update first name
if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: if (
update_user_dto.new_auth_user.first_name is not None
and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name
):
user.first_name = update_user_dto.new_auth_user.first_name user.first_name = update_user_dto.new_auth_user.first_name
# update last name # update last name
if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and \ if (
update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: update_user_dto.new_auth_user.last_name is not None
and update_user_dto.new_auth_user.last_name != ""
and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name
):
user.last_name = update_user_dto.new_auth_user.last_name user.last_name = update_user_dto.new_auth_user.last_name
# update E-Mail # update E-Mail
if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: if (
update_user_dto.new_auth_user.email is not None
and update_user_dto.new_auth_user.email != ""
and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email
):
user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email)
if user_by_new_e_mail is not None: if user_by_new_e_mail is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user.email = update_user_dto.new_auth_user.email user.email = update_user_dto.new_auth_user.email
update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt) update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt)
if update_user_dto.auth_user.password != user.password: if update_user_dto.auth_user.password != user.password:
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') raise ServiceException(ServiceErrorCode.InvalidUser, "Wrong password")
# update password # update password
if update_user_dto.new_auth_user.password is not None and self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password: if (
update_user_dto.new_auth_user.password is not None
and self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password
):
user.password_salt = uuid.uuid4() user.password_salt = uuid.uuid4()
user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt)
@@ -359,20 +374,22 @@ class AuthService(AuthServiceABC):
async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO):
if update_user_dto is None: if update_user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"User is empty")
if update_user_dto.auth_user is None: if update_user_dto.auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"Existing user is empty")
if update_user_dto.new_auth_user is None: if update_user_dto.new_auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty') raise ServiceException(ServiceErrorCode.InvalidData, f"New user is empty")
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(
raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail') update_user_dto.new_auth_user.email
):
raise ServiceException(ServiceErrorCode.InvalidData, f"Invalid E-Mail")
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
if user is None: if user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') raise ServiceException(ServiceErrorCode.InvalidUser, "User not found")
if user.confirmation_id is not None and update_user_dto.new_auth_user.is_confirmed: if user.confirmation_id is not None and update_user_dto.new_auth_user.is_confirmed:
user.confirmation_id = None user.confirmation_id = None
@@ -382,27 +399,45 @@ class AuthService(AuthServiceABC):
# raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') # raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed')
# update first name # update first name
if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: if (
update_user_dto.new_auth_user.first_name is not None
and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name
):
user.first_name = update_user_dto.new_auth_user.first_name user.first_name = update_user_dto.new_auth_user.first_name
# update last name # update last name
if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: if (
update_user_dto.new_auth_user.last_name is not None
and update_user_dto.new_auth_user.last_name != ""
and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name
):
user.last_name = update_user_dto.new_auth_user.last_name user.last_name = update_user_dto.new_auth_user.last_name
# update E-Mail # update E-Mail
if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: if (
update_user_dto.new_auth_user.email is not None
and update_user_dto.new_auth_user.email != ""
and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email
):
user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email)
if user_by_new_e_mail is not None: if user_by_new_e_mail is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user.email = update_user_dto.new_auth_user.email user.email = update_user_dto.new_auth_user.email
# update password # update password
if update_user_dto.new_auth_user.password is not None and update_user_dto.change_password and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt): if (
update_user_dto.new_auth_user.password is not None
and update_user_dto.change_password
and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt)
):
user.password_salt = uuid.uuid4() user.password_salt = uuid.uuid4()
user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt)
# update role # update role
if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: if (
user.auth_role == update_user_dto.auth_user.auth_role
and user.auth_role != update_user_dto.new_auth_user.auth_role
):
user.auth_role = update_user_dto.new_auth_user.auth_role user.auth_role = update_user_dto.new_auth_user.auth_role
self._auth_users.update_auth_user(user) self._auth_users.update_auth_user(user)
@@ -414,43 +449,46 @@ class AuthService(AuthServiceABC):
self._auth_users.delete_auth_user(user) self._auth_users.delete_auth_user(user)
self._db.save_changes() self._db.save_changes()
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Cannot delete user', e) self._logger.error(__name__, f"Cannot delete user", e)
raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {email}') raise ServiceException(ServiceErrorCode.UnableToDelete, f"Cannot delete user by mail {email}")
async def delete_auth_user_async(self, user_dto: AuthUser): async def delete_auth_user_async(self, user_dto: AuthUser):
try: try:
self._auth_users.delete_auth_user(AUT.to_db(user_dto)) self._auth_users.delete_auth_user(AUT.to_db(user_dto))
self._db.save_changes() self._db.save_changes()
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Cannot delete user', e) self._logger.error(__name__, f"Cannot delete user", e)
raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {user_dto.email}') raise ServiceException(
ServiceErrorCode.UnableToDelete,
f"Cannot delete user by mail {user_dto.email}",
)
def verify_login(self, token_str: str) -> bool: def verify_login(self, token_str: str) -> bool:
try: try:
token = self.decode_token(token_str) token = self.decode_token(token_str)
if token is None or 'email' not in token: if token is None or "email" not in token:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') raise ServiceException(ServiceErrorCode.InvalidData, "Token invalid")
user = self._auth_users.find_auth_user_by_email(token['email']) user = self._auth_users.find_auth_user_by_email(token["email"])
if user is None: if user is None:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') raise ServiceException(ServiceErrorCode.InvalidData, "Token expired")
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Token invalid', e) self._logger.error(__name__, f"Token invalid", e)
return False return False
return True return True
async def login_async(self, user_dto: AuthUser) -> TokenDTO: async def login_async(self, user_dto: AuthUser) -> TokenDTO:
if user_dto is None: if user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, 'User not set') raise ServiceException(ServiceErrorCode.InvalidData, "User not set")
db_user = self._auth_users.find_auth_user_by_email(user_dto.email) db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
if db_user is None: if db_user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') raise ServiceException(ServiceErrorCode.InvalidUser, f"User not found")
user_dto.password = self._hash_sha256(user_dto.password, db_user.password_salt) user_dto.password = self._hash_sha256(user_dto.password, db_user.password_salt)
if db_user.password != user_dto.password: if db_user.password != user_dto.password:
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') raise ServiceException(ServiceErrorCode.InvalidUser, "Wrong password")
token = self.generate_token(db_user) token = self.generate_token(db_user)
refresh_token = self._create_and_save_refresh_token(db_user) refresh_token = self._create_and_save_refresh_token(db_user)
@@ -460,40 +498,66 @@ class AuthService(AuthServiceABC):
self._db.save_changes() self._db.save_changes()
return TokenDTO(token, refresh_token) 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: async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO:
if token_dto is None: if token_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') raise ServiceException(ServiceErrorCode.InvalidData, f"Token not set")
try: try:
token = self.decode_token(token_dto.token) token = self.decode_token(token_dto.token)
if token is None or 'email' not in token: if token is None or "email" not in token:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') raise ServiceException(ServiceErrorCode.InvalidData, "Token invalid")
user = self._auth_users.get_auth_user_by_email(token['email']) user = self._auth_users.get_auth_user_by_email(token["email"])
if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): if (
raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') user is None
or user.refresh_token != token_dto.refresh_token
or user.refresh_token_expire_time <= datetime.now()
):
raise ServiceException(ServiceErrorCode.InvalidData, "Token expired")
return TokenDTO(self.generate_token(user), self._create_and_save_refresh_token(user)) return TokenDTO(self.generate_token(user), self._create_and_save_refresh_token(user))
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Refreshing token failed', e) self._logger.error(__name__, f"Refreshing token failed", e)
return TokenDTO('', '') return TokenDTO("", "")
async def revoke_async(self, token_dto: TokenDTO): async def revoke_async(self, token_dto: TokenDTO):
if token_dto is None or token_dto.token is None or token_dto.refresh_token is None: if token_dto is None or token_dto.token is None or token_dto.refresh_token is None:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token not set') raise ServiceException(ServiceErrorCode.InvalidData, "Token not set")
try: try:
token = self.decode_token(token_dto.token) token = self.decode_token(token_dto.token)
user = self._auth_users.get_auth_user_by_email(token['email']) user = self._auth_users.get_auth_user_by_email(token["email"])
if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): if (
raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') user is None
or user.refresh_token != token_dto.refresh_token
or user.refresh_token_expire_time <= datetime.now()
):
raise ServiceException(ServiceErrorCode.InvalidData, "Token expired")
user.refresh_token = None user.refresh_token = None
self._auth_users.update_auth_user(user) self._auth_users.update_auth_user(user)
self._db.save_changes() self._db.save_changes()
except Exception as e: except Exception as e:
self._logger.error(__name__, f'Refreshing token failed', e) self._logger.error(__name__, f"Refreshing token failed", e)
async def confirm_email_async(self, id: str) -> bool: async def confirm_email_async(self, id: str) -> bool:
user = self._auth_users.find_auth_user_by_confirmation_id(id) user = self._auth_users.find_auth_user_by_confirmation_id(id)
@@ -522,13 +586,16 @@ class AuthService(AuthServiceABC):
async def reset_password_async(self, rp_dto: ResetPasswordDTO): async def reset_password_async(self, rp_dto: ResetPasswordDTO):
user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id) user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id)
if user is None: if user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, f'User by forgot password id {rp_dto.id} not found') raise ServiceException(
ServiceErrorCode.InvalidUser,
f"User by forgot password id {rp_dto.id} not found",
)
if user.confirmation_id is not None: if user.confirmation_id is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, f'E-Mail not confirmed') raise ServiceException(ServiceErrorCode.InvalidUser, f"E-Mail not confirmed")
if user.password is None or rp_dto.password == '': if user.password is None or rp_dto.password == "":
raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') raise ServiceException(ServiceErrorCode.InvalidData, f"Password not set")
user.password_salt = uuid.uuid4() user.password_salt = uuid.uuid4()
user.password = self._hash_sha256(rp_dto.password, user.password_salt) user.password = self._hash_sha256(rp_dto.password, user.password_salt)

View File

@@ -20,14 +20,13 @@ from bot_data.model.server import Server
class DiscordService: class DiscordService:
def __init__( def __init__(
self, self,
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
servers: ServerRepositoryABC, servers: ServerRepositoryABC,
auth: AuthServiceABC, auth: AuthServiceABC,
auth_users: AuthUserRepositoryABC, auth_users: AuthUserRepositoryABC,
users: UserRepositoryABC, users: UserRepositoryABC,
): ):
self._bot = bot self._bot = bot
self._servers = servers self._servers = servers
@@ -38,64 +37,53 @@ class DiscordService:
def _to_dto(self, x: Server) -> Optional[ServerDTO]: def _to_dto(self, x: Server) -> Optional[ServerDTO]:
guild = self._bot.get_guild(x.discord_server_id) guild = self._bot.get_guild(x.discord_server_id)
if guild is None: if guild is None:
return ServerTransformer.to_dto( return ServerTransformer.to_dto(x, "", 0, None)
x,
'',
0,
None
)
return ServerTransformer.to_dto( return ServerTransformer.to_dto(x, guild.name, guild.member_count, guild.icon)
x,
guild.name,
guild.member_count,
guild.icon
)
async def get_all_servers(self) -> List[ServerDTO]: async def get_all_servers(self) -> List[ServerDTO]:
servers = List(ServerDTO, self._servers.get_servers()) servers = List(ServerDTO, self._servers.get_servers())
return servers.select(self._to_dto).where(lambda x: x.name != '') return servers.select(self._to_dto).where(lambda x: x.name != "")
async def get_all_servers_by_user(self) -> List[ServerDTO]: async def get_all_servers_by_user(self) -> List[ServerDTO]:
token = self._auth.get_decoded_token_from_request() token = self._auth.get_decoded_token_from_request()
if token is None or 'email' not in token or 'role' not in token: if token is None or "email" not in token or "role" not in token:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') raise ServiceException(ServiceErrorCode.InvalidData, "Token invalid")
role = AuthRoleEnum(token['role']) role = AuthRoleEnum(token["role"])
servers = self._servers.get_servers() servers = self._servers.get_servers()
if role != AuthRoleEnum.admin: if role != AuthRoleEnum.admin:
auth_user = self._auth_users.find_auth_user_by_email(token['email']) auth_user = self._auth_users.find_auth_user_by_email(token["email"])
if auth_user is not None: if auth_user is not None:
user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id) user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id)
servers = servers.where(lambda x: x.server_id in user_ids) servers = servers.where(lambda x: x.server_id in user_ids)
servers = List(ServerDTO, servers) servers = List(ServerDTO, servers)
return servers.select(self._to_dto).where(lambda x: x.name != '') return servers.select(self._to_dto).where(lambda x: x.name != "")
async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO:
token = self._auth.get_decoded_token_from_request() token = self._auth.get_decoded_token_from_request()
if token is None or 'email' not in token or 'role' not in token: if token is None or "email" not in token or "role" not in token:
raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') raise ServiceException(ServiceErrorCode.InvalidData, "Token invalid")
role = AuthRoleEnum(token['role']) role = AuthRoleEnum(token["role"])
filtered_result = self._servers.get_filtered_servers(criteria) filtered_result = self._servers.get_filtered_servers(criteria)
# filter out servers, where the user not exists # filter out servers, where the user not exists
if role != AuthRoleEnum.admin: if role != AuthRoleEnum.admin:
auth_user = self._auth_users.find_auth_user_by_email(token['email']) auth_user = self._auth_users.find_auth_user_by_email(token["email"])
if auth_user is not None: if auth_user is not None:
user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id) user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id)
filtered_result.result = filtered_result.result.where(lambda x: x.server_id in user_ids) filtered_result.result = filtered_result.result.where(lambda x: x.server_id in user_ids)
servers: List = filtered_result.result.select(self._to_dto).where(lambda x: x.name != '') servers: List = filtered_result.result.select(self._to_dto).where(lambda x: x.name != "")
result = List(ServerDTO, servers) result = List(ServerDTO, servers)
if criteria.name is not None and criteria.name != '': if criteria.name is not None and criteria.name != "":
result = result.where(lambda x: criteria.name.lower() in x.name.lower() or x.name.lower() == criteria.name.lower()) result = result.where(
lambda x: criteria.name.lower() in x.name.lower() or x.name.lower() == criteria.name.lower()
)
return ServerFilteredResultDTO( return ServerFilteredResultDTO(List(ServerDTO, result), servers.count())
List(ServerDTO, result),
servers.count()
)
async def get_server_by_id_async(self, id: int) -> ServerDTO: async def get_server_by_id_async(self, id: int) -> ServerDTO:
server = self._servers.get_server_by_id(id) server = self._servers.get_server_by_id(id)

View File

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

View File

@@ -7,7 +7,6 @@ from bot_data.model.auth_user import AuthUser
class AuthUserTransformer(TransformerABC): class AuthUserTransformer(TransformerABC):
@staticmethod @staticmethod
def to_db(dto: AuthUserDTO) -> AuthUser: def to_db(dto: AuthUserDTO) -> AuthUser:
return AuthUser( return AuthUser(
@@ -22,7 +21,7 @@ class AuthUserTransformer(TransformerABC):
None, None,
datetime.now(), datetime.now(),
AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role),
auth_user_id=0 if dto.id is None else dto.id auth_user_id=0 if dto.id is None else dto.id,
) )
@staticmethod @staticmethod
@@ -32,7 +31,7 @@ class AuthUserTransformer(TransformerABC):
db.first_name, db.first_name,
db.last_name, db.last_name,
db.email, db.email,
'' if password is None else password, "" if password is None else password,
db.confirmation_id, db.confirmation_id,
db.auth_role db.auth_role,
) )

View File

@@ -8,7 +8,6 @@ from bot_data.model.server import Server
class ServerTransformer(TransformerABC): class ServerTransformer(TransformerABC):
@staticmethod @staticmethod
def to_db(dto: ServerDTO) -> Server: def to_db(dto: ServerDTO) -> Server:
return Server(dto.discord_id) return Server(dto.discord_id)

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core' __title__ = "bot_core"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports # imports
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core.service' __title__ = "bot_core.abc"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -0,0 +1,61 @@
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Callable
from cpl_query.extension import List
from discord.ext.commands import Context
from bot_data.model.user import User
from modules.base.configuration.base_server_settings import BaseServerSettings
class ClientUtilsABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod
def received_command(self, guild_id: int):
pass
@abstractmethod
def moved_user(self, guild_id: int):
pass
@abstractmethod
def moved_users(self, guild_id: int, count: int):
pass
@abstractmethod
def get_client(self, dc_ic: int, guild_id: int):
pass
@abstractmethod
async def check_if_bot_is_ready_yet(self) -> bool:
pass
@abstractmethod
async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool:
pass
@abstractmethod
async def presence_game(self, t_key: str):
pass
@abstractmethod
def get_auto_complete_list(self, _l: List, current: str, select: Callable = None) -> List:
pass
@abstractmethod
def is_message_xp_count_by_hour_higher_that_max_message_count_per_hour(
self,
created_at: datetime,
user: User,
settings: BaseServerSettings,
is_reaction: bool = False,
) -> bool:
pass
@abstractmethod
def get_ontime_for_user(self, user: User) -> float:
pass

View File

@@ -1,27 +0,0 @@
from abc import ABC, abstractmethod
from discord.ext.commands import Context
class ClientUtilsServiceABC(ABC):
@abstractmethod
def __init__(self): pass
@abstractmethod
def received_command(self, guild_id: int): pass
@abstractmethod
def moved_user(self, guild_id: int): pass
@abstractmethod
def get_client(self, dc_ic: int, guild_id: int): pass
@abstractmethod
async def check_if_bot_is_ready_yet(self) -> bool: pass
@abstractmethod
async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: pass
@abstractmethod
async def presence_game(self, t_key: str): pass

View File

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

View File

@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Union from typing import Union, Optional
import discord import discord
from cpl_query.extension import List from cpl_query.extension import List
@@ -8,24 +8,58 @@ from discord.ext.commands import Context
class MessageServiceABC(ABC): class MessageServiceABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod @abstractmethod
def __init__(self): pass async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False):
pass
@abstractmethod @abstractmethod
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass async def delete_message(self, message: discord.Message, without_tracking=False):
pass
@abstractmethod @abstractmethod
async def delete_message(self, message: discord.Message, without_tracking=False): pass async def send_channel_message(
self,
channel: discord.TextChannel,
message: Union[str, discord.Embed],
without_tracking=True,
):
pass
@abstractmethod @abstractmethod
async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass async def send_dm_message(
self,
message: Union[str, discord.Embed],
receiver: Union[discord.User, discord.Member],
without_tracking=False,
):
pass
@abstractmethod @abstractmethod
async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass async def send_ctx_msg(
self,
ctx: Context,
message: Union[str, discord.Embed],
file: discord.File = None,
is_persistent: bool = False,
is_public: bool = False,
wait_before_delete: int = None,
without_tracking=True,
) -> Optional[discord.Message]:
pass
@abstractmethod @abstractmethod
async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass async def send_interaction_msg(
self,
@abstractmethod interaction: Interaction,
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 message: Union[str, discord.Embed],
is_persistent: bool = False,
is_public: bool = False,
wait_before_delete: int = None,
without_tracking=True,
**kwargs
):
pass

View File

@@ -7,7 +7,6 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
class ModuleABC(StartupExtensionABC): class ModuleABC(StartupExtensionABC):
@abstractmethod @abstractmethod
def __init__(self, dc: DiscordCollectionABC, feature_flag: FeatureFlagsEnum): def __init__(self, dc: DiscordCollectionABC, feature_flag: FeatureFlagsEnum):
StartupExtensionABC.__init__(self) StartupExtensionABC.__init__(self)

View File

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

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core.configuration' __title__ = "bot_core.configuration"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -8,7 +8,6 @@ from bot_core.configuration.file_logging_settings import FileLoggingSettings
class BotLoggingSettings(ConfigurationModelABC): class BotLoggingSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._files: List[FileLoggingSettings] = List(FileLoggingSettings) self._files: List[FileLoggingSettings] = List(FileLoggingSettings)
@@ -22,12 +21,12 @@ class BotLoggingSettings(ConfigurationModelABC):
files = List(FileLoggingSettings) files = List(FileLoggingSettings)
for s in settings: for s in settings:
st = FileLoggingSettings() st = FileLoggingSettings()
settings[s]['Key'] = s settings[s]["Key"] = s
st.from_dict(settings[s]) st.from_dict(settings[s])
files.append(st) files.append(st)
self._files = files self._files = files
except Exception as e: except Exception as e:
Console.set_foreground_color(ForegroundColorEnum.red) Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")
Console.set_foreground_color(ForegroundColorEnum.default) Console.set_foreground_color(ForegroundColorEnum.default)

View File

@@ -8,7 +8,6 @@ from bot_core.configuration.server_settings import ServerSettings
class BotSettings(ConfigurationModelABC): class BotSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
@@ -16,7 +15,8 @@ class BotSettings(ConfigurationModelABC):
self._technicians: List[int] = List(int) self._technicians: List[int] = List(int)
self._wait_for_restart = 2 self._wait_for_restart = 2
self._wait_for_shutdown = 2 self._wait_for_shutdown = 2
self._cache_max_messages = 1000
@property @property
def servers(self) -> List[ServerSettings]: def servers(self) -> List[ServerSettings]:
return self._servers return self._servers
@@ -33,6 +33,10 @@ class BotSettings(ConfigurationModelABC):
def wait_for_shutdown(self) -> int: def wait_for_shutdown(self) -> int:
return self._wait_for_shutdown return self._wait_for_shutdown
@property
def cache_max_messages(self) -> int:
return self._cache_max_messages
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._technicians = settings["Technicians"] self._technicians = settings["Technicians"]
@@ -41,6 +45,11 @@ class BotSettings(ConfigurationModelABC):
settings.pop("Technicians") settings.pop("Technicians")
settings.pop("WaitForRestart") settings.pop("WaitForRestart")
settings.pop("WaitForShutdown") settings.pop("WaitForShutdown")
if "CacheMaxMessages" in settings:
self._cache_max_messages = settings["CacheMaxMessages"]
settings.pop("CacheMaxMessages")
servers = List(ServerSettings) servers = List(ServerSettings)
for s in settings: for s in settings:
st = ServerSettings() st = ServerSettings()
@@ -49,5 +58,5 @@ class BotSettings(ConfigurationModelABC):
servers.append(st) servers.append(st)
self._servers = servers self._servers = servers
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -3,19 +3,19 @@ from enum import Enum
class FeatureFlagsEnum(Enum): class FeatureFlagsEnum(Enum):
# modules # modules
api_module = 'ApiModule' api_module = "ApiModule"
admin_module = 'AdminModule' admin_module = "AdminModule"
auto_role_module = 'AutoRoleModule' auto_role_module = "AutoRoleModule"
base_module = 'BaseModule' base_module = "BaseModule"
boot_log_module = 'BootLogModule' boot_log_module = "BootLogModule"
core_module = 'CoreModule' core_module = "CoreModule"
core_extension_module = 'CoreExtensionModule' core_extension_module = "CoreExtensionModule"
data_module = 'DataModule', data_module = ("DataModule",)
database_module = 'DatabaseModule', database_module = ("DatabaseModule",)
level_module = 'LevelModule' level_module = "LevelModule"
moderator_module = 'ModeratorModule' moderator_module = "ModeratorModule"
permission_module = 'PermissionModule' permission_module = "PermissionModule"
stats_module = 'StatsModule' stats_module = "StatsModule"
# features # features
api_only = 'ApiOnly' api_only = "ApiOnly"
presence = 'Presence' presence = "Presence"

View File

@@ -8,7 +8,6 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
class FeatureFlagsSettings(ConfigurationModelABC): class FeatureFlagsSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
@@ -47,5 +46,5 @@ class FeatureFlagsSettings(ConfigurationModelABC):
for flag in [f.value for f in FeatureFlagsEnum]: for flag in [f.value for f in FeatureFlagsEnum]:
self._load_flag(settings, FeatureFlagsEnum(flag)) self._load_flag(settings, FeatureFlagsEnum(flag))
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

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

View File

@@ -5,7 +5,6 @@ from cpl_core.console import Console
class ServerSettings(ConfigurationModelABC): class ServerSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
@@ -22,8 +21,8 @@ class ServerSettings(ConfigurationModelABC):
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._id = int(settings['Id']) self._id = int(settings["Id"])
self._message_delete_timer = int(settings['MessageDeleteTimer']) self._message_delete_timer = int(settings["MessageDeleteTimer"])
except Exception as e: except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in settings') Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in settings")
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core.core_extension' __title__ = "bot_core.core_extension"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -3,7 +3,7 @@ from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.dependency_injection import ServiceProviderABC
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
@@ -13,7 +13,6 @@ from modules.permission.abc.permission_service_abc import PermissionServiceABC
class CoreExtension(ApplicationExtensionABC): class CoreExtension(ApplicationExtensionABC):
def __init__(self): def __init__(self):
ApplicationExtensionABC.__init__(self) ApplicationExtensionABC.__init__(self)
@@ -23,7 +22,7 @@ class CoreExtension(ApplicationExtensionABC):
return return
permissions: PermissionServiceABC = services.get_service(PermissionServiceABC) permissions: PermissionServiceABC = services.get_service(PermissionServiceABC)
client_utils: ClientUtilsServiceABC = services.get_service(ClientUtilsServiceABC) client_utils: ClientUtilsABC = services.get_service(ClientUtilsABC)
message_service: MessageServiceABC = services.get_service(MessageServiceABC) message_service: MessageServiceABC = services.get_service(MessageServiceABC)
t: TranslatePipe = services.get_service(TranslatePipe) t: TranslatePipe = services.get_service(TranslatePipe)
CommandChecks.init(permissions, client_utils, message_service, t) CommandChecks.init(permissions, client_utils, message_service, t)

View File

@@ -6,11 +6,12 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.core_extension.core_extension_on_ready_event import CoreExtensionOnReadyEvent from bot_core.core_extension.core_extension_on_ready_event import (
CoreExtensionOnReadyEvent,
)
class CoreExtensionModule(ModuleABC): class CoreExtensionModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC): def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_extension_module) ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_extension_module)

View File

@@ -5,17 +5,16 @@ from cpl_discord.events import OnReadyABC
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.client_utils_abc import ClientUtilsABC
class CoreExtensionOnReadyEvent(OnReadyABC): class CoreExtensionOnReadyEvent(OnReadyABC):
def __init__( def __init__(
self, self,
logger: LoggerABC, logger: LoggerABC,
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC, client_utils: ClientUtilsABC,
t: TranslatePipe t: TranslatePipe,
): ):
OnReadyABC.__init__(self) OnReadyABC.__init__(self)
@@ -24,9 +23,9 @@ class CoreExtensionOnReadyEvent(OnReadyABC):
self._client_utils = client_utils self._client_utils = client_utils
self._t = t self._t = t
self._logger.info(__name__, f'Module {type(self)} loaded') self._logger.info(__name__, f"Module {type(self)} loaded")
async def on_ready(self): async def on_ready(self):
self._logger.debug(__name__, f'Module {type(self)} started') self._logger.debug(__name__, f"Module {type(self)} started")
await self._client_utils.presence_game('common.presence.running') await self._client_utils.presence_game("common.presence.running")
self._logger.trace(__name__, f'Module {type(self)} stopped') self._logger.trace(__name__, f"Module {type(self)} stopped")

View File

@@ -4,7 +4,7 @@ from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.abc.module_abc import ModuleABC from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
@@ -15,7 +15,6 @@ from bot_core.service.message_service import MessageService
class CoreModule(ModuleABC): class CoreModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC): def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_module) ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_module)
@@ -24,7 +23,7 @@ class CoreModule(ModuleABC):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(MessageServiceABC, MessageService) services.add_transient(MessageServiceABC, MessageService)
services.add_transient(ClientUtilsServiceABC, ClientUtilsService) services.add_transient(ClientUtilsABC, ClientUtilsService)
# pipes # pipes
services.add_transient(DateTimeOffsetPipe) services.add_transient(DateTimeOffsetPipe)

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core.events' __title__ = "bot_core.events"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

View File

@@ -3,17 +3,16 @@ from cpl_discord.events import OnReadyABC
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.client_utils_abc import ClientUtilsABC
class CoreOnReadyEvent(OnReadyABC): class CoreOnReadyEvent(OnReadyABC):
def __init__( def __init__(
self, self,
logger: LoggerABC, logger: LoggerABC,
bot: DiscordBotServiceABC, bot: DiscordBotServiceABC,
client_utils: ClientUtilsServiceABC, client_utils: ClientUtilsABC,
t: TranslatePipe, t: TranslatePipe,
): ):
OnReadyABC.__init__(self) OnReadyABC.__init__(self)
@@ -22,9 +21,9 @@ class CoreOnReadyEvent(OnReadyABC):
self._client_utils = client_utils self._client_utils = client_utils
self._t = t self._t = t
self._logger.info(__name__, f'Module {type(self)} loaded') self._logger.info(__name__, f"Module {type(self)} loaded")
async def on_ready(self): async def on_ready(self):
self._logger.debug(__name__, f'Module {type(self)} started') self._logger.debug(__name__, f"Module {type(self)} started")
await self._client_utils.presence_game('common.presence.booting') await self._client_utils.presence_game("common.presence.booting")
self._logger.trace(__name__, f'Module {type(self)} stopped') self._logger.trace(__name__, f"Module {type(self)} stopped")

View File

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

View File

@@ -2,6 +2,5 @@ from discord.ext.commands import CommandError
class CheckError(CommandError): class CheckError(CommandError):
def __init__(self, message, *args): def __init__(self, message, *args):
CommandError.__init__(self, message, *args) CommandError.__init__(self, message, *args)

View File

@@ -6,21 +6,21 @@ bot Keksdose bot
Discord bot for the Keksdose discord Server Discord bot for the Keksdose discord Server
:copyright: (c) 2022 sh-edraft.de :copyright: (c) 2022 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'bot_core.helper' __title__ = "bot_core.helper"
__author__ = 'Sven Heidemann' __author__ = "Sven Heidemann"
__license__ = 'MIT' __license__ = "MIT"
__copyright__ = 'Copyright (c) 2022 sh-edraft.de' __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = '0.3.dev25' __version__ = "0.3.1"
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major='0', minor='3', micro='dev25') version_info = VersionInfo(major="0", minor="3", micro="1")

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