164 Commits

Author SHA1 Message Date
db61a764eb Merge pull request 'staging' (#447) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m45s
Reviewed-on: #447
2023-12-02 19:34:22 +01:00
919eef79f6 Merge branch 'master' into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m58s
2023-12-02 19:33:58 +01:00
a2dd447dbd Always check scheduled_events after scheduled_event update #410
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m26s
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m33s
2023-12-02 15:32:16 +01:00
8a76b46165 Improved event time loading #410
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 5m12s
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m40s
2023-12-02 15:24:13 +01:00
af3084ad36 Fixed scheduled event end update #410
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m41s
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m39s
2023-12-01 21:04:29 +01:00
285b8bdbe4 Fixed scheduled events #410
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m44s
2023-12-01 20:40:44 +01:00
e2da4f09ee Fixed command
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m50s
2023-12-01 20:02:42 +01:00
4ed99da689 Merge pull request 'dev' (#445) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m32s
Reviewed-on: #445
2023-12-01 19:55:54 +01:00
bc94d31a8d Added logs cleanup
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m30s
2023-12-01 19:50:18 +01:00
0d3db75190 Improved scheduled_event handling #410
Some checks reported warnings
Deploy dev on push / on-push-deploy_sh-edraft (push) Has been cancelled
2023-12-01 19:06:11 +01:00
090f217f93 Fixed scheduled_events #410
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m37s
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m39s
2023-11-30 17:57:43 +01:00
b98828fce3 Fixed mass move #444 2023-11-30 17:54:31 +01:00
5f8ae787f0 Improved profile voice states & fixed new vs handling
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 5m31s
2023-11-20 09:28:53 +01:00
0c807a7de7 Merge pull request 'dev into staging' (#442) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m56s
Reviewed-on: #442
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2023-11-19 14:49:59 +01:00
2dc60acaa6 Merge branch 'staging' into dev
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m45s
2023-11-19 14:42:42 +01:00
29ea96a5e5 Fixed new scheduled event
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 5m4s
2023-11-19 14:35:26 +01:00
026331b397 Merge pull request '#440' (#441) from #440 into dev
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m43s
Reviewed-on: #441
2023-11-19 14:18:54 +01:00
bfe74ad1c5 Fixed frontend version #440 2023-11-19 14:17:47 +01:00
7c8c2bef70 Added flag handling to auth controller #440 2023-11-19 14:11:28 +01:00
4ccb57e6a3 Added feature flag for basic auth stuff #440 2023-11-19 14:01:26 +01:00
c8d3bf780d Merge pull request '#410' (#439) from #410 into dev
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m49s
Reviewed-on: #439
2023-11-19 12:57:42 +01:00
8788b727c5 Some fixes #410 2023-11-19 12:56:55 +01:00
5e9280d972 Fixed loop timer #410 2023-11-19 12:37:20 +01:00
bd856d0143 Added scheduled_event handler #410 2023-11-19 12:32:42 +01:00
da57063b68 Completed frontend #410 2023-11-19 00:31:25 +01:00
171aa63df9 Improved update & add logic #410 2023-11-18 23:10:28 +01:00
74dba4b981 Added add dialog #410 2023-11-18 22:15:18 +01:00
a3ebd07093 Working on add/edit dialog #410 2023-11-15 19:35:20 +01:00
2de5afd648 Rebased & fixed backend #410 2023-11-15 19:35:17 +01:00
e8cc42e155 Added scheduledEvent to gql #410 2023-11-15 19:35:17 +01:00
20e20969e4 Added scheduled event to db #410 2023-11-15 19:35:17 +01:00
802d5478d1 Fixed migration service
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m14s
2023-11-15 19:30:29 +01:00
05f718f3ae Set version 1.2.2
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m11s
2023-11-15 18:18:37 +01:00
7682b966a8 Reformatted
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m11s
2023-11-15 18:05:36 +01:00
7cb4f03554 Merge pull request 'Fixed workspace #428' (#438) from #428 into dev
Some checks reported warnings
Deploy dev on push / on-push-deploy_sh-edraft (push) Has been cancelled
Reviewed-on: #438
2023-11-15 18:05:07 +01:00
b1b74b2551 Merge branch 'dev' into #428 2023-11-15 18:04:59 +01:00
bbad4100dc Fixed workspace #428 2023-11-15 17:36:30 +01:00
25df0e4876 Merge pull request '#428' (#436) from #428 into dev
Some checks failed
Deploy dev on push / on-push-deploy_sh-edraft (push) Failing after 2m32s
Reviewed-on: #436
2023-11-15 16:56:31 +01:00
4ba40b826a Fixed scripts #428 2023-11-15 16:56:08 +01:00
06a0eba5c5 Added migration to old naming safety #428 2023-11-14 23:50:05 +01:00
fe5b0207c0 Added version logic & fixed downgrade scripts #428 2023-11-13 23:38:04 +01:00
e01c738cf0 Removed old logic #428 2023-11-13 22:57:17 +01:00
d2d59bdad7 Removed old scripts #428 2023-11-13 22:55:35 +01:00
dd3bfa68c6 Finished script migration #428 2023-11-13 22:54:47 +01:00
4e12ba5ffe Added other scripts #428 2023-11-10 23:39:21 +01:00
35a8b8f592 Added 1.0.0 scripts #428 2023-11-10 23:36:32 +01:00
5c8feed8aa Readded known scripts & added formatting #428 2023-11-10 23:35:56 +01:00
e018fdcbdf Updated and added script order #428 2023-11-10 23:34:07 +01:00
39b9def76c Fixed versions #428 2023-11-10 13:28:54 +01:00
2801c617f6 Fixed migrations #428 2023-11-10 13:27:58 +01:00
5461a6d8dc Added 0.3.1 migration scripts #428 2023-11-10 13:25:53 +01:00
d93c3ad6c7 Added 0.3 migration scripts #428 2023-11-10 13:23:23 +01:00
aec7dac4c7 Added 0.2.2 migration scripts #428 2023-11-10 13:22:36 +01:00
407ec08463 Added 0.1 migration scripts #428 2023-11-10 13:20:33 +01:00
4628f31993 Added logic to handle down and upgrades #428 2023-11-10 13:19:23 +01:00
1f2fbc362f Improved sql migration to script migration tool #428 2023-11-10 12:56:02 +01:00
666b20d3a9 Added mock db base #428 2023-11-10 11:57:54 +01:00
5de6710261 Merge pull request 'staging' (#435) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m34s
Reviewed-on: #435
2023-11-07 20:37:13 +01:00
e99e272029 Merge branch 'master' into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m12s
2023-11-07 20:37:03 +01:00
650f612a6b Fixed base on member join
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m19s
2023-11-07 20:36:29 +01:00
38093ab817 Removed maintenance mode
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m10s
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m12s
2023-11-07 18:27:57 +01:00
d2c37a0098 Merge pull request 'staging' (#434) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m15s
Reviewed-on: #434
2023-11-07 18:21:58 +01:00
804aa0b9b8 Fixed some histories
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m1s
2023-11-07 18:16:10 +01:00
692cf8de31 Merge pull request 'Probably fixed server stats #432' (#433) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m19s
Reviewed-on: #433
2023-11-07 18:07:11 +01:00
b15c3b7fa3 Set version 1.2.1
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m15s
2023-11-07 18:01:23 +01:00
49121fd179 Probably fixed server stats #432
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m25s
2023-11-07 17:56:57 +01:00
9bad75e7c2 Temporarily removed server stats
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 5m7s
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m16s
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m11s
2023-11-07 00:28:22 +01:00
7358b67072 Fixed server loading in web
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 5m6s
2023-11-07 00:06:17 +01:00
1b0ba01258 Fixed gql types
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m52s
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m3s
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m3s
2023-11-06 23:29:11 +01:00
6fc5ee3ed3 Merge pull request 'Fixed DeleteUserJoinedServer' (#431) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m18s
Reviewed-on: #431
2023-11-06 21:43:36 +01:00
099707446d Merge branch 'master' into staging
Some checks reported warnings
Deploy staging on push / on-push-deploy_sh-edraft (push) Has been cancelled
2023-11-06 21:43:25 +01:00
d6b7fd73df Fixed DeleteUserJoinedServer
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m2s
2023-11-06 21:42:52 +01:00
6bfb0ddbf9 Merge pull request 'Fixed remove user process' (#429) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m10s
Reviewed-on: #429
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2023-11-06 21:34:29 +01:00
e1b76fa628 Possibly fixed rate limit in actions
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m0s
2023-11-06 21:26:25 +01:00
e6f98def6a Possibly fixed rate limit in actions 2023-11-06 21:26:25 +01:00
590479eee2 Set migration only on prod 2023-11-06 21:26:25 +01:00
d25305be4a Merge pull request 'Fixed migration scripts' (#427) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m24s
Reviewed-on: #427
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2023-11-06 20:26:57 +01:00
2889a97f17 Merge branch 'master' into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m21s
2023-11-06 20:25:59 +01:00
b540821a32 Fixed migration scripts
Some checks reported warnings
Deploy staging on push / on-push-deploy_sh-edraft (push) Has been cancelled
2023-11-06 20:25:10 +01:00
5b43b72838 Merge pull request 'staging into master' (#426) from staging into master
All checks were successful
Deploy prod on push / on-push-deploy_sh-edraft (push) Successful in 3m30s
Reviewed-on: #426
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2023-11-06 20:06:33 +01:00
46cd33d194 Updated gitea actions
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m11s
2023-11-06 20:05:10 +01:00
1287829bdf Set migration only on prod
Some checks failed
Deploy staging on push / on-push-deploy_sh-edraft (push) Failing after 5s
2023-11-06 19:41:52 +01:00
c91c8b2bcd Updated config
Some checks failed
Deploy staging on push / on-push-deploy_sh-edraft (push) Failing after 5s
2023-11-06 19:25:40 +01:00
a5b4c4cd66 Updated config
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m27s
2023-11-06 19:22:40 +01:00
1688347367 Merge pull request 'dev into staging' (#425) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m54s
Reviewed-on: #425
2023-11-06 19:06:09 +01:00
6e79811bc9 Added maintenance option to technician settings #424
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m20s
2023-11-06 18:56:04 +01:00
e1258151de Added maintenance to db and gql #424 2023-11-06 18:02:09 +01:00
a7c833b9db Added maintenance mode #423
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m9s
2023-11-05 22:22:39 +01:00
2f161d2f43 Fixed technicianFullDataAccess
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m59s
2023-11-05 17:50:37 +01:00
0a5f23f1af Merge pull request 'dev' (#422) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m22s
Reviewed-on: #422
2023-11-05 17:07:56 +01:00
23a3dbbc5e Merge branch 'staging' into dev
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m42s
2023-11-05 17:07:48 +01:00
8975665407 Merge pull request '#420' (#421) from #420 into dev
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m23s
Reviewed-on: #421
2023-11-05 15:51:47 +01:00
90de90684c Added statistics #420 2023-11-05 15:51:25 +01:00
d1ecfe9603 Added activity score calculation #420 2023-11-05 15:34:17 +01:00
f7f25e9428 Added base for server statistics #420 2023-11-05 14:01:24 +01:00
6ae7ab5c5a Set higher timer for steam offer watcher
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m20s
2023-11-05 11:48:45 +01:00
315c90cacc Fixed dropdown width
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m57s
2023-11-05 00:38:44 +01:00
2caa910613 Merge pull request 'dev into staging' (#415) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m29s
Reviewed-on: #415
2023-11-05 00:29:44 +01:00
8e55ebc4b7 Added active member count #413
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 6m38s
2023-11-05 00:18:01 +01:00
665e524aa9 Fixed menu design
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m13s
2023-11-04 23:56:36 +01:00
ae021ac7ea Added custom level header to make multi env compatible
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m0s
2023-11-04 23:43:54 +01:00
dcbb481b10 Added setting for max steam offer count 2023-11-04 23:37:07 +01:00
e6165caed9 Only view first 150 elements of steam offers
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m27s
2023-11-04 18:01:40 +01:00
58fdc9f48f Fixed staging icon
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 4m5s
2023-11-04 17:54:39 +01:00
1c02608410 Merge pull request 'dev into staging' (#414) from dev into staging
Some checks failed
Deploy staging on push / on-push-deploy_sh-edraft (push) Failing after 2m30s
Reviewed-on: #414
2023-11-04 17:47:23 +01:00
5e0b322273 Removed invalid files
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m48s
2023-11-04 15:33:28 +01:00
4f336bed05 Fixed technician config loading
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m50s
2023-11-04 15:21:22 +01:00
99d476df86 Fixed version handling
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m20s
2023-11-04 14:40:42 +01:00
81062e74d7 Fixed docker
Some checks failed
Deploy dev on push / on-push-deploy_sh-edraft (push) Failing after 3m28s
2023-11-04 14:27:27 +01:00
acae8ac891 Fixed deploy actions
Some checks failed
Deploy dev on push / on-push-deploy_sh-edraft (push) Failing after 3m19s
2023-11-04 14:22:15 +01:00
e3dc0f725e Possibly fixed web build
Some checks failed
Deploy dev on push / on-push-deploy_sh-edraft (push) Failing after 3m18s
2023-11-04 14:13:55 +01:00
40a7919a3f Set migration only for test purpose
Some checks failed
Deploy dev on push / on-push-deploy_sh-edraft (push) Failing after 3m16s
2023-11-04 14:00:31 +01:00
14d0e38c5d Improved deployment 2023-11-04 13:21:55 +01:00
62649226ba Updated config to deploy dev 2023-11-04 13:06:11 +01:00
0a04ad74d8 Added dev env 2023-11-04 13:02:09 +01:00
467050d87e Set steam offer loop to one hour 2023-11-04 11:04:04 +01:00
e8f2a01851 Updated config
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m31s
2023-11-03 15:19:04 +01:00
f972055ebf Added translation for short role name position 2023-11-03 15:07:23 +01:00
9bbfe498cc Fixed channel sort
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m43s
2023-11-03 15:02:27 +01:00
b21c9f894f Fixed sale count for steam offers
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m35s
2023-11-03 14:54:25 +01:00
7a0f29f557 Improved workflows
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m42s
2023-11-03 14:15:59 +01:00
ed72620d33 Changed steam sale offer checker
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 4m5s
2023-11-03 08:12:21 +01:00
b8356917b3 Prevent logging bots 2023-11-03 08:12:21 +01:00
84fedfaa0b Improved data validation for import 2023-11-03 08:12:21 +01:00
2d358188af Fixed formatting 2023-11-03 08:12:21 +01:00
79278d8f4b Fixed sync by member command 2023-11-03 08:12:21 +01:00
b943b9a7ed Fixed version #407 2023-11-03 08:12:21 +01:00
1c20bbed66 Added tooltip translation #407 2023-11-03 08:12:21 +01:00
33006e3d01 Added data import & export #407 2023-11-03 08:12:21 +01:00
90ae55c0d4 Fixed birthday gql output 2023-11-03 08:12:21 +01:00
626d0891f9 Fixed config loading query 2023-11-03 08:12:21 +01:00
09cccd1b89 Fixed deps 2023-11-03 08:12:21 +01:00
940a2ed065 Fixed add user 2023-11-03 08:12:21 +01:00
c2fa1a1c00 Fixed docker config 2023-11-03 08:12:21 +01:00
1f1b1f7b65 Fixed docker config 2023-11-03 08:12:21 +01:00
4b52995143 Updated image name in stack 2023-11-03 08:12:21 +01:00
6a6d19686c Fixed workflow 2023-11-03 08:12:21 +01:00
798162fb62 Readded configs 2023-11-03 08:12:21 +01:00
74c71abea5 Readded docker 2023-11-03 08:12:21 +01:00
0e679eda32 Fixed action 2023-11-03 08:12:21 +01:00
403c169b71 Fixed action 2023-11-03 08:12:21 +01:00
a113e21e19 Fixed angular.json 2023-11-03 08:12:21 +01:00
71c091c226 Added todo comment 2023-11-03 08:12:21 +01:00
b8d27cc682 Fixed sorting 2023-11-03 08:12:21 +01:00
c9b967b9d0 Changed env var prefix #405 2023-11-03 08:12:21 +01:00
410049be6e Updated web build #405 2023-11-03 08:12:21 +01:00
be6361f619 Formatted stuff #405 2023-11-03 08:12:21 +01:00
3a7daf8b42 Fixed submodules #405 2023-11-03 08:12:21 +01:00
f435d3dd48 Moved folders #405 2023-11-03 08:12:21 +01:00
eb32bec43c Fixed task logging #404 2023-11-03 08:12:21 +01:00
232429a77d Updated cpl-discord #404 2023-11-03 08:12:21 +01:00
0946734633 Removed mysql_native_password #404 2023-11-03 08:12:21 +01:00
ea1472a9eb Fixed type param for get_services #404 2023-11-03 08:12:21 +01:00
f710f6ad6c Build new version #404 2023-11-03 08:12:21 +01:00
030fedfcfb Updated deps #404 2023-11-03 08:12:21 +01:00
bd1d230f67 Added birthday watcher #401 2023-11-03 08:12:21 +01:00
dcafa63d74 Improved steam offer #188 2023-11-03 08:12:21 +01:00
3a42b42dbf Improved steam offer #188 2023-11-03 08:12:21 +01:00
4747f23202 Get steam offers #188 2023-11-03 08:12:21 +01:00
5057cbed16 Added birthday command #401 2023-11-03 08:12:21 +01:00
7aff767a4b Added birthday to wi #401 2023-11-03 08:12:21 +01:00
61bdc8a52a Improved user warnings in WI #402 2023-11-03 08:12:21 +01:00
5afd0fafa8 Show user warnings in profile & lazy load other stuff #402 2023-11-03 08:12:21 +01:00
eeda0405f3 Merge pull request 'dev' (#400) from dev into master
All checks were successful
Deploy dev on push / on-push-deploy_sh-edraft (push) Successful in 3m13s
Reviewed-on: sh-edraft.de/kd_discord_bot#400
2023-10-03 11:09:46 +02:00
546 changed files with 8592 additions and 6534 deletions

View File

@@ -8,50 +8,56 @@ on:
jobs:
on-push-deploy_sh-edraft:
runs-on: [ dobby.sh-edraft.de, ubuntu-latest ]
container: catthehacker/ubuntu:act-latest
container: sh-edraft.de/act-runner:latest
steps:
- name: Setup Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10.12"
- run: python -v
- name: Setup docker
uses: https://github.com/papodaca/install-docker-action@main
- run: docker -v
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
- name: Shutdown stack
run: docker stack rm kdb_staging
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
submodules: true
- name: Prepare bot build
run: |
cd kdb-bot
pip install --extra-index-url https://pip.sh-edraft.de cpl-cli
cd bot
python3.10 -m pip install --extra-index-url https://pip.sh-edraft.de cpl-cli
cpl i
- name: Build docker bot
run: |
cd kdb-bot
docker image prune -f
cpl docker-build
- name: Setup node
uses: https://github.com/actions/setup-node@v3
- name: Prepare web build
run: |
cd kdb-web
cd web
npm install -g ts-node
npm i
npm ci
- name: Shutdown stack
run: docker stack rm sdb_dev
- name: Build docker bot
run: |
cd bot
docker image prune -f
cpl build
docker build -t sh-edraft.de/sdb-bot:$(cpl gv)-dev .
- name: Build docker web
run: |
cd kdb-web
cd web
docker image prune -f
npm run docker-build
cp src/favicon.dev.ico src/favicon.ico
npm run build
docker build -t sh-edraft.de/sdb-web:$(npm run -s gv)-dev .
- name: Set version
run: |
cd bot/docker
chmod +x ./set-docker-compose-image-version.sh
./set-docker-compose-image-version.sh sh-edraft.de/sdb-bot:$(cd ../; cpl gv)-dev sh-edraft.de/sdb-web:$(cd ../../web; npm run -s gv;)-dev
- name: Deploy Stack to sh-edraft.de
uses: https://github.com/kgierke/portainer-stack-deployment@v1
@@ -60,6 +66,6 @@ jobs:
portainer-username: "gitea_job"
portainer-password: "${{ secrets.docker_job }}"
portainer-endpoint: 2
name: kdb_staging
file: ./docker-compose.staging.yml
name: sdb_dev
file: bot/docker/docker-compose.dev.yml
variables: '{}'

View File

@@ -1,5 +1,5 @@
name: Deploy dev on push
run-name: Deploy dev on push
name: Deploy prod on push
run-name: Deploy prod on push
on:
push:
branches:
@@ -8,50 +8,55 @@ on:
jobs:
on-push-deploy_sh-edraft:
runs-on: [ dobby.sh-edraft.de, ubuntu-latest ]
container: catthehacker/ubuntu:act-latest
container: sh-edraft.de/act-runner:latest
steps:
- name: Setup Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10.12"
- run: python -v
- name: Setup docker
uses: https://github.com/papodaca/install-docker-action@main
- run: docker -v
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
- name: Shutdown stack
run: docker stack rm kdb_prod
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
submodules: true
- name: Prepare bot build
run: |
cd kdb-bot
pip install --extra-index-url https://pip.sh-edraft.de cpl-cli
cd bot
python3.10 -m pip install --extra-index-url https://pip.sh-edraft.de cpl-cli
cpl i
- name: Build docker bot
run: |
cd kdb-bot
docker image prune -f
cpl docker-build
- name: Setup node
uses: https://github.com/actions/setup-node@v3
- name: Prepare web build
run: |
cd kdb-web
cd web
npm install -g ts-node
npm i
npm ci
- name: Shutdown stack
run: docker stack rm sdb_prod
- name: Build docker bot
run: |
cd bot
docker image prune -f
cpl build
docker build -t sh-edraft.de/sdb-bot:$(cpl gv) .
- name: Build docker web
run: |
cd kdb-web
cd web
docker image prune -f
npm run docker-build
npm run build
docker build -t sh-edraft.de/sdb-web:$(npm run -s gv) .
- name: Set version
run: |
cd bot/docker
chmod +x ./set-docker-compose-image-version.sh
./set-docker-compose-image-version.sh sh-edraft.de/sdb-bot:$(cd ../; cpl gv) sh-edraft.de/sdb-web:$(cd ../../web; npm run -s gv;)
- name: Deploy Stack to sh-edraft.de
uses: https://github.com/kgierke/portainer-stack-deployment@v1
@@ -60,6 +65,6 @@ jobs:
portainer-username: "gitea_job"
portainer-password: "${{ secrets.docker_job }}"
portainer-endpoint: 2
name: kdb_prod
file: ./docker-compose.yml
name: sdb_prod
file: bot/docker/docker-compose.yml
variables: '{}'

View File

@@ -0,0 +1,71 @@
name: Deploy staging on push
run-name: Deploy staging on push
on:
push:
branches:
- staging
jobs:
on-push-deploy_sh-edraft:
runs-on: [ dobby.sh-edraft.de, ubuntu-latest ]
container: sh-edraft.de/act-runner:latest
steps:
- name: Setup docker
uses: https://github.com/papodaca/install-docker-action@main
- run: docker -v
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
submodules: true
- name: Prepare bot build
run: |
cd bot
python3.10 -m pip install --extra-index-url https://pip.sh-edraft.de cpl-cli
cpl i
- name: Setup node
uses: https://github.com/actions/setup-node@v3
- name: Prepare web build
run: |
cd web
npm install -g ts-node
npm ci
- name: Shutdown stack
run: docker stack rm sdb_staging
- name: Build docker bot
run: |
cd bot
docker image prune -f
cpl build
docker build -t sh-edraft.de/sdb-bot:$(cpl gv)-staging .
- name: Build docker web
run: |
cd web
docker image prune -f
cp src/favicon.staging.ico src/favicon.ico
npm run build
docker build -t sh-edraft.de/sdb-web:$(npm run -s gv)-staging .
- name: Set version
run: |
cd bot/docker
chmod +x ./set-docker-compose-image-version.sh
./set-docker-compose-image-version.sh sh-edraft.de/sdb-bot:$(cd ../; cpl gv)-staging sh-edraft.de/sdb-web:$(cd ../../web; npm run -s gv;)-staging
- name: Deploy Stack to sh-edraft.de
uses: https://github.com/kgierke/portainer-stack-deployment@v1
with:
portainer-url: "https://docker.sh-edraft.de"
portainer-username: "gitea_job"
portainer-password: "${{ secrets.docker_job }}"
portainer-endpoint: 2
name: sdb_staging
file: bot/docker/docker-compose.staging.yml
variables: '{}'

View File

@@ -21,7 +21,8 @@
"checks": "tools/checks/checks.json",
"get-version": "tools/get_version/get-version.json",
"post-build": "tools/post_build/post-build.json",
"set-version": "tools/set_version/set-version.json"
"set-version": "tools/set_version/set-version.json",
"migration-to-sql": "tools/migration_to_sql/migration-to-sql.json"
},
"Scripts": {
"format": "black ./",
@@ -32,12 +33,12 @@
"pre-build": "cpl set-version $ARGS; black ./;",
"post-build": "cpl run post-build --dev; black ./;",
"pre-prod": "cpl build",
"prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;",
"prod": "export SDB_ENVIRONMENT=production; export SDB_NAME=SDB-Prod; cpl start;",
"pre-stage": "cpl build",
"stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;",
"stage": "export SDB_ENVIRONMENT=staging; export SDB_NAME=SDB-Stage; cpl start;",
"pre-dev": "cpl build",
"dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;",
"docker-build": "cpl build $ARGS; docker build -t sh-edraft.de/kdb-bot:$(cpl gv) .;",
"dev": "export SDB_ENVIRONMENT=development; export SDB_NAME=SDB-Dev; cpl start;",
"docker-build": "cpl build $ARGS; docker build -t sh-edraft.de/sdb-bot:$(cpl gv) .;",
"dc-up": "docker-compose up -d",
"dc-down": "docker-compose down",
"docker": "cpl dc-down; cpl docker-build; cpl dc-up;"

1
bot/docker Submodule

Submodule bot/docker added at b0bacce9f6

View File

@@ -2,7 +2,7 @@
FROM python:3.10.4-alpine
WORKDIR /app
COPY ./dist/bot/build/kdb-bot/ .
COPY ./dist/bot/build/bot/ .
COPY ./dist/bot/build/requirements.txt .
RUN python -m pip install --upgrade pip

View File

@@ -15,7 +15,7 @@ __title__ = "bot"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -11,6 +11,7 @@ from bot_api.api_thread import ApiThread
from bot_core.abc.task_abc import TaskABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_core.environment_variables import MAINTENANCE
from bot_core.service.data_integrity_service import DataIntegrityService
@@ -23,25 +24,17 @@ class Application(DiscordBotApplicationABC):
# cpl-core
self._logger: LoggerABC = services.get_service(LoggerABC)
self._data_integrity: DataIntegrityService = services.get_service(
DataIntegrityService
)
self._data_integrity: DataIntegrityService = services.get_service(DataIntegrityService)
# cpl-discord
self._bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC)
self._bot_settings: DiscordBotSettings = config.get_configuration(
DiscordBotSettings
)
self._bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings)
# cpl-translation
self._translation: TranslationServiceABC = services.get_service(
TranslationServiceABC
)
self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC)
self._t: TranslatePipe = services.get_service(TranslatePipe)
# internal stuff
self._tasks = services.get_services(TaskABC)
self._feature_flags: FeatureFlagsSettings = config.get_configuration(
FeatureFlagsSettings
)
self._feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings)
# api
if self._feature_flags.get_flag(FeatureFlagsEnum.api_module):
@@ -50,9 +43,7 @@ class Application(DiscordBotApplicationABC):
self._is_stopping = False
async def configure(self):
self._translation.load_by_settings(
self._configuration.get_configuration(TranslationSettings)
)
self._translation.load_by_settings(self._configuration.get_configuration(TranslationSettings))
async def main(self):
try:
@@ -68,8 +59,9 @@ class Application(DiscordBotApplicationABC):
return
self._logger.info(__name__, f"Try to start {DiscordBotService.__name__}")
for task in self._tasks:
await self._bot.add_cog(task)
if not self._config.get_configuration(MAINTENANCE):
for task in self._tasks:
await self._bot.add_cog(task)
await self._bot.start_async()
await self._bot.stop_async()
@@ -95,8 +87,4 @@ class Application(DiscordBotApplicationABC):
Console.write_line()
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

0
bot/src/bot/bot Normal file → Executable file
View File

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "2",
"Micro": "0"
"Micro": "2"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
@@ -31,11 +31,14 @@
"icmplib==3.0.4",
"ariadne==0.20.1",
"cryptography==41.0.4",
"discord==2.3.2"
"discord==2.3.2",
"bs4==0.0.1",
"lxml==4.9.3"
],
"DevDependencies": [
"cpl-cli==2023.4.0.post3",
"pygount==1.6.1"
"pygount==1.6.1",
"black==23.10.1"
],
"PythonVersion": ">=3.10.4",
"PythonPath": {},

1
bot/src/bot/config Submodule

Submodule bot/src/bot/config added at c11ca6f2e8

View File

@@ -15,7 +15,7 @@ __title__ = "bot.extension"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -0,0 +1,22 @@
import os
import shutil
from datetime import datetime
from cpl_core.application.application_extension_abc import ApplicationExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_query.extension import List
class CleanLogsExtension(ApplicationExtensionABC):
def __init__(self):
pass
async def run(self, config: ConfigurationABC, services: ServiceProviderABC):
(
List(str, os.listdir("logs/"))
.where(lambda x: os.path.isdir(f"logs/{x}"))
.order_by()
.where(lambda x: (datetime.now() - datetime.strptime(x, "%Y-%m-%d")).days >= 7)
.for_each(lambda x: shutil.rmtree(f"logs/{x}"))
)

View File

@@ -13,6 +13,4 @@ class InitBotExtension(ApplicationExtensionABC):
async def run(self, config: ConfigurationABC, services: ServiceProviderABC):
settings = config.get_configuration(TechnicianConfig)
bot: DiscordBotServiceABC = services.get_service(
DiscordBotServiceABC, max_messages=settings.cache_max_messages
)
bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC, max_messages=settings.cache_max_messages)

View File

@@ -6,10 +6,11 @@ from cpl_core.application import ApplicationBuilder
from cpl_core.console import Console
from bot.application import Application
from bot.extension.clean_logs_extension import CleanLogsExtension
from bot.extension.init_bot_extension import InitBotExtension
from bot.startup import Startup
from bot.startup_discord_extension import StartupDiscordExtension
from bot.startup_migration_extension import StartupMigrationExtension
from bot_data.startup_migration_extension import StartupMigrationExtension
from bot.startup_module_extension import StartupModuleExtension
from bot.startup_settings_extension import StartupSettingsExtension
from bot_api.app_api_extension import AppApiExtension
@@ -31,6 +32,7 @@ class Program:
.use_extension(StartupDiscordExtension)
.use_extension(StartupModuleExtension)
.use_extension(StartupMigrationExtension)
.use_extension(CleanLogsExtension)
.use_extension(DatabaseExtension)
.use_extension(ConfigExtension)
.use_extension(InitBotExtension)

View File

@@ -50,9 +50,7 @@ class Startup(StartupABC):
services.add_singleton(CustomFileLoggerABC, ApiLogger)
services.add_translation()
services.add_db_context(
DBContext, self._config.get_configuration(DatabaseSettings)
)
services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings))
provider = services.build_service_provider()
# instantiate custom logger

View File

@@ -9,13 +9,9 @@ class StartupDiscordExtension(StartupExtensionABC):
def __init__(self):
pass
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_discord()
dcc = get_discord_collection(services)

View File

@@ -1,106 +0,0 @@
from cpl_core.application import StartupExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from bot_data.abc.migration_abc import MigrationABC
from bot_data.migration.achievements_migration import AchievementsMigration
from bot_data.migration.api_key_migration import ApiKeyMigration
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.birthday_migration import BirthdayMigration
from bot_data.migration.config_feature_flags_migration import (
ConfigFeatureFlagsMigration,
)
from bot_data.migration.config_migration import ConfigMigration
from bot_data.migration.db_history_migration import DBHistoryMigration
from bot_data.migration.default_role_migration import DefaultRoleMigration
from bot_data.migration.fix_updates_migration import FixUpdatesMigration
from bot_data.migration.fix_user_history_migration import FixUserHistoryMigration
from bot_data.migration.initial_migration import InitialMigration
from bot_data.migration.level_migration import LevelMigration
from bot_data.migration.remove_stats_migration import RemoveStatsMigration
from bot_data.migration.short_role_name_migration import ShortRoleNameMigration
from bot_data.migration.short_role_name_only_highest_migration import (
ShortRoleNameOnlyHighestMigration,
)
from bot_data.migration.stats_migration import StatsMigration
from bot_data.migration.steam_special_offer_migration import SteamSpecialOfferMigration
from bot_data.migration.user_joined_game_server_migration import (
UserJoinedGameServerMigration,
)
from bot_data.migration.user_message_count_per_hour_migration import (
UserMessageCountPerHourMigration,
)
from bot_data.migration.user_warning_migration import UserWarningMigration
from bot_data.service.migration_service import MigrationService
class StartupMigrationExtension(StartupExtensionABC):
def __init__(self):
pass
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
pass
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
services.add_transient(MigrationService)
services.add_transient(MigrationABC, InitialMigration)
services.add_transient(
MigrationABC, AutoRoleMigration
) # 03.10.2022 #54 - 0.2.2
services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0
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, AutoRoleFix1Migration
) # 30.12.2022 #151 - 0.3.0
services.add_transient(
MigrationABC, UserMessageCountPerHourMigration
) # 11.01.2023 #168 - 0.3.1
services.add_transient(MigrationABC, ApiKeyMigration) # 09.02.2023 #162 - 1.0.0
services.add_transient(
MigrationABC, UserJoinedGameServerMigration
) # 12.02.2023 #181 - 1.0.0
services.add_transient(
MigrationABC, RemoveStatsMigration
) # 19.02.2023 #190 - 1.0.0
services.add_transient(
MigrationABC, UserWarningMigration
) # 21.02.2023 #35 - 1.0.0
services.add_transient(
MigrationABC, DBHistoryMigration
) # 06.03.2023 #246 - 1.0.0
services.add_transient(
MigrationABC, AchievementsMigration
) # 14.06.2023 #268 - 1.1.0
services.add_transient(MigrationABC, ConfigMigration) # 19.07.2023 #127 - 1.1.0
services.add_transient(
MigrationABC, ConfigFeatureFlagsMigration
) # 15.08.2023 #334 - 1.1.0
services.add_transient(
MigrationABC, DefaultRoleMigration
) # 24.09.2023 #360 - 1.1.3
services.add_transient(
MigrationABC, ShortRoleNameMigration
) # 28.09.2023 #378 - 1.1.7
services.add_transient(
MigrationABC, FixUpdatesMigration
) # 28.09.2023 #378 - 1.1.7
services.add_transient(
MigrationABC, ShortRoleNameOnlyHighestMigration
) # 02.10.2023 #391 - 1.1.9
services.add_transient(
MigrationABC, FixUserHistoryMigration
) # 10.10.2023 #401 - 1.2.0
services.add_transient(
MigrationABC, BirthdayMigration
) # 10.10.2023 #401 - 1.2.0
services.add_transient(
MigrationABC, SteamSpecialOfferMigration
) # 10.10.2023 #188 - 1.2.0

View File

@@ -18,15 +18,11 @@ class StartupModuleExtension(StartupExtensionABC):
self._modules = ModuleList.get_modules()
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
self._config = config
self._feature_flags = config.get_configuration(FeatureFlagsSettings)
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
provider = services.build_service_provider()
dc_collection: DiscordCollectionABC = provider.get_service(DiscordCollectionABC)

View File

@@ -8,44 +8,39 @@ from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from bot_core.configuration.bot_logging_settings import BotLoggingSettings
from bot_core.environment_variables import MAINTENANCE, MIGRATION_ONLY
class StartupSettingsExtension(StartupExtensionABC):
def __init__(self):
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
environment.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
configuration.add_environment_variables("KDB_")
configuration.add_environment_variables("SDB_")
configuration.add_environment_variables("DISCORD_")
configuration.add_configuration(
MAINTENANCE, configuration.get_configuration(MAINTENANCE) in [True, "true", "True"]
)
configuration.add_configuration(
MIGRATION_ONLY, configuration.get_configuration(MIGRATION_ONLY) in [True, "true", "True"]
)
configuration.add_json_file(f"config/appsettings.json", optional=False)
configuration.add_json_file(
f"config/appsettings.{environment.environment_name}.json", optional=True
)
configuration.add_json_file(
f"config/appsettings.{environment.host_name}.json", optional=True
)
configuration.add_json_file(f"config/appsettings.{environment.environment_name}.json", optional=True)
configuration.add_json_file(f"config/appsettings.{environment.host_name}.json", optional=True)
# load feature-flags
configuration.add_json_file(f"config/feature-flags.json", optional=False)
configuration.add_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_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))
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
@staticmethod
@@ -57,6 +52,4 @@ class StartupSettingsExtension(StartupExtensionABC):
return
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

@@ -90,7 +90,8 @@
"booting": "Ich fahre gerade hoch...",
"restart": "Muss neue Kekse holen...",
"running": "Ich esse Kekse :D",
"shutdown": "Ich werde bestimmt wieder kommen..."
"shutdown": "Ich werde bestimmt wieder kommen...",
"maintenance": "In Wartung!"
}
},
"modules": {

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.abc"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -40,15 +40,11 @@ class AuthServiceABC(ABC):
pass
@abstractmethod
async def get_filtered_auth_users_async(
self, criteria: AuthUserSelectCriteria
) -> AuthUserFilteredResultDTO:
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO:
pass
@abstractmethod
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:
pass
@abstractmethod

View File

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

View File

@@ -57,9 +57,7 @@ class Api(Flask):
# Added async_mode see link below
# https://github.com/miguelgrinberg/Flask-SocketIO/discussions/1849
# https://stackoverflow.com/questions/39370848/flask-socket-io-sometimes-client-calls-freeze-the-server
self._socketio = SocketIO(
self, cors_allowed_origins="*", path="/api/socket.io", async_mode="eventlet"
)
self._socketio = SocketIO(self, cors_allowed_origins="*", path="/api/socket.io", async_mode="eventlet")
self._socketio.on_event("connect", self.on_connect)
self._socketio.on_event("disconnect", self.on_disconnect)
@@ -145,9 +143,7 @@ class Api(Flask):
data = request.get_data()
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)
return response
@@ -162,9 +158,7 @@ class Api(Flask):
# from waitress import serve
# https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html
# serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10)
self._socket = eventlet.listen(
(self._api_settings.host, self._api_settings.port)
)
self._socket = eventlet.listen((self._api_settings.host, self._api_settings.port))
wsgi.server(self._socket, self, log_output=False)
def stop(self):

View File

@@ -26,21 +26,15 @@ class ApiModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
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
env.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
config.add_json_file(f"config/apisettings.json", optional=False)
config.add_json_file(
f"config/apisettings.{env.environment_name}.json", optional=True
)
config.add_json_file(f"config/apisettings.{env.environment_name}.json", optional=True)
config.add_json_file(f"config/apisettings.{env.host_name}.json", optional=True)
env.set_working_directory(cwd)
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_singleton(EMailClientABC, EMailClient)
services.add_singleton(ApiThread)

View File

@@ -12,9 +12,7 @@ class AppApiExtension(ApplicationExtensionABC):
ApplicationExtensionABC.__init__(self)
async def run(self, config: ConfigurationABC, services: ServiceProviderABC):
feature_flags: FeatureFlagsSettings = config.get_configuration(
FeatureFlagsSettings
)
feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings)
if not feature_flags.get_flag(FeatureFlagsEnum.api_module):
return

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "2",
"Micro": "0"
"Micro": "2"
},
"Author": "",
"AuthorEmail": "",

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.configuration"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -16,9 +16,7 @@ class AuthenticationSettings(ConfigurationModelABC):
self._issuer = "" if issuer is None else issuer
self._audience = "" if audience is None else audience
self._token_expire_time = 0 if token_expire_time is None else token_expire_time
self._refresh_token_expire_time = (
0 if refresh_token_expire_time is None else refresh_token_expire_time
)
self._refresh_token_expire_time = 0 if refresh_token_expire_time is None else refresh_token_expire_time
@property
def secret_key(self) -> str:

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.controller"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -14,7 +14,10 @@ from bot_api.model.reset_password_dto import ResetPasswordDTO
from bot_api.model.token_dto import TokenDTO
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
from bot_api.route.route import Route
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_data.model.auth_role_enum import AuthRoleEnum
from bot_data.model.technician_config import TechnicianConfig
class AuthController:
@@ -30,6 +33,7 @@ class AuthController:
mail_settings: EMailClientSettings,
mailer: EMailClientABC,
auth_service: AuthServiceABC,
technician_config: TechnicianConfig,
):
self._config = config
self._env = env
@@ -39,6 +43,7 @@ class AuthController:
self._mail_settings = mail_settings
self._mailer = mailer
self._auth_service = auth_service
self._technician_config = technician_config
@Route.get(f"{BasePath}/users")
@Route.authorize(role=AuthRoleEnum.admin)
@@ -70,22 +75,33 @@ class AuthController:
@Route.post(f"{BasePath}/register")
async def register(self):
dto: AuthUserDTO = JSONProcessor.process(
AuthUserDTO, request.get_json(force=True, silent=True)
)
if not FeatureFlagsSettings.get_flag_from_dict(
self._technician_config.feature_flags, FeatureFlagsEnum.basic_registration
):
return
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
self._auth_service.add_auth_user(dto)
return "", 200
@Route.post(f"{BasePath}/register-by-id/<id>")
async def register_id(self, id: str):
if not FeatureFlagsSettings.get_flag_from_dict(
self._technician_config.feature_flags, FeatureFlagsEnum.basic_registration
):
return
result = await self._auth_service.confirm_email_async(id)
return jsonify(result)
@Route.post(f"{BasePath}/login")
async def login(self) -> Response:
dto: AuthUserDTO = JSONProcessor.process(
AuthUserDTO, request.get_json(force=True, silent=True)
)
if not FeatureFlagsSettings.get_flag_from_dict(
self._technician_config.feature_flags, FeatureFlagsEnum.basic_login
):
return jsonify({})
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
result = await self._auth_service.login_async(dto)
return jsonify(result.to_dict())
@@ -104,6 +120,11 @@ class AuthController:
@Route.post(f"{BasePath}/forgot-password/<email>")
async def forgot_password(self, email: str):
if not FeatureFlagsSettings.get_flag_from_dict(
self._technician_config.feature_flags, FeatureFlagsEnum.basic_login
):
return "", 409
await self._auth_service.forgot_password_async(email)
return "", 200
@@ -114,52 +135,45 @@ class AuthController:
@Route.post(f"{BasePath}/reset-password")
async def reset_password(self):
dto: ResetPasswordDTO = JSONProcessor.process(
ResetPasswordDTO, request.get_json(force=True, silent=True)
)
if not FeatureFlagsSettings.get_flag_from_dict(
self._technician_config.feature_flags, FeatureFlagsEnum.basic_login
):
return "", 409
dto: ResetPasswordDTO = JSONProcessor.process(ResetPasswordDTO, request.get_json(force=True, silent=True))
await self._auth_service.reset_password_async(dto)
return "", 200
@Route.post(f"{BasePath}/update-user")
@Route.authorize
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)
return "", 200
@Route.post(f"{BasePath}/update-user-as-admin")
@Route.authorize(role=AuthRoleEnum.admin)
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)
return "", 200
@Route.post(f"{BasePath}/refresh")
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)
return jsonify(result.to_dict())
@Route.post(f"{BasePath}/revoke")
async def revoke(self):
dto: TokenDTO = JSONProcessor.process(
TokenDTO, request.get_json(force=True, silent=True)
)
dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True))
await self._auth_service.revoke_async(dto)
return "", 200
@Route.post(f"{BasePath}/delete-user")
@Route.authorize(role=AuthRoleEnum.admin)
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)
return "", 200

