aa-killtracker 0.18.0a2__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aa_killtracker-0.18.0a2.dist-info → aa_killtracker-1.0.0.dist-info}/METADATA +7 -8
- {aa_killtracker-0.18.0a2.dist-info → aa_killtracker-1.0.0.dist-info}/RECORD +33 -30
- killtracker/__init__.py +1 -1
- killtracker/admin.py +1 -1
- killtracker/app_settings.py +14 -7
- killtracker/core/discord.py +162 -0
- killtracker/core/helpers.py +13 -0
- killtracker/core/{discord_messages.py → trackers.py} +16 -75
- killtracker/core/{worker_shutdown.py → workers.py} +3 -4
- killtracker/core/{killmails.py → zkb.py} +32 -40
- killtracker/managers.py +1 -1
- killtracker/models/trackers.py +4 -5
- killtracker/models/webhooks.py +4 -53
- killtracker/signals.py +4 -4
- killtracker/tasks.py +47 -49
- killtracker/tests/core/test_discord.py +184 -0
- killtracker/tests/core/test_helpers.py +23 -0
- killtracker/tests/core/{test_discord_messages_1.py → test_tracker_1.py} +12 -39
- killtracker/tests/core/{test_discord_messages_2.py → test_tracker_2.py} +3 -3
- killtracker/tests/core/test_workers.py +49 -0
- killtracker/tests/core/{test_killmails.py → test_zkb.py} +58 -46
- killtracker/tests/models/test_killmails.py +0 -2
- killtracker/tests/models/test_trackers_1.py +1 -1
- killtracker/tests/models/test_trackers_2.py +2 -2
- killtracker/tests/models/test_webhooks.py +63 -0
- killtracker/tests/test_integration.py +26 -13
- killtracker/tests/test_tasks.py +68 -58
- killtracker/tests/test_utils.py +39 -0
- killtracker/tests/testdata/factories.py +1 -1
- killtracker/tests/testdata/helpers.py +1 -1
- killtracker/tests/utils.py +28 -0
- killtracker/exceptions.py +0 -32
- killtracker/tests/core/test_worker_shutdown.py +0 -34
- killtracker/tests/models/test_webhook.py +0 -164
- killtracker/tests/test_exceptions.py +0 -12
- {aa_killtracker-0.18.0a2.dist-info → aa_killtracker-1.0.0.dist-info}/WHEEL +0 -0
- {aa_killtracker-0.18.0a2.dist-info → aa_killtracker-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aa-killtracker
|
3
|
-
Version: 0.
|
3
|
+
Version: 1.0.0
|
4
4
|
Summary: An app for running killmail trackers with Alliance Auth and Discord.
|
5
5
|
Author-email: Erik Kalkoken <kalkoken87@gmail.com>
|
6
6
|
Requires-Python: >=3.8
|
7
7
|
Description-Content-Type: text/markdown
|
8
8
|
Classifier: Environment :: Web Environment
|
9
9
|
Classifier: Framework :: Django
|
10
|
-
Classifier: Framework :: Django :: 4.0
|
11
10
|
Classifier: Framework :: Django :: 4.2
|
12
11
|
Classifier: Intended Audience :: End Users/Desktop
|
13
12
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -17,15 +16,16 @@ Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
20
|
Classifier: Topic :: Internet :: WWW/HTTP
|
21
21
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
22
22
|
License-File: LICENSE
|
23
|
-
Requires-Dist: allianceauth-app-utils>=1.
|
24
|
-
Requires-Dist: allianceauth>=
|
23
|
+
Requires-Dist: allianceauth-app-utils>=1.26
|
24
|
+
Requires-Dist: allianceauth>=4,<5
|
25
25
|
Requires-Dist: dacite
|
26
|
-
Requires-Dist: dhooks-lite>=1.
|
27
|
-
Requires-Dist: django-eveuniverse>=1.
|
28
|
-
Requires-Dist: redis-simple-mq>=0
|
26
|
+
Requires-Dist: dhooks-lite>=1.1
|
27
|
+
Requires-Dist: django-eveuniverse>=1.5
|
28
|
+
Requires-Dist: redis-simple-mq>=1.0
|
29
29
|
Project-URL: Home, https://gitlab.com/ErikKalkoken/aa-killtracker
|
30
30
|
|
31
31
|
# Killtracker
|
@@ -242,7 +242,6 @@ Note that all settings are optional and the app will use the documented default
|
|
242
242
|
Name | Description | Default
|
243
243
|
-- | -- | --
|
244
244
|
`KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER`| Ignore killmails that are older than the given number in minutes. Sometimes killmails appear belated on ZKB, this feature ensures they don't create new alerts | `60`
|
245
|
-
`KILLTRACKER_MAX_KILLMAILS_PER_RUN`| Maximum number of killmails retrieved from ZKB by task run. This value should be set such that the task that fetches new killmails from ZKB every minute will reliable finish within one minute. To test this run a "Catch all" tracker and see how many killmails your system is capable of processing. Note that you can get that information from the worker's log file. It will look something like this: `Total killmails received from ZKB in 49 secs: 100` | `100`
|
246
245
|
`KILLTRACKER_PURGE_KILLMAILS_AFTER_DAYS`| Killmails older than set number of days will be purged from the database. If you want to keep all killmails set this to 0. Note that this setting is only relevant if you have storing killmails enabled. | `30`
|
247
246
|
`KILLTRACKER_QUEUE_ID`| Unique ID used to identify this server when fetching killmails from zKillboard. This setting is mandatory. | ``
|
248
247
|
`KILLTRACKER_STORING_KILLMAILS_ENABLED`| If set to true Killtracker will automatically store all received killmails in the local database. This can be useful if you want to run analytics on killmails etc. However, please note that Killtracker itself currently does not use stored killmails in any way. | `False`
|
@@ -1,19 +1,20 @@
|
|
1
|
-
killtracker/__init__.py,sha256=
|
2
|
-
killtracker/admin.py,sha256=
|
3
|
-
killtracker/app_settings.py,sha256=
|
1
|
+
killtracker/__init__.py,sha256=Pi-3zSugKHOaitGQHTp9XWoyaiw6k0pRhcNTmCdrBlg,354
|
2
|
+
killtracker/admin.py,sha256=IH04Q1etHUnXSJIhm3bnGfIbSU6lPSSaltLERb-OkYg,14229
|
3
|
+
killtracker/app_settings.py,sha256=KqWSS4OVKIa7DmBAa5AbwxFVXw5EQebPfBtPo1UJYgY,4295
|
4
4
|
killtracker/apps.py,sha256=lWolk2CVEHPNIMEylbK2fSuCSTRW1n6LBmiDjLoqqq4,384
|
5
5
|
killtracker/checks.py,sha256=ytqWlhIbCsvGKi3xFbUvAZzbUlgOhiOrB67PZ2WfxM4,1311
|
6
6
|
killtracker/constants.py,sha256=W7-VARE3B4_sdzV_9k7qunUzlxTcdSAeSp_ZJVnbUhw,2514
|
7
|
-
killtracker/exceptions.py,sha256=aFY6qxQOta1V-qvgKp19Cgy6M16O7Si8moc2205OsX0,846
|
8
7
|
killtracker/forms.py,sha256=SUM0U-Sa6FPcGVpsYJbVxHfEtf7hb1Qc_l92Zfa1OLk,5193
|
9
|
-
killtracker/managers.py,sha256=
|
8
|
+
killtracker/managers.py,sha256=hrmpiuMzK-7p-AsT4ee0mMEhQv4b57AGmhlRTxEl7wk,4416
|
10
9
|
killtracker/providers.py,sha256=3DiOlklBq5kLGDV9nMlMbP2-S8AodNgR6gtB4cWWCUo,358
|
11
|
-
killtracker/signals.py,sha256=
|
12
|
-
killtracker/tasks.py,sha256=
|
10
|
+
killtracker/signals.py,sha256=rFCg-WYMVPmAhuqX8KHNaicsP1nyj_HYA2O4Qr8wqhk,892
|
11
|
+
killtracker/tasks.py,sha256=9ZqItmm2tlzVA-lelNTyzpPuBYCUvfZR2nCV16Xu6fA,9267
|
13
12
|
killtracker/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
killtracker/core/
|
15
|
-
killtracker/core/
|
16
|
-
killtracker/core/
|
13
|
+
killtracker/core/discord.py,sha256=cJY1qZ1ULlndliqn304B2deZkgL8VRqnAN_E5bXbTPk,5330
|
14
|
+
killtracker/core/helpers.py,sha256=Ulwhz96IopCCNPD6m7ylz9IGnxknIvLSCfcLrmJ8uDE,335
|
15
|
+
killtracker/core/trackers.py,sha256=ZENNdZ96L9AP3nIaP3_s9uFIVRgq_kLPGLK-rKT5gig,14693
|
16
|
+
killtracker/core/workers.py,sha256=m9Vp7C-eXSfQpf0ugzTfuqcPBPKvarfds_4mb-JwJ-o,1278
|
17
|
+
killtracker/core/zkb.py,sha256=n9eD9RT5xZ3Mc88ItoaQE_1lJAniqpr_oyps47sdGYQ,21481
|
17
18
|
killtracker/management/commands/killtracker_load_eve.py,sha256=5P2wr6LU-EMl9_gG-DKP2yw3eFXOU7ApeuGAWEbvCKk,1159
|
18
19
|
killtracker/migrations/0001_initial_new.py,sha256=WszI5DmNFtk45IZ2Zul3_Ak-VXmye9ffJKFAsGIxoXA,27818
|
19
20
|
killtracker/migrations/0001_squashed_all.py,sha256=KXwOcRayjGGGmleg7-aTcXeIi-92Mn4BaGkEY3iE9Vc,26221
|
@@ -31,41 +32,43 @@ killtracker/migrations/0009_remove_old_models.py,sha256=bDIz0hywpFKbHYhdXFXzra5D
|
|
31
32
|
killtracker/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
33
|
killtracker/models/__init__.py,sha256=WL6VD9MK7EsZot_RLPz0nX8WxJkp7RtQpw_mYpnVqu8,188
|
33
34
|
killtracker/models/killmails.py,sha256=CCdrHkwUSoTOGqavG-TpMxDRleF9UEacOTui8v3-VT8,5181
|
34
|
-
killtracker/models/trackers.py,sha256=
|
35
|
-
killtracker/models/webhooks.py,sha256=
|
35
|
+
killtracker/models/trackers.py,sha256=wo6KWl1GOASOquTTdAeo5DaZgRzKai1ek1GjQ49oOhY,31154
|
36
|
+
killtracker/models/webhooks.py,sha256=6S1wXwlOMth56oU0hsYH5PiimECMwZyLXiKok2yQo_g,5850
|
36
37
|
killtracker/static/killtracker/killtracker_logo.png,sha256=3jc9zmYHqP60Np5piP5RfkX0_II-315DNjZ4FRGbqKc,74625
|
37
38
|
killtracker/static/killtracker/zkb_icon.png,sha256=wuVfgyTbTs9qS4KGbDAH3Q9KVPgHqYV5eaVrjEmTjsE,328
|
38
39
|
killtracker/templates/admin/killtracker/tracker/killmail_test.html,sha256=TI2ON8qf9pW4rX6G7pT990ZadkipwncQgGUC0FPcWQY,649
|
39
40
|
killtracker/tests/__init__.py,sha256=CEt5qqp6ptDrx1lwuA6epZ5bzh1CwUJYXeMZqWK9jT0,278
|
40
41
|
killtracker/tests/test_admin.py,sha256=XFAlF_Q4h5Z_agkmRtB30GmCayqQPblLolFrN41xu7Q,5659
|
41
42
|
killtracker/tests/test_admin_2.py,sha256=rP0EscM9qUil_ZuXute3R8TQY2P5XSjmxHLbziCGcVw,1140
|
42
|
-
killtracker/tests/
|
43
|
-
killtracker/tests/
|
44
|
-
killtracker/tests/
|
45
|
-
killtracker/tests/utils.py,sha256=
|
43
|
+
killtracker/tests/test_integration.py,sha256=fOAOu-lKYFAk0hYYT-V4F3KgmPcrD7JgcR-w-z3lStg,3516
|
44
|
+
killtracker/tests/test_tasks.py,sha256=hTxXKoza-hgAUsK2zj5qMkl4igHKB7SB2OkPFJtgXeA,16274
|
45
|
+
killtracker/tests/test_utils.py,sha256=1TA0pzCpq8CAKRBTi_xZ9_45n3rPx7UclkgFPzT1VFY,1130
|
46
|
+
killtracker/tests/utils.py,sha256=auwXHSpLwI_Qi3NmQUOCrIAcEsuw04ZaNJW82-jD4DU,1139
|
46
47
|
killtracker/tests/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
|
-
killtracker/tests/core/
|
48
|
-
killtracker/tests/core/
|
49
|
-
killtracker/tests/core/
|
50
|
-
killtracker/tests/core/
|
48
|
+
killtracker/tests/core/test_discord.py,sha256=w_QU1kVfo4uKl1xbj1C5NFQuPqASiveMjf0Q0fEIRWY,6163
|
49
|
+
killtracker/tests/core/test_helpers.py,sha256=pQSi9QBOZWOem9NMzcO-qmc_EIWyhyA_aOevuLXrBk4,613
|
50
|
+
killtracker/tests/core/test_tracker_1.py,sha256=EjGX2XyHDMAfhevUwi90YcPBSUhTlDv1c7bMgv4qUX0,3528
|
51
|
+
killtracker/tests/core/test_tracker_2.py,sha256=ZQ3bZ4tvG3Xf_z8pMQllVlAcWOaltSP2BzqmJAezBEc,4748
|
52
|
+
killtracker/tests/core/test_workers.py,sha256=stId22PF5VgrSOpCJV5ZiDak7eKM5rNOS7pbx1QNp6Q,1722
|
53
|
+
killtracker/tests/core/test_zkb.py,sha256=lfsJlQgtX-V4rmnkwF60-c2IeRO2tRdead0RAS6WMQg,16557
|
51
54
|
killtracker/tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
|
-
killtracker/tests/models/test_killmails.py,sha256=
|
53
|
-
killtracker/tests/models/test_trackers_1.py,sha256=
|
54
|
-
killtracker/tests/models/test_trackers_2.py,sha256
|
55
|
-
killtracker/tests/models/
|
55
|
+
killtracker/tests/models/test_killmails.py,sha256=pNJ7vSLqSl6BbokePdJnQNFWIDwbxkxugpP-QVoJcKQ,8247
|
56
|
+
killtracker/tests/models/test_trackers_1.py,sha256=gqywaysnijyF_DT2TdiSJuLc1FUegftnO1pYECMX_Tc,42788
|
57
|
+
killtracker/tests/models/test_trackers_2.py,sha256=-iVWnyFvoRb9Y7yKskjc5mmUl3XX53jdQYo8fri4TgA,5458
|
58
|
+
killtracker/tests/models/test_webhooks.py,sha256=PtbTxNfYH-NKpLnLZrzbOOqHlJ9vY4Gy19tf2TsHbzI,2597
|
56
59
|
killtracker/tests/testdata/__init__.py,sha256=9aQhf8V-DseZMWjJ_QMXOba6CypoHFSVpYRZEUh-oFc,212
|
57
60
|
killtracker/tests/testdata/create_eveuniverse.py,sha256=Ewr5OBiQwjK1kwDCRh3A3f0Z-CCPi8Gp8dpsPgVAU4A,966
|
58
61
|
killtracker/tests/testdata/evealliances.json,sha256=i6udrtUnWQjn71Iw17WWz9Eb1fdGUNhI1UYLt0hZHog,307
|
59
62
|
killtracker/tests/testdata/evecorporations.json,sha256=Hk4WuplZl69rQcquOU-15lgUyvHRMZW0fnYfKz8oeWw,1229
|
60
63
|
killtracker/tests/testdata/eveentities.json,sha256=0yStlAMEhednEiMKvKOYFsyRovtsGxw6IpWAyvnQXv4,1459
|
61
64
|
killtracker/tests/testdata/eveuniverse.json,sha256=JagrJ-asKlkxrqj3dtAth6d5bhIpeEmlvBHgXjLTMSI,36117
|
62
|
-
killtracker/tests/testdata/factories.py,sha256=
|
63
|
-
killtracker/tests/testdata/helpers.py,sha256=
|
65
|
+
killtracker/tests/testdata/factories.py,sha256=MEJLfslB13J_UnN7TXASL6poH1fkQ1NW5dQX_xtfPvA,11013
|
66
|
+
killtracker/tests/testdata/helpers.py,sha256=bsHv2HlbORuw5kU8qZ806nXPM0hqNUuj0qVQCm4KoZ8,4735
|
64
67
|
killtracker/tests/testdata/killmails.json,sha256=HKfpXQJ_ikymBSewh4wWixYpLwC3kNBqi04cCms1AjA,45366
|
65
68
|
killtracker/tests/testdata/load_eveuniverse.py,sha256=FitVc12E_ob56ezK5Y1q07Kbyc7N8poHiCNaLRGU4Jg,388
|
66
69
|
killtracker/tools/drop_tables_killtracker.sql,sha256=fJrGY23NKRunpYC3hI-klFkWub4sBuhfP8TaT__zjPg,1726
|
67
70
|
killtracker/tools/generate_conditions_text.py,sha256=LxAtTQIX-IbuqaNDMV1XHhq5AbrrlwxLWgXa1M7DWQo,767
|
68
|
-
aa_killtracker-0.
|
69
|
-
aa_killtracker-0.
|
70
|
-
aa_killtracker-0.
|
71
|
-
aa_killtracker-0.
|
71
|
+
aa_killtracker-1.0.0.dist-info/licenses/LICENSE,sha256=XZiwB_S_40_HhnvLg5xvtBb3g1oGjPrk0rpFwk8iInE,1070
|
72
|
+
aa_killtracker-1.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
73
|
+
aa_killtracker-1.0.0.dist-info/METADATA,sha256=of14ijiJxhikyRX3EjazGngQlvdynnaeZFKyJXK3Qf8,13442
|
74
|
+
aa_killtracker-1.0.0.dist-info/RECORD,,
|
killtracker/__init__.py
CHANGED
killtracker/admin.py
CHANGED
@@ -12,7 +12,7 @@ from django.utils.safestring import mark_safe
|
|
12
12
|
from allianceauth import NAME as site_header
|
13
13
|
|
14
14
|
from killtracker import tasks
|
15
|
-
from killtracker.core.
|
15
|
+
from killtracker.core.zkb import Killmail
|
16
16
|
from killtracker.forms import (
|
17
17
|
TrackerAdminForm,
|
18
18
|
TrackerAdminKillmailIdForm,
|
killtracker/app_settings.py
CHANGED
@@ -13,13 +13,8 @@ sometimes killmails appear belated on ZKB,
|
|
13
13
|
this feature ensures they don't create new alerts.
|
14
14
|
"""
|
15
15
|
|
16
|
-
KILLTRACKER_MAX_KILLMAILS_PER_RUN = clean_setting(
|
17
|
-
"KILLTRACKER_MAX_KILLMAILS_PER_RUN", 500
|
18
|
-
)
|
19
|
-
"""Maximum number of killmails retrieved from ZKB by task run."""
|
20
|
-
|
21
16
|
KILLTRACKER_PURGE_KILLMAILS_AFTER_DAYS = clean_setting(
|
22
|
-
"KILLTRACKER_PURGE_KILLMAILS_AFTER_DAYS", 30
|
17
|
+
"KILLTRACKER_PURGE_KILLMAILS_AFTER_DAYS", default_value=30, min_value=0
|
23
18
|
)
|
24
19
|
"""Killmails older than set number of days will be purged from the database.
|
25
20
|
If you want to keep all killmails set this to 0.
|
@@ -103,9 +98,21 @@ KILLTRACKER_STORAGE_KILLMAILS_LIFETIME = clean_setting(
|
|
103
98
|
)
|
104
99
|
"""Max lifetime of killmails in temporary storage in seconds."""
|
105
100
|
|
106
|
-
KILLTRACKER_ZKB_REQUEST_DELAY = clean_setting(
|
101
|
+
KILLTRACKER_ZKB_REQUEST_DELAY = clean_setting(
|
102
|
+
"KILLTRACKER_ZKB_REQUEST_DELAY", default_value=500, min_value=500
|
103
|
+
)
|
107
104
|
"""Delay between subsequent calls to ZKB API in milliseconds.
|
108
105
|
|
109
106
|
This delay ensures the app does not breach the CloudFlare rate limit of currently
|
110
107
|
two (2) requests per second per IP address.
|
111
108
|
"""
|
109
|
+
|
110
|
+
KILLTRACKER_MAX_KILLMAILS_PER_RUN = clean_setting(
|
111
|
+
"KILLTRACKER_MAX_KILLMAILS_PER_RUN", default_value=500, min_value=1
|
112
|
+
)
|
113
|
+
"""Maximum number of killmails retrieved from ZKB by task run."""
|
114
|
+
|
115
|
+
KILLTRACKER_MAX_MESSAGES_SENT_PER_RUN = clean_setting(
|
116
|
+
"KILLTRACKER_MAX_MESSAGES_SENT_PER_RUN", default_value=10, min_value=1
|
117
|
+
)
|
118
|
+
"""Maximum number of messages processed per task run."""
|
@@ -0,0 +1,162 @@
|
|
1
|
+
"""Send messages to Discord webhooks."""
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
import json
|
5
|
+
from copy import copy
|
6
|
+
from dataclasses import dataclass
|
7
|
+
from http import HTTPStatus
|
8
|
+
from time import sleep
|
9
|
+
from typing import List, Optional
|
10
|
+
|
11
|
+
import dhooks_lite
|
12
|
+
|
13
|
+
from django.core.cache import cache
|
14
|
+
from django.utils.timezone import now
|
15
|
+
|
16
|
+
from allianceauth.services.hooks import get_extension_logger
|
17
|
+
from app_utils.json import JSONDateTimeDecoder, JSONDateTimeEncoder
|
18
|
+
from app_utils.logging import LoggerAddTag
|
19
|
+
|
20
|
+
from killtracker import APP_NAME, HOMEPAGE_URL, __title__, __version__
|
21
|
+
from killtracker.app_settings import KILLTRACKER_DISCORD_SEND_DELAY
|
22
|
+
from killtracker.core.helpers import datetime_or_none
|
23
|
+
|
24
|
+
_DEFAULT_429_TIMEOUT = 600
|
25
|
+
|
26
|
+
logger = LoggerAddTag(get_extension_logger(__name__), __title__)
|
27
|
+
|
28
|
+
|
29
|
+
class HTTPError(Exception):
|
30
|
+
"""A HTTP error."""
|
31
|
+
|
32
|
+
def __init__(self, status_code: int):
|
33
|
+
self.status_code = status_code
|
34
|
+
|
35
|
+
|
36
|
+
class WebhookRateLimitExhausted(Exception):
|
37
|
+
"""The rate limit of a webhook has been exhausted."""
|
38
|
+
|
39
|
+
def __init__(self, retry_at: dt.datetime, is_original: bool = True):
|
40
|
+
self.retry_at = retry_at
|
41
|
+
self.is_original = is_original
|
42
|
+
|
43
|
+
|
44
|
+
@dataclass
|
45
|
+
class DiscordMessage:
|
46
|
+
"""A Discord message created from a Killmail."""
|
47
|
+
|
48
|
+
avatar_url: Optional[str] = None
|
49
|
+
content: Optional[str] = None
|
50
|
+
embeds: Optional[List[dhooks_lite.Embed]] = None
|
51
|
+
killmail_id: int = 0 # Killmail ID this message from created from
|
52
|
+
username: Optional[str] = None
|
53
|
+
|
54
|
+
def __post_init__(self):
|
55
|
+
if not self.content and not self.embeds:
|
56
|
+
raise ValueError("Message must have content or embeds to be valid")
|
57
|
+
|
58
|
+
def to_json(self) -> str:
|
59
|
+
"""Converts a Discord message into a JSON object and returns it."""
|
60
|
+
|
61
|
+
if self.embeds:
|
62
|
+
embeds_list = [obj.asdict() for obj in self.embeds]
|
63
|
+
else:
|
64
|
+
embeds_list = None
|
65
|
+
|
66
|
+
message = {}
|
67
|
+
if self.killmail_id:
|
68
|
+
message["killmail_id"] = self.killmail_id
|
69
|
+
if self.content:
|
70
|
+
message["content"] = self.content
|
71
|
+
if embeds_list:
|
72
|
+
message["embeds"] = embeds_list
|
73
|
+
if self.username:
|
74
|
+
message["username"] = self.username
|
75
|
+
if self.avatar_url:
|
76
|
+
message["avatar_url"] = self.avatar_url
|
77
|
+
|
78
|
+
return json.dumps(message, cls=JSONDateTimeEncoder)
|
79
|
+
|
80
|
+
@classmethod
|
81
|
+
def from_json(cls, s: str) -> "DiscordMessage":
|
82
|
+
"""Creates a DiscordMessage object from an JSON object and returns it."""
|
83
|
+
message1: dict = json.loads(s, cls=JSONDateTimeDecoder)
|
84
|
+
message2 = copy(message1)
|
85
|
+
if message1.get("embeds"):
|
86
|
+
message2["embeds"] = [
|
87
|
+
dhooks_lite.Embed.from_dict(embed_dict)
|
88
|
+
for embed_dict in message1.get("embeds")
|
89
|
+
]
|
90
|
+
else:
|
91
|
+
message2["embeds"] = None
|
92
|
+
return cls(**message2)
|
93
|
+
|
94
|
+
|
95
|
+
def send_message_to_webhook(name: str, url: str, message: DiscordMessage) -> int:
|
96
|
+
"""Send a message to a Discord webhook and returns the ID of new message."""
|
97
|
+
|
98
|
+
key_retry_at = _make_key_retry_at(url)
|
99
|
+
retry_at = datetime_or_none(cache.get(key_retry_at))
|
100
|
+
if retry_at is not None and retry_at > now():
|
101
|
+
raise WebhookRateLimitExhausted(retry_at=retry_at, is_original=False)
|
102
|
+
|
103
|
+
key_last_request = _make_key_last_request(url)
|
104
|
+
last_request = datetime_or_none(cache.get(key_last_request))
|
105
|
+
if last_request is not None:
|
106
|
+
next_slot = last_request + dt.timedelta(seconds=KILLTRACKER_DISCORD_SEND_DELAY)
|
107
|
+
seconds = (next_slot - now()).total_seconds()
|
108
|
+
if seconds > 0:
|
109
|
+
logger.debug(
|
110
|
+
"%s: Waiting %f seconds for next free slot for webhook", name, seconds
|
111
|
+
)
|
112
|
+
sleep(seconds)
|
113
|
+
|
114
|
+
hook = dhooks_lite.Webhook(
|
115
|
+
url=url,
|
116
|
+
user_agent=dhooks_lite.UserAgent(
|
117
|
+
name=APP_NAME, url=HOMEPAGE_URL, version=__version__
|
118
|
+
),
|
119
|
+
)
|
120
|
+
response = hook.execute(
|
121
|
+
content=message.content,
|
122
|
+
embeds=message.embeds,
|
123
|
+
username=message.username,
|
124
|
+
avatar_url=message.avatar_url,
|
125
|
+
wait_for_response=True,
|
126
|
+
max_retries=0, # we will handle retries ourselves
|
127
|
+
)
|
128
|
+
cache.set(key_last_request, now(), timeout=KILLTRACKER_DISCORD_SEND_DELAY + 30)
|
129
|
+
logger.debug(
|
130
|
+
"%s: Response from Discord for creating message from killmail %d: %s %s %s",
|
131
|
+
name,
|
132
|
+
message.killmail_id,
|
133
|
+
response.status_code,
|
134
|
+
response.headers,
|
135
|
+
response.content,
|
136
|
+
)
|
137
|
+
if not response.status_ok:
|
138
|
+
if response.status_code == HTTPStatus.TOO_MANY_REQUESTS:
|
139
|
+
try:
|
140
|
+
retry_after = int(response.headers["Retry-After"])
|
141
|
+
except KeyError:
|
142
|
+
retry_after = _DEFAULT_429_TIMEOUT
|
143
|
+
retry_at = now() + dt.timedelta(seconds=retry_after)
|
144
|
+
cache.set(key_retry_at, retry_at, timeout=retry_after + 60)
|
145
|
+
raise WebhookRateLimitExhausted(retry_at=retry_at, is_original=True)
|
146
|
+
|
147
|
+
raise HTTPError(response.status_code)
|
148
|
+
|
149
|
+
try:
|
150
|
+
message_id = int(response.content.get("id"))
|
151
|
+
except (AttributeError, ValueError):
|
152
|
+
message_id = 0
|
153
|
+
|
154
|
+
return message_id
|
155
|
+
|
156
|
+
|
157
|
+
def _make_key_last_request(url):
|
158
|
+
return f"killtracker-webhook-last-request-{url}"
|
159
|
+
|
160
|
+
|
161
|
+
def _make_key_retry_at(url):
|
162
|
+
return f"killtracker-webhook-retry-at-{url}"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""Helper functions for core modules."""
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
from typing import Any, Optional
|
5
|
+
|
6
|
+
|
7
|
+
def datetime_or_none(v: Any) -> Optional[dt.datetime]:
|
8
|
+
"""Returns as datetime when it is a datetime else returns None."""
|
9
|
+
if v is None:
|
10
|
+
return None
|
11
|
+
if not isinstance(v, dt.datetime):
|
12
|
+
return None
|
13
|
+
return v
|
@@ -1,14 +1,12 @@
|
|
1
|
-
"""
|
1
|
+
"""Generate Discord messages from tracked killmails."""
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
import json
|
6
|
-
from copy import copy
|
7
5
|
from dataclasses import dataclass
|
8
|
-
from typing import TYPE_CHECKING,
|
6
|
+
from typing import TYPE_CHECKING, Optional
|
9
7
|
|
10
8
|
import dhooks_lite
|
11
|
-
|
9
|
+
import requests
|
12
10
|
|
13
11
|
from eveuniverse.helpers import EveEntityNameResolver
|
14
12
|
from eveuniverse.models import EveEntity, EveSolarSystem
|
@@ -16,89 +14,32 @@ from eveuniverse.models import EveEntity, EveSolarSystem
|
|
16
14
|
from allianceauth.eveonline.evelinks import dotlan, eveimageserver, zkillboard
|
17
15
|
from allianceauth.services.hooks import get_extension_logger
|
18
16
|
from app_utils.django import app_labels
|
19
|
-
from app_utils.json import JSONDateTimeDecoder, JSONDateTimeEncoder
|
20
17
|
from app_utils.logging import LoggerAddTag
|
21
18
|
from app_utils.urls import static_file_absolute_url
|
22
19
|
from app_utils.views import humanize_value
|
23
20
|
|
24
21
|
from killtracker import __title__
|
25
|
-
|
26
|
-
from .
|
22
|
+
from killtracker.core.discord import DiscordMessage
|
23
|
+
from killtracker.core.zkb import ZKB_KILLMAIL_BASEURL, Killmail, TrackerInfo
|
27
24
|
|
28
25
|
if TYPE_CHECKING:
|
29
26
|
from killtracker.models import Tracker
|
30
27
|
|
31
|
-
|
32
28
|
_ICON_SIZE = 128
|
33
29
|
|
34
|
-
|
35
30
|
logger = LoggerAddTag(get_extension_logger(__name__), __title__)
|
36
31
|
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def __post_init__(self):
|
50
|
-
if not self.content and not self.embeds:
|
51
|
-
raise ValueError("Message must have content or embeds to be valid")
|
52
|
-
|
53
|
-
def to_json(self) -> str:
|
54
|
-
"""Converts a Discord message into a JSON object and returns it."""
|
55
|
-
|
56
|
-
if self.embeds:
|
57
|
-
embeds_list = [obj.asdict() for obj in self.embeds]
|
58
|
-
else:
|
59
|
-
embeds_list = None
|
60
|
-
|
61
|
-
message = {}
|
62
|
-
if self.killmail_id:
|
63
|
-
message["killmail_id"] = self.killmail_id
|
64
|
-
if self.content:
|
65
|
-
message["content"] = self.content
|
66
|
-
if embeds_list:
|
67
|
-
message["embeds"] = embeds_list
|
68
|
-
if self.tts:
|
69
|
-
message["tts"] = self.tts
|
70
|
-
if self.username:
|
71
|
-
message["username"] = self.username
|
72
|
-
if self.avatar_url:
|
73
|
-
message["avatar_url"] = self.avatar_url
|
74
|
-
|
75
|
-
return json.dumps(message, cls=JSONDateTimeEncoder)
|
76
|
-
|
77
|
-
@classmethod
|
78
|
-
def from_json(cls, s: str) -> "DiscordMessage":
|
79
|
-
"""Creates a DiscordMessage object from an JSON object and returns it."""
|
80
|
-
message1: dict = json.loads(s, cls=JSONDateTimeDecoder)
|
81
|
-
message2 = copy(message1)
|
82
|
-
if message1.get("embeds"):
|
83
|
-
message2["embeds"] = [
|
84
|
-
dhooks_lite.Embed.from_dict(embed_dict)
|
85
|
-
for embed_dict in message1.get("embeds")
|
86
|
-
]
|
87
|
-
else:
|
88
|
-
message2["embeds"] = None
|
89
|
-
return cls(**message2)
|
90
|
-
|
91
|
-
@classmethod
|
92
|
-
def from_killmail(
|
93
|
-
cls, tracker: Tracker, killmail: Killmail, intro_text: Optional[str] = None
|
94
|
-
) -> "DiscordMessage":
|
95
|
-
"""Creates a DiscordMessage object from a Killmail and returns it."""
|
96
|
-
m = DiscordMessage(
|
97
|
-
killmail_id=killmail.id,
|
98
|
-
content=_create_content(tracker, intro_text),
|
99
|
-
embeds=[_create_embed(tracker, killmail)],
|
100
|
-
)
|
101
|
-
return m
|
33
|
+
def create_discord_message_from_killmail(
|
34
|
+
tracker: Tracker, killmail: Killmail, intro_text: Optional[str] = None
|
35
|
+
) -> "DiscordMessage":
|
36
|
+
"""Creates a Discord message from a Killmail and returns it."""
|
37
|
+
m = DiscordMessage(
|
38
|
+
killmail_id=killmail.id,
|
39
|
+
content=_create_content(tracker, intro_text),
|
40
|
+
embeds=[_create_embed(tracker, killmail)],
|
41
|
+
)
|
42
|
+
return m
|
102
43
|
|
103
44
|
|
104
45
|
@dataclass(frozen=True)
|
@@ -154,7 +95,7 @@ def _create_content(tracker: Tracker, intro_text: Optional[str] = None) -> str:
|
|
154
95
|
for group in tracker.ping_groups.all():
|
155
96
|
try:
|
156
97
|
role = DiscordUser.objects.group_to_role(group) # type: ignore
|
157
|
-
except HTTPError:
|
98
|
+
except requests.exceptions.HTTPError:
|
158
99
|
logger.warning(
|
159
100
|
"Failed to get Discord roles. Can not ping groups.",
|
160
101
|
exc_info=True,
|
@@ -1,5 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
whether their worker is currently shutting down.
|
1
|
+
"""Allows tasks to find out whether their worker is currently shutting down.
|
3
2
|
|
4
3
|
This enables long running tasks to abort early,
|
5
4
|
which helps to speed up a warm worker shutdown.
|
@@ -19,12 +18,12 @@ _TIMEOUT_SECONDS = 120
|
|
19
18
|
logger = LoggerAddTag(get_extension_logger(__name__), __title__)
|
20
19
|
|
21
20
|
|
22
|
-
def
|
21
|
+
def state_reset(hostname: str) -> None:
|
23
22
|
"""Resets the shutting down state for a worker."""
|
24
23
|
cache.delete(_make_key(hostname))
|
25
24
|
|
26
25
|
|
27
|
-
def
|
26
|
+
def state_set(hostname: str) -> None:
|
28
27
|
"""Sets a worker into the shutting down state."""
|
29
28
|
cache.set(_make_key(hostname), "shutting down", timeout=_TIMEOUT_SECONDS)
|
30
29
|
|