aa-killtracker 0.17.0__py3-none-any.whl → 1.0.0a1__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.
Files changed (39) hide show
  1. {aa_killtracker-0.17.0.dist-info → aa_killtracker-1.0.0a1.dist-info}/METADATA +7 -7
  2. {aa_killtracker-0.17.0.dist-info → aa_killtracker-1.0.0a1.dist-info}/RECORD +36 -29
  3. killtracker/__init__.py +1 -1
  4. killtracker/admin.py +13 -8
  5. killtracker/app_settings.py +20 -10
  6. killtracker/apps.py +2 -4
  7. killtracker/core/discord.py +162 -0
  8. killtracker/core/helpers.py +13 -0
  9. killtracker/core/{discord_messages.py → trackers.py} +74 -59
  10. killtracker/core/workers.py +46 -0
  11. killtracker/core/{killmails.py → zkb.py} +97 -72
  12. killtracker/forms.py +1 -1
  13. killtracker/managers.py +3 -3
  14. killtracker/models/trackers.py +7 -10
  15. killtracker/models/webhooks.py +60 -128
  16. killtracker/providers.py +1 -1
  17. killtracker/signals.py +31 -0
  18. killtracker/tasks.py +141 -92
  19. killtracker/tests/core/test_discord.py +184 -0
  20. killtracker/tests/core/test_helpers.py +23 -0
  21. killtracker/tests/core/{test_discord_messages_1.py → test_tracker_1.py} +28 -8
  22. killtracker/tests/core/{test_discord_messages_2.py → test_tracker_2.py} +11 -11
  23. killtracker/tests/core/test_workers.py +49 -0
  24. killtracker/tests/core/{test_killmails.py → test_zkb.py} +109 -52
  25. killtracker/tests/models/test_killmails.py +0 -2
  26. killtracker/tests/models/test_trackers_1.py +24 -24
  27. killtracker/tests/models/test_trackers_2.py +6 -5
  28. killtracker/tests/models/test_webhooks.py +63 -0
  29. killtracker/tests/test_integration.py +25 -12
  30. killtracker/tests/test_tasks.py +161 -92
  31. killtracker/tests/test_utils.py +39 -0
  32. killtracker/tests/testdata/factories.py +1 -1
  33. killtracker/tests/testdata/helpers.py +1 -1
  34. killtracker/tests/utils.py +44 -0
  35. killtracker/exceptions.py +0 -32
  36. killtracker/tests/models/test_webhook.py +0 -150
  37. killtracker/tests/test_exceptions.py +0 -12
  38. {aa_killtracker-0.17.0.dist-info → aa_killtracker-1.0.0a1.dist-info}/WHEEL +0 -0
  39. {aa_killtracker-0.17.0.dist-info → aa_killtracker-1.0.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aa-killtracker
3
- Version: 0.17.0
3
+ Version: 1.0.0a1
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.19.0
24
- Requires-Dist: allianceauth>=3
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.0
27
- Requires-Dist: django-eveuniverse>=1.3
28
- Requires-Dist: redis-simple-mq>=0.5
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
@@ -1,17 +1,20 @@
1
- killtracker/__init__.py,sha256=g8DgOYglXz6Q4YuXHKdw2HdUBMexl4BSn7RbdC-hnGo,355
2
- killtracker/admin.py,sha256=s_O86Ct4prno0cQJE8otumCEqgkZ7H3Q_GFptEOTI_c,14103
3
- killtracker/app_settings.py,sha256=8owiemErRDKxkYY4XB0vFHTI9EqRMjz2SZGTTsEA2ao,3886
4
- killtracker/apps.py,sha256=yYOv4_T_eIiK5M7gr9Dre20WaNQXRKYOdfKyrforNhM,380
1
+ killtracker/__init__.py,sha256=wIg1n4zLhPPBnSm9Q9U1g_FFh1IGzAzKfJQMohSx95s,356
2
+ killtracker/admin.py,sha256=IH04Q1etHUnXSJIhm3bnGfIbSU6lPSSaltLERb-OkYg,14229
3
+ killtracker/app_settings.py,sha256=KqWSS4OVKIa7DmBAa5AbwxFVXw5EQebPfBtPo1UJYgY,4295
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
- killtracker/forms.py,sha256=BqJYcAQ9jbZxTkDsHxrxj0qKKTIGIW0mShbBYdCDlaA,5182
9
- killtracker/managers.py,sha256=JCrfc3yrKD4uHCfIzPu7y2HF-VIJm1yskdc-CFEf7V4,4390
10
- killtracker/providers.py,sha256=smkZe_rKHkZeWFzJ0RhIhJVcbrZUpLYR5fKCge-dpXY,348
11
- killtracker/tasks.py,sha256=GNdX1BGTWgR_wD_Eep6ibPHqC-PFIeVXDiyIJE7vW-E,8109
7
+ killtracker/forms.py,sha256=SUM0U-Sa6FPcGVpsYJbVxHfEtf7hb1Qc_l92Zfa1OLk,5193
8
+ killtracker/managers.py,sha256=hrmpiuMzK-7p-AsT4ee0mMEhQv4b57AGmhlRTxEl7wk,4416
9
+ killtracker/providers.py,sha256=3DiOlklBq5kLGDV9nMlMbP2-S8AodNgR6gtB4cWWCUo,358
10
+ killtracker/signals.py,sha256=rFCg-WYMVPmAhuqX8KHNaicsP1nyj_HYA2O4Qr8wqhk,892
11
+ killtracker/tasks.py,sha256=9ZqItmm2tlzVA-lelNTyzpPuBYCUvfZR2nCV16Xu6fA,9267
12
12
  killtracker/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- killtracker/core/discord_messages.py,sha256=mOKiMWq-3WnaSUFbfgVmlkAedGTZ7OWcVxLp7n58-Z0,14132
14
- killtracker/core/killmails.py,sha256=v7ciMSPHyIg4L4d-Q8UECuqJShfxfd3g_sdfRnKKSPY,20499
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
15
18
  killtracker/management/commands/killtracker_load_eve.py,sha256=5P2wr6LU-EMl9_gG-DKP2yw3eFXOU7ApeuGAWEbvCKk,1159
16
19
  killtracker/migrations/0001_initial_new.py,sha256=WszI5DmNFtk45IZ2Zul3_Ak-VXmye9ffJKFAsGIxoXA,27818
17
20
  killtracker/migrations/0001_squashed_all.py,sha256=KXwOcRayjGGGmleg7-aTcXeIi-92Mn4BaGkEY3iE9Vc,26221
@@ -29,39 +32,43 @@ killtracker/migrations/0009_remove_old_models.py,sha256=bDIz0hywpFKbHYhdXFXzra5D
29
32
  killtracker/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
33
  killtracker/models/__init__.py,sha256=WL6VD9MK7EsZot_RLPz0nX8WxJkp7RtQpw_mYpnVqu8,188
31
34
  killtracker/models/killmails.py,sha256=CCdrHkwUSoTOGqavG-TpMxDRleF9UEacOTui8v3-VT8,5181
32
- killtracker/models/trackers.py,sha256=t3pm29UW_0JqBgHbU0Xx1qgLyft7_EjNtKCJ37OM_fE,31188
33
- killtracker/models/webhooks.py,sha256=hh1mYE4pBU26YFawkv1z-j4uyX8aT25B0crOTsocSSY,8268
35
+ killtracker/models/trackers.py,sha256=wo6KWl1GOASOquTTdAeo5DaZgRzKai1ek1GjQ49oOhY,31154
36
+ killtracker/models/webhooks.py,sha256=6S1wXwlOMth56oU0hsYH5PiimECMwZyLXiKok2yQo_g,5850
34
37
  killtracker/static/killtracker/killtracker_logo.png,sha256=3jc9zmYHqP60Np5piP5RfkX0_II-315DNjZ4FRGbqKc,74625
35
38
  killtracker/static/killtracker/zkb_icon.png,sha256=wuVfgyTbTs9qS4KGbDAH3Q9KVPgHqYV5eaVrjEmTjsE,328
36
39
  killtracker/templates/admin/killtracker/tracker/killmail_test.html,sha256=TI2ON8qf9pW4rX6G7pT990ZadkipwncQgGUC0FPcWQY,649
37
40
  killtracker/tests/__init__.py,sha256=CEt5qqp6ptDrx1lwuA6epZ5bzh1CwUJYXeMZqWK9jT0,278
38
41
  killtracker/tests/test_admin.py,sha256=XFAlF_Q4h5Z_agkmRtB30GmCayqQPblLolFrN41xu7Q,5659
39
42
  killtracker/tests/test_admin_2.py,sha256=rP0EscM9qUil_ZuXute3R8TQY2P5XSjmxHLbziCGcVw,1140
40
- killtracker/tests/test_exceptions.py,sha256=wheTwm0JOBqYVI-DJM4KK2usFAZT1Q-O52VMOnSAz8Y,326
41
- killtracker/tests/test_integration.py,sha256=EX0IQGU_Vwsmjeoqeprg13VuW8AT9I8CT1bm3_cvfPs,3226
42
- killtracker/tests/test_tasks.py,sha256=ZDoCUBd-1kSOKrvSeTrlDaSGFmzHieT5ef0iBz6nr2I,13929
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
43
47
  killtracker/tests/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- killtracker/tests/core/test_discord_messages_1.py,sha256=1cYpj4E9pNqQOeE7yWeV1mme-JYGIHfSIibOauEHi88,3101
45
- killtracker/tests/core/test_discord_messages_2.py,sha256=AppA_FUFQA5I35UKjaEwn_ZQcnscqillR1SIo1unwJg,4774
46
- killtracker/tests/core/test_killmails.py,sha256=OFV6EbERi0UyMzXblLsNoJrPbLSIgtA27Wt9vGwOYd8,14349
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
47
54
  killtracker/tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- killtracker/tests/models/test_killmails.py,sha256=0ytwIGbTPb1OTzmK8B9VnR1ylxMuaYtp2tvCXzNOwNc,8293
49
- killtracker/tests/models/test_trackers_1.py,sha256=_ARZKBrQz0QF6_OZEOFOsTqNdfflz7wWenAT-T35nqU,42771
50
- killtracker/tests/models/test_trackers_2.py,sha256=FmtaWBjV32NlIjYPaG5Kh87oES5ChoDMVJ4ol2uWopE,5404
51
- killtracker/tests/models/test_webhook.py,sha256=E4BgmQw9mI5ZenV6ncYHbBonlEeWvvdilAS8IKFkbjw,5246
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
52
59
  killtracker/tests/testdata/__init__.py,sha256=9aQhf8V-DseZMWjJ_QMXOba6CypoHFSVpYRZEUh-oFc,212
53
60
  killtracker/tests/testdata/create_eveuniverse.py,sha256=Ewr5OBiQwjK1kwDCRh3A3f0Z-CCPi8Gp8dpsPgVAU4A,966
54
61
  killtracker/tests/testdata/evealliances.json,sha256=i6udrtUnWQjn71Iw17WWz9Eb1fdGUNhI1UYLt0hZHog,307
55
62
  killtracker/tests/testdata/evecorporations.json,sha256=Hk4WuplZl69rQcquOU-15lgUyvHRMZW0fnYfKz8oeWw,1229
56
63
  killtracker/tests/testdata/eveentities.json,sha256=0yStlAMEhednEiMKvKOYFsyRovtsGxw6IpWAyvnQXv4,1459
57
64
  killtracker/tests/testdata/eveuniverse.json,sha256=JagrJ-asKlkxrqj3dtAth6d5bhIpeEmlvBHgXjLTMSI,36117
58
- killtracker/tests/testdata/factories.py,sha256=WqmIthF6kgrdQrV5CSlub4hBYo42lC_8UORzZ8sGAp0,11019
59
- killtracker/tests/testdata/helpers.py,sha256=5T35ITnh16pTswR1O4t4IdVSpokC3x8_Me_vk3CgBB8,4741
65
+ killtracker/tests/testdata/factories.py,sha256=MEJLfslB13J_UnN7TXASL6poH1fkQ1NW5dQX_xtfPvA,11013
66
+ killtracker/tests/testdata/helpers.py,sha256=bsHv2HlbORuw5kU8qZ806nXPM0hqNUuj0qVQCm4KoZ8,4735
60
67
  killtracker/tests/testdata/killmails.json,sha256=HKfpXQJ_ikymBSewh4wWixYpLwC3kNBqi04cCms1AjA,45366
61
68
  killtracker/tests/testdata/load_eveuniverse.py,sha256=FitVc12E_ob56ezK5Y1q07Kbyc7N8poHiCNaLRGU4Jg,388
62
69
  killtracker/tools/drop_tables_killtracker.sql,sha256=fJrGY23NKRunpYC3hI-klFkWub4sBuhfP8TaT__zjPg,1726
63
70
  killtracker/tools/generate_conditions_text.py,sha256=LxAtTQIX-IbuqaNDMV1XHhq5AbrrlwxLWgXa1M7DWQo,767
64
- aa_killtracker-0.17.0.dist-info/licenses/LICENSE,sha256=XZiwB_S_40_HhnvLg5xvtBb3g1oGjPrk0rpFwk8iInE,1070
65
- aa_killtracker-0.17.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
66
- aa_killtracker-0.17.0.dist-info/METADATA,sha256=ixjtGgUPTMWb4glIZb_EmI-WZMdl9R9BLLR-aUlV9Gg,13927
67
- aa_killtracker-0.17.0.dist-info/RECORD,,
71
+ aa_killtracker-1.0.0a1.dist-info/licenses/LICENSE,sha256=XZiwB_S_40_HhnvLg5xvtBb3g1oGjPrk0rpFwk8iInE,1070
72
+ aa_killtracker-1.0.0a1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
73
+ aa_killtracker-1.0.0a1.dist-info/METADATA,sha256=D8mZwY0eM39wkvv8FcnkgaomKJ6KbQhb6cxew0iVqEo,13941
74
+ aa_killtracker-1.0.0a1.dist-info/RECORD,,
killtracker/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  # pylint: disable = invalid-name
4
4
  default_app_config = "killtracker.apps.KillmailsConfig"
5
5
 
6
- __version__ = "0.17.0"
6
+ __version__ = "1.0.0a1"
7
7
  __title__ = "Killtracker"
8
8
 
9
9
  APP_NAME = "aa-killtracker"
killtracker/admin.py CHANGED
@@ -4,16 +4,21 @@
4
4
 
5
5
 
6
6
  from django.contrib import admin
7
+ from django.db.models import QuerySet
7
8
  from django.http import HttpResponseRedirect
8
9
  from django.shortcuts import render
9
10
  from django.utils.safestring import mark_safe
10
11
 
11
12
  from allianceauth import NAME as site_header
12
13
 
13
- from . import tasks
14
- from .core.killmails import Killmail
15
- from .forms import TrackerAdminForm, TrackerAdminKillmailIdForm, field_nice_display
16
- from .models import EveKillmail, EveKillmailAttacker, Tracker, Webhook
14
+ from killtracker import tasks
15
+ from killtracker.core.zkb import Killmail
16
+ from killtracker.forms import (
17
+ TrackerAdminForm,
18
+ TrackerAdminKillmailIdForm,
19
+ field_nice_display,
20
+ )
21
+ from killtracker.models import EveKillmail, EveKillmailAttacker, Tracker, Webhook
17
22
 
18
23
 
19
24
  class EveKillmailAttackerInline(admin.TabularInline):
@@ -46,17 +51,17 @@ class WebhookAdmin(admin.ModelAdmin):
46
51
  list_filter = ("is_enabled",)
47
52
  ordering = ("name",)
48
53
 
49
- def _messages_in_queue(self, obj):
50
- return obj.main_queue.size()
54
+ def _messages_in_queue(self, obj: Webhook):
55
+ return obj.messages_queued()
51
56
 
52
57
  actions = ["send_test_message", "purge_messages"]
53
58
 
54
59
  @admin.display(description="Purge queued messages of selected webhooks")
55
- def purge_messages(self, request, queryset):
60
+ def purge_messages(self, request, queryset: QuerySet[Webhook]):
56
61
  actions_count = 0
57
62
  killmails_deleted = 0
58
63
  for webhook in queryset:
59
- killmails_deleted += webhook.main_queue.clear()
64
+ killmails_deleted += webhook.delete_queued_messages()
60
65
  actions_count += 1
61
66
  self.message_user(
62
67
  request,
@@ -6,20 +6,15 @@ KILLTRACKER_REDISQ_LOCK_TIMEOUT = clean_setting("KILLTRACKER_REDISQ_LOCK_TIMEOUT
6
6
  """Timeout for lock to ensure atomic access to ZKB RedisQ."""
7
7
 
8
8
  KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER = clean_setting(
9
- "KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER", 60
9
+ "KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER", 600
10
10
  )
11
11
  """Ignore killmails that are older than the given number in minutes
12
12
  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", 100
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.
@@ -62,11 +57,14 @@ when creating trackers.
62
57
  #####################
63
58
  # INTERNAL SETTINGS
64
59
 
65
- KILLTRACKER_REDISQ_TTW = clean_setting("KILLTRACKER_REDISQ_TTW", 5)
60
+ KILLTRACKER_REDISQ_TTW = clean_setting("KILLTRACKER_REDISQ_TTW", 1)
66
61
  """Max duration to wait for new killmails from redisq in seconds."""
67
62
 
68
63
  KILLTRACKER_TASKS_TIMEOUT = clean_setting("KILLTRACKER_TASKS_TIMEOUT", 1_800)
69
- """Tasks hard timeout."""
64
+ """Tasks hard timeout in seconds."""
65
+
66
+ KILLTRACKER_RUN_TIMEOUT = clean_setting("KILLTRACKER_RUN_TIMEOUT", 55)
67
+ """Timeout for killtracker run in seconds."""
70
68
 
71
69
  KILLTRACKER_DISCORD_SEND_DELAY = clean_setting(
72
70
  "KILLTRACKER_DISCORD_SEND_DELAY", default_value=2, min_value=1, max_value=900
@@ -100,9 +98,21 @@ KILLTRACKER_STORAGE_KILLMAILS_LIFETIME = clean_setting(
100
98
  )
101
99
  """Max lifetime of killmails in temporary storage in seconds."""
102
100
 
103
- KILLTRACKER_ZKB_REQUEST_DELAY = clean_setting("KILLTRACKER_ZKB_REQUEST_DELAY", 500)
101
+ KILLTRACKER_ZKB_REQUEST_DELAY = clean_setting(
102
+ "KILLTRACKER_ZKB_REQUEST_DELAY", default_value=500, min_value=500
103
+ )
104
104
  """Delay between subsequent calls to ZKB API in milliseconds.
105
105
 
106
106
  This delay ensures the app does not breach the CloudFlare rate limit of currently
107
107
  two (2) requests per second per IP address.
108
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."""
killtracker/apps.py CHANGED
@@ -9,7 +9,5 @@ class KillmailsConfig(AppConfig):
9
9
  verbose_name = f"Killtracker v{__version__}"
10
10
 
11
11
  def ready(self) -> None:
12
- from . import checks # noqa: F401 pylint: disable=unused-import
13
- from .core.killmails import Killmail
14
-
15
- Killmail.reset_lock_key()
12
+ import killtracker.checks # noqa: F401 pylint: disable=unused-import
13
+ import killtracker.signals # noqa: F401 pylint: disable=unused-import
@@ -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