View File

@@ -12,6 +12,9 @@ from bot_api.logging.api_logger import ApiLogger
from bot_api.model.settings_dto import SettingsDTO
from bot_api.model.version_dto import VersionDTO
from bot_api.route.route import Route
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_data.model.technician_config import TechnicianConfig
class GuiController:
@@ -82,3 +85,11 @@ class GuiController:
)
self._mailer.send_mail(mail)
return "", 200
@Route.get(f"{BasePath}/has-feature-flag/<flag>")
async def has_feature_flag(self, flag: str):
settings: TechnicianConfig = self._config.get_configuration(TechnicianConfig)
return {
"key": flag,
"value": FeatureFlagsSettings.get_flag_from_dict(settings.feature_flags, FeatureFlagsEnum(flag)),
}

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.event"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.exception"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.filter"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -13,9 +13,7 @@ class AuthUserSelectCriteria(SelectCriteriaABC):
email: str,
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)
self.first_name = first_name
self.last_name = last_name

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.filter.discord"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -10,8 +10,6 @@ class ServerSelectCriteria(SelectCriteriaABC):
sort_column: 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)
self.name = name

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.logging"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.model"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.model.discord"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -11,9 +11,7 @@ class ErrorDTO(DtoABC):
def __init__(self, error_code: Optional[ServiceErrorCode], message: str):
DtoABC.__init__(self)
self._error_code = (
ServiceErrorCode.Unknown if error_code is None else error_code
)
self._error_code = ServiceErrorCode.Unknown if error_code is None else error_code
self._message = message
@property

View File

@@ -34,9 +34,7 @@ class UpdateAuthUserDTO(DtoABC):
def from_dict(self, values: dict):
self._auth_user = AuthUserDTO().from_dict(values["authUser"])
self._new_auth_user = AuthUserDTO().from_dict(values["newAuthUser"])
self._change_password = (
False if "changePassword" not in values else bool(values["changePassword"])
)
self._change_password = False if "changePassword" not in values else bool(values["changePassword"])
def to_dict(self) -> dict:
return {

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.route"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -65,9 +65,7 @@ class Route:
by_api_key=False,
):
if f is None:
return functools.partial(
cls.authorize, role=role, skip_in_dev=skip_in_dev, by_api_key=by_api_key
)
return functools.partial(cls.authorize, role=role, skip_in_dev=skip_in_dev, by_api_key=by_api_key)
@wraps(f)
async def decorator(*args, **kwargs):
@@ -78,9 +76,7 @@ class Route:
api_key = None
if "Authorization" in request.headers:
if " " not in request.headers.get("Authorization"):
ex = ServiceException(
ServiceErrorCode.Unauthorized, f"Token not set"
)
ex = ServiceException(ServiceErrorCode.Unauthorized, f"Token not set")
error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401
@@ -102,9 +98,7 @@ class Route:
return jsonify(e), 500
if not valid:
ex = ServiceException(
ServiceErrorCode.Unauthorized, f"API-Key invalid"
)
ex = ServiceException(ServiceErrorCode.Unauthorized, f"API-Key invalid")
error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401
@@ -116,9 +110,7 @@ class Route:
return jsonify(error.to_dict()), 401
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)
return jsonify(error.to_dict()), 401
@@ -140,9 +132,7 @@ class Route:
return jsonify(error.to_dict()), 401
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)
return jsonify(error.to_dict()), 403

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.service"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -90,9 +90,7 @@ class AuthService(AuthServiceABC):
def _get_api_key_str(self, api_key: ApiKey) -> str:
return hashlib.sha256(
f"{api_key.identifier}:{api_key.key}+{self._auth_settings.secret_key}".encode(
"utf-8"
)
f"{api_key.identifier}:{api_key.key}+{self._auth_settings.secret_key}".encode("utf-8")
).hexdigest()
def generate_token(self, user: AuthUser) -> str:
@@ -101,8 +99,7 @@ class AuthService(AuthServiceABC):
"user_id": user.id,
"email": user.email,
"role": user.auth_role.value,
"exp": datetime.now(tz=timezone.utc)
+ timedelta(days=self._auth_settings.token_expire_time),
"exp": datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time),
"iss": self._auth_settings.issuer,
"aud": self._auth_settings.audience,
},
@@ -158,9 +155,7 @@ class AuthService(AuthServiceABC):
def _create_and_save_refresh_token(self, user: AuthUser) -> str:
token = str(uuid.uuid4())
user.refresh_token = token
user.refresh_token_expire_time = datetime.now() + timedelta(
days=self._auth_settings.refresh_token_expire_time
)
user.refresh_token_expire_time = datetime.now() + timedelta(days=self._auth_settings.refresh_token_expire_time)
self._auth_users.update_auth_user(user)
self._db.save_changes()
return token
@@ -193,12 +188,8 @@ class AuthService(AuthServiceABC):
self._send_link_mail(
user.email,
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.subject").format(user.first_name, user.last_name),
self._t.transform("api.auth.confirmation.message").format(url, user.confirmation_id),
)
def _send_forgot_password_id_to_user(self, user: AuthUser):
@@ -208,38 +199,28 @@ class AuthService(AuthServiceABC):
self._send_link_mail(
user.email,
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.subject").format(user.first_name, user.last_name),
self._t.transform("api.auth.forgot_password.message").format(url, user.forgot_password_id),
)
async def get_all_auth_users_async(self) -> List[AuthUserDTO]:
result = self._auth_users.get_all_auth_users().select(lambda x: AUT.to_dto(x))
return List(AuthUserDTO, result)
async def get_filtered_auth_users_async(
self, criteria: AuthUserSelectCriteria
) -> AuthUserFilteredResultDTO:
async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO:
users = self._auth_users.get_filtered_auth_users(criteria)
result = users.result.select(lambda x: AUT.to_dto(x))
return AuthUserFilteredResultDTO(List(AuthUserDTO, result), users.total_count)
async def get_auth_user_by_email_async(
self, email: str, with_password: bool = False
) -> AuthUserDTO:
async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO:
try:
# todo: check if logged in user is admin then send mail
user = self._auth_users.get_auth_user_by_email(email)
return AUT.to_dto(user, password=user.password if with_password else None)
except Exception as 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]:
user = self._auth_users.find_auth_user_by_email(email)
@@ -257,22 +238,16 @@ class AuthService(AuthServiceABC):
user.password_salt = uuid.uuid4()
user.password = self._hash_sha256(user_dto.password, user.password_salt)
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:
user.confirmation_id = uuid.uuid4()
self._auth_users.add_auth_user(user)
self._send_confirmation_id_to_user(user)
self._db.save_changes()
self._logger.info(
__name__, f"Added auth user with E-Mail: {user_dto.email}"
)
self._logger.info(__name__, f"Added auth user with E-Mail: {user_dto.email}")
except Exception as e:
self._logger.error(
__name__, f"Cannot add user with E-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")
async def add_auth_user_by_oauth_async(self, dto: OAuthDTO):
@@ -288,20 +263,14 @@ class AuthService(AuthServiceABC):
db_user.first_name = dto.user.first_name
db_user.last_name = dto.user.last_name
db_user.password_salt = uuid.uuid4()
db_user.password = self._hash_sha256(
dto.user.password, db_user.password_salt
)
db_user.password = self._hash_sha256(dto.user.password, db_user.password_salt)
db_user.oauth_id = None
db_user.confirmation_id = uuid.uuid4()
self._send_confirmation_id_to_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:
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")
self._db.save_changes()
@@ -311,16 +280,14 @@ class AuthService(AuthServiceABC):
raise ServiceException(ServiceErrorCode.InvalidData, f"User is empty")
if update_user_dto.auth_user is None:
raise ServiceException(
ServiceErrorCode.InvalidData, f"Existing user is empty"
)
raise ServiceException(ServiceErrorCode.InvalidData, f"Existing user is empty")
if update_user_dto.new_auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f"New user is empty")
if not self._is_email_valid(
update_user_dto.auth_user.email
) or not self._is_email_valid(update_user_dto.new_auth_user.email):
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(
update_user_dto.new_auth_user.email
):
raise ServiceException(ServiceErrorCode.InvalidData, f"Invalid E-Mail")
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
@@ -333,8 +300,7 @@ class AuthService(AuthServiceABC):
# 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
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
@@ -342,8 +308,7 @@ class AuthService(AuthServiceABC):
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
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
@@ -353,33 +318,22 @@ class AuthService(AuthServiceABC):
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:
raise ServiceException(
ServiceErrorCode.InvalidUser, "User already exists"
)
raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
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:
raise ServiceException(ServiceErrorCode.InvalidUser, "Wrong 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
and self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password
):
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)
self._auth_users.update_auth_user(user)
self._db.save_changes()
@@ -389,31 +343,23 @@ class AuthService(AuthServiceABC):
raise ServiceException(ServiceErrorCode.InvalidData, f"User is empty")
if update_user_dto.auth_user is None:
raise ServiceException(
ServiceErrorCode.InvalidData, f"Existing user is empty"
)
raise ServiceException(ServiceErrorCode.InvalidData, f"Existing user is empty")
if update_user_dto.new_auth_user is None:
raise ServiceException(ServiceErrorCode.InvalidData, f"New user is empty")
if not self._is_email_valid(
update_user_dto.auth_user.email
) or not self._is_email_valid(update_user_dto.new_auth_user.email):
if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(
update_user_dto.new_auth_user.email
):
raise ServiceException(ServiceErrorCode.InvalidData, f"Invalid E-Mail")
user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email)
if user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, "User not found")
if (
user.confirmation_id is not None
and update_user_dto.new_auth_user.is_confirmed
):
if user.confirmation_id is not None and update_user_dto.new_auth_user.is_confirmed:
user.confirmation_id = None
elif (
user.confirmation_id is None
and not update_user_dto.new_auth_user.is_confirmed
):
elif user.confirmation_id is None and not update_user_dto.new_auth_user.is_confirmed:
user.confirmation_id = uuid.uuid4()
# else
# raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed')
@@ -421,8 +367,7 @@ class AuthService(AuthServiceABC):
# 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
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
@@ -430,8 +375,7 @@ class AuthService(AuthServiceABC):
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
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
@@ -441,28 +385,19 @@ class AuthService(AuthServiceABC):
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:
raise ServiceException(
ServiceErrorCode.InvalidUser, "User already exists"
)
raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user.email = update_user_dto.new_auth_user.email
# 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
)
and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt)
):
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
if (
@@ -481,9 +416,7 @@ class AuthService(AuthServiceABC):
self._db.save_changes()
except Exception as e:
self._logger.error(__name__, f"Cannot delete user", e)
raise ServiceException(
ServiceErrorCode.UnableToDelete, f"Cannot delete user by mail {email}"
)
raise ServiceException(ServiceErrorCode.UnableToDelete, f"Cannot delete user by mail {email}")
async def delete_auth_user_async(self, user_dto: AuthUser):
try:
@@ -567,9 +500,7 @@ class AuthService(AuthServiceABC):
if user.id in user_ids:
continue
self._auth_users.add_auth_user_user_rel(
AuthUserUsersRelation(db_user, user)
)
self._auth_users.add_auth_user_user_rel(AuthUserUsersRelation(db_user, user))
if db_user.confirmation_id is not None and not added_user:
raise ServiceException(ServiceErrorCode.Forbidden, "E-Mail not verified")
@@ -599,19 +530,13 @@ class AuthService(AuthServiceABC):
):
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:
self._logger.error(__name__, f"Refreshing token failed", e)
return TokenDTO("", "")
async def revoke_async(self, token_dto: TokenDTO):
if (
token_dto is None
or token_dto.token is None
or token_dto.refresh_token is None
):
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")
try:
@@ -664,9 +589,7 @@ class AuthService(AuthServiceABC):
)
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 == "":
raise ServiceException(ServiceErrorCode.InvalidData, f"Password not set")

View File

@@ -53,17 +53,13 @@ class DiscordService:
if role != AuthRoleEnum.admin:
auth_user = self._auth_users.find_auth_user_by_email(token["email"])
if auth_user is not None:
user_ids = auth_user.users.select(
lambda x: x.server is not None and x.server.id
)
user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.id)
servers = servers.where(lambda x: x.id in user_ids)
servers = List(ServerDTO, servers)
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()
if token is None or "email" not in token or "role" not in token:
raise ServiceException(ServiceErrorCode.InvalidData, "Token invalid")
@@ -74,22 +70,15 @@ class DiscordService:
if role != AuthRoleEnum.admin:
auth_user = self._auth_users.find_auth_user_by_email(token["email"])
if auth_user is not None:
user_ids = auth_user.users.select(
lambda x: x.server is not None and x.server.id
)
filtered_result.result = filtered_result.result.where(
lambda x: x.id in user_ids
)
user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.id)
filtered_result.result = filtered_result.result.where(lambda x: x.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)
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()
lambda x: criteria.name.lower() in x.name.lower() or x.name.lower() == criteria.name.lower()
)
return ServerFilteredResultDTO(List(ServerDTO, result), servers.count())
@@ -98,7 +87,5 @@ class DiscordService:
server = self._servers.get_server_by_id(id)
guild = self._bot.get_guild(server.discord_id)
server_dto = ServerTransformer.to_dto(
server, guild.name, guild.member_count, guild.icon
)
server_dto = ServerTransformer.to_dto(server, guild.name, guild.member_count, guild.icon)
return server_dto

View File

@@ -15,7 +15,7 @@ __title__ = "bot_api.transformer"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -27,35 +27,27 @@ class AuthUserTransformer(TransformerABC):
None,
None,
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,
)
@staticmethod
@ServiceProviderABC.inject
def _is_technician(
user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC
):
def _is_technician(user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC):
guild = bot.get_guild(user.server.discord_id)
member = guild.get_member(user.discord_id)
return permissions.is_member_technician(member)
@staticmethod
@ServiceProviderABC.inject
def _is_admin(
user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC
):
def _is_admin(user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC):
guild = bot.get_guild(user.server.discord_id)
member = guild.get_member(user.discord_id)
return permissions.is_member_admin(member)
@staticmethod
@ServiceProviderABC.inject
def _is_moderator(
user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC
):
def _is_moderator(user: User, bot: DiscordBotServiceABC, permissions: PermissionServiceABC):
guild = bot.get_guild(user.server.discord_id)
member = guild.get_member(user.discord_id)
return permissions.is_member_moderator(member)

View File

@@ -13,9 +13,7 @@ class ServerTransformer(TransformerABC):
return Server(dto.discord_id)
@staticmethod
def to_dto(
db: Server, name: str, member_count: int, icon_url: Optional[discord.Asset]
) -> ServerDTO:
def to_dto(db: Server, name: str, member_count: int, icon_url: Optional[discord.Asset]) -> ServerDTO:
return ServerDTO(
db.id,
db.discord_id,

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.abc"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -45,9 +45,7 @@ class ClientUtilsABC(ABC):
pass
@abstractmethod
def get_auto_complete_list(
self, _l: List, current: str, select: Callable = None
) -> List:
def get_auto_complete_list(self, _l: List, current: str, select: Callable = None) -> List:
pass
@abstractmethod
@@ -77,3 +75,7 @@ class ClientUtilsABC(ABC):
@abstractmethod
async def check_default_role(self, member: Union[discord.User, discord.Member]):
pass
@abstractmethod
async def set_maintenance_mode(self, state: bool):
pass

View File

@@ -18,9 +18,7 @@ class CustomFileLoggerABC(Logger, ABC):
env: ApplicationEnvironmentABC,
):
self._key = key
self._settings: LoggingSettings = config.get_configuration(
f"{FileLoggingSettings.__name__}_{key}"
)
self._settings: LoggingSettings = config.get_configuration(f"{FileLoggingSettings.__name__}_{key}")
Logger.__init__(self, self._settings, time_format, env)
self._begin_log()
@@ -34,9 +32,7 @@ class CustomFileLoggerABC(Logger, ABC):
self.info(__name__, f"Starting...")
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(" ")
log_level = level.name
string = f"<{self._get_datetime_now()}> [ {log_level} ]"

View File

@@ -13,9 +13,7 @@ class MessageServiceABC(ABC):
pass
@abstractmethod
async def delete_messages(
self, messages: List[discord.Message], guild_id: int, without_tracking=False
):
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False):
pass
@abstractmethod

View File

@@ -6,6 +6,7 @@ from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from discord.ext import commands
from bot_core.environment_variables import MAINTENANCE
from bot_core.logging.task_logger import TaskLogger
@@ -15,15 +16,17 @@ class TaskABC(commands.Cog):
commands.Cog.__init__(self)
@ServiceProviderABC.inject
async def _wait_until_ready(
self, config: ConfigurationABC, logger: TaskLogger, bot: DiscordBotServiceABC
):
logger.debug(__name__, f"Waiting before {type(self).__name__}")
def _is_maintenance(self, config: ConfigurationABC) -> bool:
return config.get_configuration(MAINTENANCE) is True
@ServiceProviderABC.inject
async def _wait_until_ready(self, config: ConfigurationABC, logger: TaskLogger, bot: DiscordBotServiceABC):
logger.debug(__name__, f"Waiting before ready {type(self).__name__}")
await bot.wait_until_ready()
async def wait():
is_ready = config.get_configuration("IS_READY")
if is_ready != "true":
is_ready = config.get_configuration("IS_READY") is True
if not is_ready:
await asyncio.sleep(1)
await wait()

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "2",
"Micro": "0"
"Micro": "2"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.configuration"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -27,3 +27,6 @@ class FeatureFlagsEnum(Enum):
short_role_name = "ShortRoleName"
technician_full_access = "TechnicianFullAccess"
steam_special_offers = "SteamSpecialOffers"
scheduled_events = "ScheduledEvents"
basic_registration = "BasicRegistration"
basic_login = "BasicLogin"

View File

@@ -29,6 +29,9 @@ class FeatureFlagsSettings(ConfigurationModelABC):
FeatureFlagsEnum.short_role_name.value: False, # 28.09.2023 #378
FeatureFlagsEnum.technician_full_access.value: False, # 03.10.2023 #393
FeatureFlagsEnum.steam_special_offers.value: False, # 11.10.2023 #188
FeatureFlagsEnum.scheduled_events.value: False, # 14.11.2023 #410
FeatureFlagsEnum.basic_registration.value: False, # 19.11.2023 #440
FeatureFlagsEnum.basic_login.value: False, # 19.11.2023 #440
}
def __init__(self, **kwargs: dict):

View File

@@ -10,9 +10,7 @@ class FileLoggingSettings(LoggingSettings):
console_log_level: LoggingLevelEnum = None,
file_log_level: LoggingLevelEnum = None,
):
LoggingSettings.__init__(
self, path, filename, console_log_level, file_log_level
)
LoggingSettings.__init__(self, path, filename, console_log_level, file_log_level)
self._key = key

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.core_extension"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -17,9 +17,7 @@ class CoreExtension(ApplicationExtensionABC):
ApplicationExtensionABC.__init__(self)
async def run(self, config: ConfigurationABC, services: ServiceProviderABC):
feature_flags: FeatureFlagsSettings = config.get_configuration(
FeatureFlagsSettings
)
feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings)
if not feature_flags.get_flag(FeatureFlagsEnum.core_module):
return

View File

@@ -15,14 +15,8 @@ class CoreExtensionModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_extension_module)
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
services.add_transient(
DiscordEventTypesEnum.on_ready.value, CoreExtensionOnReadyEvent
)
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(DiscordEventTypesEnum.on_ready.value, CoreExtensionOnReadyEvent)

View File

@@ -1,16 +1,19 @@
import asyncio
from cpl_core.configuration import ConfigurationABC
from cpl_core.logging import LoggerABC
from cpl_discord.events import OnReadyABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_core.environment_variables import MAINTENANCE
class CoreExtensionOnReadyEvent(OnReadyABC):
def __init__(
self,
config: ConfigurationABC,
logger: LoggerABC,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsABC,
@@ -18,6 +21,7 @@ class CoreExtensionOnReadyEvent(OnReadyABC):
):
OnReadyABC.__init__(self)
self._config = config
self._logger = logger
self._bot = bot
self._client_utils = client_utils
@@ -27,5 +31,5 @@ class CoreExtensionOnReadyEvent(OnReadyABC):
async def on_ready(self):
self._logger.debug(__name__, f"Module {type(self)} started")
await self._client_utils.presence_game("common.presence.running")
await self._client_utils.set_maintenance_mode(self._config.get_configuration(MAINTENANCE))
self._logger.trace(__name__, f"Module {type(self)} stopped")

View File

@@ -20,14 +20,10 @@ class CoreModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.core_module)
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_transient(ConfigService)
services.add_transient(MessageServiceABC, MessageService)
services.add_transient(ClientUtilsABC, ClientUtilsService)

View File

@@ -0,0 +1,2 @@
MIGRATION_ONLY = "MIGRATION_ONLY"
MAINTENANCE = "MAINTENANCE"

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.events"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.exception"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.helper"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -44,9 +44,7 @@ class CommandChecks:
async def check_is_member_admin(ctx: Context):
has_permission = cls._permissions.is_member_admin(ctx.author)
if not has_permission:
await cls._message_service.send_ctx_msg(
ctx, cls._t.transform("common.no_permission_message")
)
await cls._message_service.send_ctx_msg(ctx, cls._t.transform("common.no_permission_message"))
raise CheckError(f"Member {ctx.author.name} is not admin")
return has_permission
@@ -58,9 +56,7 @@ class CommandChecks:
async def check_is_member_technician(ctx: Context):
has_permission = cls._permissions.is_member_technician(ctx.author)
if not has_permission:
await cls._message_service.send_ctx_msg(
ctx, cls._t.transform("common.no_permission_message")
)
await cls._message_service.send_ctx_msg(ctx, cls._t.transform("common.no_permission_message"))
raise CheckError(f"Member {ctx.author.name} is not technician")
return has_permission
@@ -72,9 +68,7 @@ class CommandChecks:
async def check_is_member_moderator(ctx: Context):
has_permission = cls._permissions.is_member_moderator(ctx.author)
if not has_permission:
await cls._message_service.send_ctx_msg(
ctx, cls._t.transform("common.no_permission_message")
)
await cls._message_service.send_ctx_msg(ctx, cls._t.transform("common.no_permission_message"))
raise CheckError(f"Member {ctx.author.name} is not moderator")
return has_permission

View File

@@ -1,3 +1,4 @@
import inspect
from typing import Optional
from discord.ext import commands
@@ -17,11 +18,18 @@ class EventChecks:
cls._client_utils = client_utils
@classmethod
def check_is_ready(cls):
async def check_if_bot_is_ready() -> bool:
def check_is_ready(cls, func):
async def check_if_bot_is_ready(*args, **kwargs):
result = await cls._client_utils.check_if_bot_is_ready_yet()
if not result:
raise CheckError(f"Bot is not ready")
return result
return commands.check(check_if_bot_is_ready)
def empty(*args, **kwargs):
return
return empty
return await func(*args, **kwargs)
check_if_bot_is_ready.__name__ = func.__name__
sig = inspect.signature(func)
check_if_bot_is_ready.__signature__ = sig.replace(parameters=tuple(sig.parameters.values())[1:])
return check_if_bot_is_ready

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.logging"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.pipes"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_core.service"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -16,6 +16,7 @@ from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_core.abc.message_service_abc import MessageServiceABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_core.environment_variables import MAINTENANCE
from bot_data.abc.client_repository_abc import ClientRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import (
@@ -62,40 +63,39 @@ class ClientUtilsService(ClientUtilsABC):
def received_command(self, guild_id: int):
server = self._servers.get_server_by_discord_id(guild_id)
client = self._clients.find_client_by_discord_id_and_server_id(
self._bot.user.id, server.id
)
client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.id)
client.received_command_count += 1
self._clients.update_client(client)
self._db.save_changes()
def moved_user(self, guild_id: int):
server = self._servers.get_server_by_discord_id(guild_id)
client = self._clients.find_client_by_discord_id_and_server_id(
self._bot.user.id, server.id
)
client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.id)
client.moved_users_count += 1
self._clients.update_client(client)
self._db.save_changes()
def moved_users(self, guild_id: int, count: int):
server = self._servers.get_server_by_discord_id(guild_id)
client = self._clients.find_client_by_discord_id_and_server_id(
self._bot.user.id, server.id
)
client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.id)
client.moved_users_count += count
self._clients.update_client(client)
self._db.save_changes()
def get_client(self, dc_ic: int, guild_id: int):
server = self._servers.get_server_by_discord_id(guild_id)
client = self._clients.find_client_by_discord_id_and_server_id(
self._bot.user.id, server.id
)
client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.id)
return client
async def check_if_bot_is_ready_yet(self) -> bool:
if self._config.get_configuration("IS_READY") == "true":
if self._config.get_configuration(MAINTENANCE):
self._logger.warn(
__name__,
f"Bot is in maintenance mode",
)
return False
if self._config.get_configuration("IS_READY") is True:
return True
self._logger.debug(
@@ -129,9 +129,7 @@ class ClientUtilsService(ClientUtilsABC):
await self._bot.change_presence(activity=discord.Game(name=name))
self._logger.info(__name__, f"Set presence {name}")
def get_auto_complete_list(
self, _l: List, current: str, select: Callable = None
) -> List:
def get_auto_complete_list(self, _l: List, current: str, select: Callable = None) -> List:
if current != "":
if select is None:
select = lambda x: x
@@ -154,9 +152,7 @@ class ClientUtilsService(ClientUtilsABC):
) -> bool:
umcph = None
try:
umcph = self._umcphs.find_user_message_count_per_hour_by_user_id_and_date(
user.id, created_at
)
umcph = self._umcphs.find_user_message_count_per_hour_by_user_id_and_date(user.id, created_at)
if umcph is None:
self._umcphs.add_user_message_count_per_hour(
UserMessageCountPerHour(
@@ -169,11 +165,7 @@ class ClientUtilsService(ClientUtilsABC):
self._db.save_changes()
umcph = (
self._umcphs.get_user_message_count_per_hour_by_user_id_and_date(
user.id, created_at
)
)
umcph = self._umcphs.get_user_message_count_per_hour_by_user_id_and_date(user.id, created_at)
except Exception as e:
self._logger.error(
__name__,
@@ -205,9 +197,7 @@ class ClientUtilsService(ClientUtilsABC):
def get_ontime_for_user(self, user: User) -> float:
return round(
self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(
user.id
)
self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.id)
.where(lambda x: x.leaved_on is not None and x.joined_on is not None)
.sum(lambda join: (join.leaved_on - join.joined_on).total_seconds() / 3600),
2,
@@ -224,11 +214,7 @@ class ClientUtilsService(ClientUtilsABC):
guild: Guild = self._bot.guilds.where(lambda g: g == guild).single()
channel = guild.get_channel(discord_channel_id)
message = await channel.fetch_message(discord_message_id)
emoji = (
List(discord.Emoji, guild.emojis)
.where(lambda x: x.name == rule.emoji_name)
.single()
)
emoji = List(discord.Emoji, guild.emojis).where(lambda x: x.name == rule.emoji_name).single()
if emoji is None:
self._logger.debug(__name__, f"Emoji {rule.emoji_name} not found")
@@ -248,9 +234,7 @@ class ClientUtilsService(ClientUtilsABC):
async def check_default_role(self, member: Union[discord.User, discord.Member]):
try:
server = self._servers.get_server_by_discord_id(member.guild.id)
settings: ServerConfig = self._config.get_configuration(
f"ServerConfig_{server.discord_id}"
)
settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}")
if settings.default_role_id is None:
return
@@ -262,6 +246,11 @@ class ClientUtilsService(ClientUtilsABC):
await member.add_roles(default_role)
except Exception as e:
self._logger.error(
__name__, f"Cannot check for default role for member {member.id}", e
)
self._logger.error(__name__, f"Cannot check for default role for member {member.id}", e)
async def set_maintenance_mode(self, state: bool):
self._config.add_configuration(MAINTENANCE, state)
if state:
await self.presence_game("common.presence.maintenance")
else:
await self.presence_game("common.presence.running")

View File

@@ -7,6 +7,7 @@ from bot_data.abc.technician_config_repository_abc import TechnicianConfigReposi
from bot_data.model.server import Server
from bot_data.model.technician_config import TechnicianConfig
from bot_data.service.server_config_seeder import ServerConfigSeeder
from bot_data.service.technician_config_seeder import TechnicianConfigSeeder
class ConfigService:
@@ -16,17 +17,24 @@ class ConfigService:
services: ServiceProviderABC,
technician_config_repo: TechnicianConfigRepositoryABC,
server_config_repo: ServerConfigRepositoryABC,
technician_seeder: TechnicianConfigSeeder,
server_seeder: ServerConfigSeeder,
):
self._config = config
self._services = services
self._technician_config_repo = technician_config_repo
self._technician_seeder = technician_seeder
self._server_config_repo = server_config_repo
self._server_seeder = server_seeder
def reload_technician_config(self):
technician_config = self._technician_config_repo.get_technician_config()
async def reload_technician_config(self):
try:
technician_config = self._technician_config_repo.get_technician_config()
except Exception as e:
await self._technician_seeder.seed()
technician_config = self._technician_config_repo.get_technician_config()
self._config.add_configuration(TechnicianConfig, technician_config)
self._config.add_configuration(
FeatureFlagsSettings,

View File

@@ -67,16 +67,12 @@ class DataIntegrityService:
self._is_for_shutdown = False
def _check_known_users(self):
self._logger.debug(
__name__, f"Start checking KnownUsers table, {len(self._bot.users)}"
)
self._logger.debug(__name__, f"Start checking KnownUsers table, {len(self._bot.users)}")
for u in self._bot.users:
u: discord.User = u
try:
if u.bot:
self._logger.trace(
__name__, f"User {u.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot")
continue
user = self._known_users.find_user_by_discord_id(u.id)
@@ -96,7 +92,7 @@ class DataIntegrityService:
except Exception as e:
self._logger.error(__name__, f"Cannot get user", e)
def _check_servers(self):
def check_servers(self):
self._logger.debug(__name__, f"Start checking Servers table")
for g in self._bot.guilds:
g: discord.Guild = g
@@ -129,9 +125,7 @@ class DataIntegrityService:
try:
server: Server = self._servers.find_server_by_discord_id(g.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {g.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {g.id}")
client = self._clients.find_client_by_server_id(server.id)
if client is not None:
@@ -142,9 +136,7 @@ class DataIntegrityService:
f"Client for server {g.id} not found in database: {self._bot.user.id}",
)
self._logger.debug(__name__, f"Add client: {self._bot.user.id}")
self._clients.add_client(
Client(self._bot.user.id, 0, 0, 0, 0, 0, server)
)
self._clients.add_client(Client(self._bot.user.id, 0, 0, 0, 0, 0, server))
self._db_context.save_changes()
client = self._clients.find_client_by_server_id(server.id)
@@ -170,27 +162,21 @@ class DataIntegrityService:
try:
server = self._servers.find_server_by_discord_id(g.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {g.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {g.id}")
for u in g.members:
u: Union[discord.Member, discord.User] = u
if u.bot:
self._logger.trace(
__name__, f"User {u.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot")
continue
user = self._users.find_user_by_discord_id_and_server_id(
u.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id)
if user is not None:
continue
self._logger.warn(__name__, f"User not found in database: {u.id}")
self._logger.debug(__name__, f"Add user: {u.id}")
self._users.add_user(User(u.id, 0, 0, 0, server))
self._users.add_user(User(u.id, 0, 0, 0, None, server))
self._db_context.save_changes()
self._logger.debug(__name__, f"Added User: {u.id}")
@@ -208,30 +194,20 @@ class DataIntegrityService:
server = self._servers.find_server_by_discord_id(guild.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {guild.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {guild.id}")
try:
for u in guild.members:
u: discord.User = u
if u.bot:
self._logger.trace(
__name__, f"User {u.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {u.id} is ignored, because its a bot")
continue
user = self._users.find_user_by_discord_id_and_server_id(
u.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(u.id, server.id)
if user is None:
self._logger.fatal(
__name__, f"User not found in database: {u.id}"
)
self._logger.fatal(__name__, f"User not found in database: {u.id}")
join = self._user_joins.find_active_user_joined_server_by_user_id(
user.id
)
join = self._user_joins.find_active_user_joined_server_by_user_id(user.id)
if join is not None:
continue
@@ -281,36 +257,24 @@ class DataIntegrityService:
self._logger.debug(__name__, f"Start checking UserJoinedVoiceChannel table")
for guild in self._bot.guilds:
guild: discord.Guild = guild
settings: ServerConfig = self._config.get_configuration(
f"ServerConfig_{guild.id}"
)
settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}")
server = self._servers.find_server_by_discord_id(guild.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {guild.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {guild.id}")
try:
# close open voice states
for member in guild.members:
if member.bot:
self._logger.trace(
__name__, f"User {member.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot")
continue
user = self._users.find_user_by_discord_id_and_server_id(
member.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id)
if user is None:
self._logger.fatal(
__name__, f"User not found in database: {member.id}"
)
self._logger.fatal(__name__, f"User not found in database: {member.id}")
joins = self._user_joins_vc.find_active_user_joined_voice_channels_by_user_id(
user.id
)
joins = self._user_joins_vc.find_active_user_joined_voice_channels_by_user_id(user.id)
if joins is None or len(joins) == 0:
continue
@@ -324,9 +288,7 @@ class DataIntegrityService:
if (
(join.leaved_on - join.joined_on).total_seconds() / 60 / 60
) > settings.max_voice_state_hours:
join.leaved_on = join.joined_on + timedelta(
hours=settings.max_voice_state_hours
)
join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours)
self._user_joins_vc.update_user_joined_voice_channel(join)
@@ -341,28 +303,17 @@ class DataIntegrityService:
# add open voice states
for member in guild.members:
if member.bot:
self._logger.trace(
__name__, f"User {member.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot")
continue
if (
member.voice is None
or member.voice.channel.id in settings.afk_channel_ids
):
if member.voice is None or member.voice.channel.id in settings.afk_channel_ids:
continue
user = self._users.find_user_by_discord_id_and_server_id(
member.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id)
if user is None:
self._logger.fatal(
__name__, f"User not found in database: {member.id}"
)
self._logger.fatal(__name__, f"User not found in database: {member.id}")
join = UserJoinedVoiceChannel(
user, member.voice.channel.id, datetime.now()
)
join = UserJoinedVoiceChannel(user, member.voice.channel.id, datetime.now())
self._user_joins_vc.add_user_joined_voice_channel(join)
self._db_context.save_changes()
@@ -376,29 +327,19 @@ class DataIntegrityService:
server = self._servers.find_server_by_discord_id(guild.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {guild.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {guild.id}")
try:
for member in guild.members:
if member.bot:
self._logger.trace(
__name__, f"User {member.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot")
continue
user = self._users.find_user_by_discord_id_and_server_id(
member.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id)
if user is None:
self._logger.fatal(
__name__, f"User not found in database: {member.id}"
)
self._logger.fatal(__name__, f"User not found in database: {member.id}")
joins = self._user_joined_gs.find_active_user_joined_game_servers_by_user_id(
user.id
)
joins = self._user_joined_gs.find_active_user_joined_game_servers_by_user_id(user.id)
if joins is None or len(joins) == 0:
continue
@@ -408,16 +349,12 @@ class DataIntegrityService:
f"Active UserJoinedGameServer found in database: {guild.id}:{member.id}@{join.joined_on}",
)
join.leaved_on = datetime.now()
settings: ServerConfig = self._config.get_configuration(
f"ServerConfig_{guild.id}"
)
settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild.id}")
if (
(join.leaved_on - join.joined_on).total_seconds() / 60 / 60
) > settings.max_voice_state_hours:
join.leaved_on = join.joined_on + timedelta(
hours=settings.max_voice_state_hours
)
join.leaved_on = join.joined_on + timedelta(hours=settings.max_voice_state_hours)
self._user_joined_gs.update_user_joined_game_server(join)
if self._is_for_shutdown:
@@ -434,24 +371,16 @@ class DataIntegrityService:
for guild in self._bot.guilds:
server = self._servers.find_server_by_discord_id(guild.id)
if server is None:
self._logger.fatal(
__name__, f"Server not found in database: {guild.id}"
)
self._logger.fatal(__name__, f"Server not found in database: {guild.id}")
for member in guild.members:
if member.bot:
self._logger.trace(
__name__, f"User {member.id} is ignored, because its a bot"
)
self._logger.trace(__name__, f"User {member.id} is ignored, because its a bot")
continue
user = self._users.find_user_by_discord_id_and_server_id(
member.id, server.id
)
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id)
if user is None:
self._logger.fatal(
__name__, f"User not found in database: {member.id}"
)
self._logger.fatal(__name__, f"User not found in database: {member.id}")
await self._achievements.validate_achievements_for_user(user)
@@ -460,16 +389,33 @@ class DataIntegrityService:
for member in guild.members:
await self._client_utils.check_default_role(member)
def _check_for_bots(self):
for guild in self._bot.guilds:
server = self._servers.get_server_by_discord_id(guild.id)
for member in guild.members.where(lambda x: x.bot):
user = self._users.find_user_by_discord_id_and_server_id(member.id, server.id)
if user is None:
continue
for join in self._user_joins.get_user_joined_servers_by_user_id(user.id):
self._user_joins.delete_user_joined_server(join)
self._user_joins_vc.delete_user_joined_voice_channel_by_user_id(user.id)
self._users.delete_user(user)
self._db_context.save_changes()
async def check_data_integrity(self, is_for_shutdown=False):
if is_for_shutdown != self._is_for_shutdown:
self._is_for_shutdown = is_for_shutdown
await self._check_default_role()
self._check_known_users()
self._check_servers()
self.check_servers()
self._check_clients()
self._check_users()
self._check_user_joins()
self._check_user_joins_vc()
self._check_user_joined_gs()
await self._check_for_user_achievements()
self._check_for_bots()

View File

@@ -31,23 +31,15 @@ class MessageService(MessageServiceABC):
self._clients = clients
self._db = db
async def delete_messages(
self, messages: List[discord.Message], guild_id: int, without_tracking=False
):
async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False):
self._logger.debug(__name__, f"Try to delete {messages.count()} messages")
server_st: ServerConfig = self._config.get_configuration(
f"ServerConfig_{guild_id}"
)
server_st: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild_id}")
await asyncio.sleep(server_st.message_delete_timer)
for message in messages:
await self.delete_message(
message, mass_delete=True, without_tracking=without_tracking
)
await self.delete_message(message, mass_delete=True, without_tracking=without_tracking)
self._logger.debug(__name__, "Deleting messages finished")
async def delete_message(
self, message: discord.Message, mass_delete=False, without_tracking=False
):
async def delete_message(self, message: discord.Message, mass_delete=False, without_tracking=False):
guild_id = (
message.guild.id
if message.guild is not None
@@ -58,9 +50,7 @@ class MessageService(MessageServiceABC):
else None
)
server_st: ServerConfig = self._config.get_configuration(
f"ServerConfig_{guild_id}"
)
server_st: ServerConfig = self._config.get_configuration(f"ServerConfig_{guild_id}")
if not mass_delete:
await asyncio.sleep(server_st.message_delete_timer)
self._logger.debug(
@@ -74,9 +64,7 @@ class MessageService(MessageServiceABC):
self._logger.error(__name__, f"Deleting message failed", e)
else:
if not without_tracking:
self._clients.append_deleted_message_count(
self._bot.user.id, guild_id, 1
)
self._clients.append_deleted_message_count(self._bot.user.id, guild_id, 1)
self._db.save_changes()
self._logger.info(__name__, f"Deleted message {message}")
@@ -88,9 +76,7 @@ class MessageService(MessageServiceABC):
wait_before_delete: int = None,
without_tracking=False,
):
self._logger.debug(
__name__, f"Try to send message\n\t{message}\n\tto: {channel}"
)
self._logger.debug(__name__, f"Try to send message\n\t{message}\n\tto: {channel}")
msg = None
try:
if isinstance(message, discord.Embed):
@@ -100,15 +86,11 @@ class MessageService(MessageServiceABC):
else:
msg = await channel.send(message)
except Exception as e:
self._logger.error(
__name__, f"Send message to channel {channel.id} failed", e
)
self._logger.error(__name__, f"Send message to channel {channel.id} failed", e)
else:
self._logger.info(__name__, f"Sent message to channel {channel.id}")
if not without_tracking:
self._clients.append_sent_message_count(
self._bot.user.id, channel.guild.id, 1
)
self._clients.append_sent_message_count(self._bot.user.id, channel.guild.id, 1)
self._db.save_changes()
if wait_before_delete is not None:
@@ -125,23 +107,17 @@ class MessageService(MessageServiceABC):
receiver: Union[discord.User, discord.Member],
without_tracking=False,
):
self._logger.debug(
__name__, f"Try to send message\n\t{message}\n\tto: {receiver}"
)
self._logger.debug(__name__, f"Try to send message\n\t{message}\n\tto: {receiver}")
try:
if isinstance(message, discord.Embed):
msg = await receiver.send(embed=message)
else:
msg = await receiver.send(message)
except Exception as e:
self._logger.error(
__name__, f"Send message to user {receiver.id} failed", e
)
self._logger.error(__name__, f"Send message to user {receiver.id} failed", e)
else:
if not without_tracking:
self._clients.append_sent_message_count(
self._bot.user.id, receiver.guild.id, 1
)
self._clients.append_sent_message_count(self._bot.user.id, receiver.guild.id, 1)
self._db.save_changes()
self._logger.info(__name__, f"Sent message to user {receiver.id}")
@@ -160,9 +136,7 @@ class MessageService(MessageServiceABC):
self._logger.debug(__name__, f"Message: {message}")
return None
self._logger.debug(
__name__, f"Try to send message\t\t{message}\n\tto: {ctx.channel}"
)
self._logger.debug(__name__, f"Try to send message\t\t{message}\n\tto: {ctx.channel}")
msg = None
try:
if isinstance(message, discord.Embed):
@@ -170,15 +144,11 @@ class MessageService(MessageServiceABC):
else:
msg = await ctx.send(message, file=file, ephemeral=not is_public)
except Exception as e:
self._logger.error(
__name__, f"Send message to channel {ctx.channel.id} failed", e
)
self._logger.error(__name__, f"Send message to channel {ctx.channel.id} failed", e)
else:
self._logger.info(__name__, f"Sent message to channel {ctx.channel.id}")
if not without_tracking and ctx.guild is not None:
self._clients.append_sent_message_count(
self._bot.user.id, ctx.guild.id, 1
)
self._clients.append_sent_message_count(self._bot.user.id, ctx.guild.id, 1)
self._db.save_changes()
if wait_before_delete is not None:
@@ -207,30 +177,18 @@ class MessageService(MessageServiceABC):
self._logger.debug(__name__, f"Message: {message}")
return
self._logger.debug(
__name__, f"Try to send message\t\t{message}\n\tto: {interaction.channel}"
)
self._logger.debug(__name__, f"Try to send message\t\t{message}\n\tto: {interaction.channel}")
try:
if isinstance(message, discord.Embed):
await interaction.response.send_message(
embed=message, ephemeral=not is_public, **kwargs
)
await interaction.response.send_message(embed=message, ephemeral=not is_public, **kwargs)
else:
await interaction.response.send_message(
message, ephemeral=not is_public, **kwargs
)
await interaction.response.send_message(message, ephemeral=not is_public, **kwargs)
except Exception as e:
self._logger.error(
__name__, f"Send message to channel {interaction.channel.id} failed", e
)
self._logger.error(__name__, f"Send message to channel {interaction.channel.id} failed", e)
else:
self._logger.info(
__name__, f"Sent message to channel {interaction.channel.id}"
)
self._logger.info(__name__, f"Sent message to channel {interaction.channel.id}")
if not without_tracking and interaction.guild is not None:
self._clients.append_sent_message_count(
self._bot.user.id, interaction.guild.id, 1
)
self._clients.append_sent_message_count(self._bot.user.id, interaction.guild.id, 1)
self._db.save_changes()
if wait_before_delete is not None:
@@ -239,6 +197,4 @@ class MessageService(MessageServiceABC):
if is_persistent:
return
await self.delete_message(
await interaction.original_response(), without_tracking
)
await self.delete_message(await interaction.original_response(), without_tracking)

View File

@@ -15,7 +15,7 @@ __title__ = "bot_data"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -15,7 +15,7 @@ __title__ = "bot_data.abc"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de"
__version__ = "1.2.0"
__version__ = "1.2.2"
from collections import namedtuple
@@ -23,4 +23,4 @@ from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="1", minor="2", micro="0")
version_info = VersionInfo(major="1", minor="2", micro="2")

View File

@@ -28,9 +28,7 @@ class AchievementRepositoryABC(ABC):
pass
@abstractmethod
def get_user_got_achievements_by_achievement_id(
self, achievement_id: int
) -> List[Achievement]:
def get_user_got_achievements_by_achievement_id(self, achievement_id: int) -> List[Achievement]:
pass
@abstractmethod

View File

@@ -23,9 +23,7 @@ class AuthUserRepositoryABC(ABC):
pass
@abstractmethod
def get_filtered_auth_users(
self, criteria: AuthUserSelectCriteria
) -> FilteredResult:
def get_filtered_auth_users(self, criteria: AuthUserSelectCriteria) -> FilteredResult:
pass
@abstractmethod

View File

@@ -36,9 +36,7 @@ class ClientRepositoryABC(ABC):
pass
@abstractmethod
def find_client_by_discord_id_and_server_id(
self, discord_id: int, server_id: int
) -> Optional[Client]:
def find_client_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[Client]:
pass
@abstractmethod

View File

@@ -0,0 +1,35 @@
from abc import ABC, abstractmethod
from cpl_query.extension import List
from bot_data.model.scheduled_event import ScheduledEvent
class ScheduledEventRepositoryABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod
def get_scheduled_events(self) -> List[ScheduledEvent]:
pass
@abstractmethod
def get_scheduled_event_by_id(self, id: int) -> ScheduledEvent:
pass
@abstractmethod
def get_scheduled_events_by_server_id(self, id: int) -> List[ScheduledEvent]:
pass
@abstractmethod
def add_scheduled_event(self, scheduled_event: ScheduledEvent):
pass
@abstractmethod
def update_scheduled_event(self, scheduled_event: ScheduledEvent):
pass
@abstractmethod
def delete_scheduled_event(self, scheduled_event: ScheduledEvent):
pass

View File

@@ -35,37 +35,25 @@ class ServerConfigRepositoryABC(ABC):
pass
@abstractmethod
def add_server_team_role_id_config(
self, server_team_role_id: ServerTeamRoleIdsConfig
):
def add_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig):
pass
@abstractmethod
def update_server_team_role_id_config(
self, server_team_role_id: ServerTeamRoleIdsConfig
):
def update_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig):
pass
@abstractmethod
def delete_server_team_role_id_config(
self, server_team_role_id: ServerTeamRoleIdsConfig
):
def delete_server_team_role_id_config(self, server_team_role_id: ServerTeamRoleIdsConfig):
pass
@abstractmethod
def add_server_afk_channel_config(
self, server_afk_channel: ServerAFKChannelIdsConfig
):
def add_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig):
pass
@abstractmethod
def update_server_afk_channel_config(
self, server_afk_channel: ServerAFKChannelIdsConfig
):
def update_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig):
pass
@abstractmethod
def delete_server_afk_channel_config(
self, server_afk_channel: ServerAFKChannelIdsConfig
):
def delete_server_afk_channel_config(self, server_afk_channel: ServerAFKChannelIdsConfig):
pass

View File

@@ -43,19 +43,13 @@ class TechnicianConfigRepositoryABC(ABC):
pass
@abstractmethod
def add_technician_ping_url_config(
self, technician_ping_url: TechnicianPingUrlConfig
):
def add_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig):
pass
@abstractmethod
def update_technician_ping_url_config(
self, technician_ping_url: TechnicianPingUrlConfig
):
def update_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig):
pass
@abstractmethod
def delete_technician_ping_url_config(
self, technician_ping_url: TechnicianPingUrlConfig
):
def delete_technician_ping_url_config(self, technician_ping_url: TechnicianPingUrlConfig):
pass

View File

@@ -20,45 +20,31 @@ class UserJoinedGameServerRepositoryABC(ABC):
pass
@abstractmethod
def get_user_joined_game_servers_by_user_id(
self, user_id: int
) -> List[UserJoinedGameServer]:
def get_user_joined_game_servers_by_user_id(self, user_id: int) -> List[UserJoinedGameServer]:
pass
@abstractmethod
def get_active_user_joined_game_server_by_user_id(
self, user_id: int
) -> UserJoinedGameServer:
def get_active_user_joined_game_server_by_user_id(self, user_id: int) -> UserJoinedGameServer:
pass
@abstractmethod
def find_active_user_joined_game_server_by_user_id(
self, user_id: int
) -> Optional[UserJoinedGameServer]:
def find_active_user_joined_game_server_by_user_id(self, user_id: int) -> Optional[UserJoinedGameServer]:
pass
@abstractmethod
def find_active_user_joined_game_servers_by_user_id(
self, user_id: int
) -> List[Optional[UserJoinedGameServer]]:
def find_active_user_joined_game_servers_by_user_id(self, user_id: int) -> List[Optional[UserJoinedGameServer]]:
pass
@abstractmethod
def add_user_joined_game_server(
self, user_joined_game_server: UserJoinedGameServer
):
def add_user_joined_game_server(self, user_joined_game_server: UserJoinedGameServer):
pass
@abstractmethod
def update_user_joined_game_server(
self, user_joined_game_server: UserJoinedGameServer
):
def update_user_joined_game_server(self, user_joined_game_server: UserJoinedGameServer):
pass
@abstractmethod
def delete_user_joined_game_server(
self, user_joined_game_server: UserJoinedGameServer
):
def delete_user_joined_game_server(self, user_joined_game_server: UserJoinedGameServer):
pass
@abstractmethod

View File

@@ -24,21 +24,15 @@ class UserJoinedServerRepositoryABC(ABC):
pass
@abstractmethod
def get_user_joined_servers_by_user_id(
self, user_id: int
) -> list[UserJoinedServer]:
def get_user_joined_servers_by_user_id(self, user_id: int) -> list[UserJoinedServer]:
pass
@abstractmethod
def get_active_user_joined_server_by_user_id(
self, user_id: int
) -> UserJoinedServer:
def get_active_user_joined_server_by_user_id(self, user_id: int) -> UserJoinedServer:
pass
@abstractmethod
def find_active_user_joined_server_by_user_id(
self, user_id: int
) -> Optional[UserJoinedServer]:
def find_active_user_joined_server_by_user_id(self, user_id: int) -> Optional[UserJoinedServer]:
pass
@abstractmethod

View File

@@ -19,45 +19,31 @@ class UserJoinedVoiceChannelRepositoryABC(ABC):
pass
@abstractmethod
def get_user_joined_voice_channels_by_user_id(
self, user_id: int
) -> List[UserJoinedVoiceChannel]:
def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[UserJoinedVoiceChannel]:
pass
@abstractmethod
def get_active_user_joined_voice_channel_by_user_id(
self, user_id: int
) -> UserJoinedVoiceChannel:
def get_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> UserJoinedVoiceChannel:
pass
@abstractmethod
def find_active_user_joined_voice_channel_by_user_id(
self, user_id: int
) -> Optional[UserJoinedVoiceChannel]:
def find_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> Optional[UserJoinedVoiceChannel]:
pass
@abstractmethod
def find_active_user_joined_voice_channels_by_user_id(
self, user_id: int
) -> List[Optional[UserJoinedVoiceChannel]]:
def find_active_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[Optional[UserJoinedVoiceChannel]]:
pass
@abstractmethod
def add_user_joined_voice_channel(
self, user_joined_voice_channel: UserJoinedVoiceChannel
):
def add_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel):
pass
@abstractmethod
def update_user_joined_voice_channel(
self, user_joined_voice_channel: UserJoinedVoiceChannel
):
def update_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel):
pass
@abstractmethod
def delete_user_joined_voice_channel(
self, user_joined_voice_channel: UserJoinedVoiceChannel
):
def delete_user_joined_voice_channel(self, user_joined_voice_channel: UserJoinedVoiceChannel):
pass
@abstractmethod

View File

@@ -17,9 +17,7 @@ class UserMessageCountPerHourRepositoryABC(ABC):
pass
@abstractmethod
def find_user_message_count_per_hour_by_user_id(
self, user_id: int
) -> Optional[UserMessageCountPerHour]:
def find_user_message_count_per_hour_by_user_id(self, user_id: int) -> Optional[UserMessageCountPerHour]:
pass
@abstractmethod

View File

@@ -32,15 +32,11 @@ class UserRepositoryABC(ABC):
pass
@abstractmethod
def get_user_by_discord_id_and_server_id(
self, discord_id: int, server_id: int
) -> User:
def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User:
pass
@abstractmethod
def find_user_by_discord_id_and_server_id(
self, discord_id: int, server_id: int
) -> Optional[User]:
def find_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> Optional[User]:
pass
@abstractmethod

View File

@@ -4,7 +4,7 @@
"Version": {
"Major": "1",
"Minor": "2",
"Micro": "0"
"Micro": "2"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",

View File

@@ -14,6 +14,7 @@ from bot_data.abc.data_seeder_abc import DataSeederABC
from bot_data.abc.game_server_repository_abc import GameServerRepositoryABC
from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC
from bot_data.abc.level_repository_abc import LevelRepositoryABC
from bot_data.abc.scheduled_event_repository_abc import ScheduledEventRepositoryABC
from bot_data.abc.server_config_repository_abc import ServerConfigRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.short_role_name_repository_abc import ShortRoleNameRepositoryABC
@@ -45,6 +46,7 @@ from bot_data.service.client_repository_service import ClientRepositoryService
from bot_data.service.game_server_repository_service import GameServerRepositoryService
from bot_data.service.known_user_repository_service import KnownUserRepositoryService
from bot_data.service.level_repository_service import LevelRepositoryService
from bot_data.service.scheduled_event_repository_service import ScheduledEventRepositoryService
from bot_data.service.seeder_service import SeederService
from bot_data.service.server_config_repository_service import (
ServerConfigRepositoryService,
@@ -86,14 +88,10 @@ class DataModule(ModuleABC):
def __init__(self, dc: DiscordCollectionABC):
ModuleABC.__init__(self, dc, FeatureFlagsEnum.data_module)
def configure_configuration(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC
):
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(
self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC
):
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
services.add_singleton(CacheService)
services.add_transient(ApiKeyRepositoryABC, ApiKeyRepositoryService)
@@ -102,15 +100,9 @@ class DataModule(ModuleABC):
services.add_transient(UserRepositoryABC, UserRepositoryService)
services.add_transient(ClientRepositoryABC, ClientRepositoryService)
services.add_transient(KnownUserRepositoryABC, KnownUserRepositoryService)
services.add_transient(
UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService
)
services.add_transient(
UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService
)
services.add_transient(
UserJoinedGameServerRepositoryABC, UserJoinedGameServerRepositoryService
)
services.add_transient(UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService)
services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService)
services.add_transient(UserJoinedGameServerRepositoryABC, UserJoinedGameServerRepositoryService)
services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService)
services.add_transient(LevelRepositoryABC, LevelRepositoryService)
services.add_transient(UserWarningsRepositoryABC, UserWarningsRepositoryService)
@@ -119,20 +111,13 @@ class DataModule(ModuleABC):
UserMessageCountPerHourRepositoryService,
)
services.add_transient(GameServerRepositoryABC, GameServerRepositoryService)
services.add_transient(
UserGameIdentRepositoryABC, UserGameIdentRepositoryService
)
services.add_transient(UserGameIdentRepositoryABC, UserGameIdentRepositoryService)
services.add_transient(AchievementRepositoryABC, AchievementRepositoryService)
services.add_transient(
TechnicianConfigRepositoryABC, TechnicianConfigRepositoryService
)
services.add_transient(TechnicianConfigRepositoryABC, TechnicianConfigRepositoryService)
services.add_transient(ServerConfigRepositoryABC, ServerConfigRepositoryService)
services.add_transient(
ShortRoleNameRepositoryABC, ShortRoleNameRepositoryService
)
services.add_transient(
SteamSpecialOfferRepositoryABC, SteamSpecialOfferRepositoryService
)
services.add_transient(ShortRoleNameRepositoryABC, ShortRoleNameRepositoryService)
services.add_transient(SteamSpecialOfferRepositoryABC, SteamSpecialOfferRepositoryService)
services.add_transient(ScheduledEventRepositoryABC, ScheduledEventRepositoryService)
services.add_transient(SeederService)
services.add_transient(DataSeederABC, TechnicianConfigSeeder)

View File

@@ -1,147 +0,0 @@
from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.migration_abc import MigrationABC
from bot_data.db_context import DBContext
class AchievementsMigration(MigrationABC):
name = "1.1.0_AchievementsMigration"
def __init__(self, logger: DatabaseLogger, db: DBContext):
MigrationABC.__init__(self)
self._logger = logger
self._db = db
self._cursor = db.cursor
def upgrade(self):
self._logger.debug(__name__, "Running upgrade")
self._cursor.execute(
str(
f"""
CREATE TABLE IF NOT EXISTS `Achievements` (
`Id` BIGINT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NOT NULL,
`Attribute` VARCHAR(255) NOT NULL,
`Operator` VARCHAR(255) NOT NULL,
`Value` VARCHAR(255) NOT NULL,
`ServerId` BIGINT,
`CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6),
`LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY(`Id`),
FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`)
);
"""
)
)
self._cursor.execute(
str(
f"""
CREATE TABLE IF NOT EXISTS `AchievementsHistory`
(
`Id` BIGINT(20) NOT NULL,
`Name` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NOT NULL,
`Attribute` VARCHAR(255) NOT NULL,
`Operator` VARCHAR(255) NOT NULL,
`Value` VARCHAR(255) NOT NULL,
`ServerId` BIGINT,
`Deleted` BOOL DEFAULT FALSE,
`DateFrom` DATETIME(6) NOT NULL,
`DateTo` DATETIME(6) NOT NULL
);
"""
)
)
self._cursor.execute(
str(
f"""
CREATE TABLE IF NOT EXISTS `UserGotAchievements` (
`Id` BIGINT NOT NULL AUTO_INCREMENT,
`UserId` BIGINT,
`AchievementId` BIGINT,
`CreatedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6),
`LastModifiedAt` DATETIME(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY(`Id`),
FOREIGN KEY (`UserId`) REFERENCES `Users`(`UserId`),
FOREIGN KEY (`AchievementId`) REFERENCES `Achievements`(`Id`)
);
"""
)
)
# A join table history between users and achievements is not necessary.
self._cursor.execute(
str(
f"""ALTER TABLE Users ADD MessageCount BIGINT NOT NULL DEFAULT 0 AFTER XP;"""
)
)
self._cursor.execute(
str(
f"""ALTER TABLE Users ADD ReactionCount BIGINT NOT NULL DEFAULT 0 AFTER XP;"""
)
)
self._cursor.execute(
str(
f"""ALTER TABLE UsersHistory ADD MessageCount BIGINT NOT NULL DEFAULT 0 AFTER XP;"""
)
)
self._cursor.execute(
str(
f"""ALTER TABLE UsersHistory ADD ReactionCount BIGINT NOT NULL DEFAULT 0 AFTER XP;"""
)
)
self._cursor.execute(
str(f"""DROP TRIGGER IF EXISTS `TR_AchievementsUpdate`;""")
)
self._cursor.execute(
str(
f"""
CREATE TRIGGER `TR_AchievementsUpdate`
AFTER UPDATE
ON `Achievements`
FOR EACH ROW
BEGIN
INSERT INTO `AchievementsHistory` (
`Id`, `Name`, `Description`, `Attribute`, `Operator`, `Value`, `ServerId`, `DateFrom`, `DateTo`
)
VALUES (
OLD.Id, OLD.Name, OLD.Description, OLD.Attribute, OLD.Operator, OLD.Value, OLD.ServerId, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6)
);
END;
"""
)
)
self._cursor.execute(
str(f"""DROP TRIGGER IF EXISTS `TR_AchievementsDelete`;""")
)
self._cursor.execute(
str(
f"""
CREATE TRIGGER `TR_AchievementsDelete`
AFTER DELETE
ON `Achievements`
FOR EACH ROW
BEGIN
INSERT INTO `AchievementsHistory` (
`Id`, `Name`, `Description`, `Attribute`, `Operator`, `Value`, `ServerId`, `Deleted`, `DateFrom`, `DateTo`
)
VALUES (
OLD.Id, OLD.Name, OLD.Description, OLD.Attribute, OLD.Operator, OLD.Value, OLD.ServerId, TRUE, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6)
);
END;
"""
)
)
def downgrade(self):
self._cursor.execute("DROP TABLE `Achievements`;")
self._cursor.execute(str(f"""ALTER TABLE Users DROP COLUMN MessageCount;"""))
self._cursor.execute(str(f"""ALTER TABLE Users DROP COLUMN ReactionCount;"""))

View File

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

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