aa-rss-to-discord 2.3.32.3.3__py3-none-any.whl → 2.4.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.
Files changed (32) hide show
  1. aa_rss_to_discord/__init__.py +3 -2
  2. aa_rss_to_discord/apps.py +5 -2
  3. aa_rss_to_discord/constants.py +1 -1
  4. aa_rss_to_discord/locale/cs_CZ/LC_MESSAGES/django.po +2 -2
  5. aa_rss_to_discord/locale/de/LC_MESSAGES/django.po +2 -2
  6. aa_rss_to_discord/locale/django.pot +3 -3
  7. aa_rss_to_discord/locale/es/LC_MESSAGES/django.po +2 -2
  8. aa_rss_to_discord/locale/fr_FR/LC_MESSAGES/django.po +2 -2
  9. aa_rss_to_discord/locale/it_IT/LC_MESSAGES/django.po +2 -2
  10. aa_rss_to_discord/locale/ja/LC_MESSAGES/django.po +2 -2
  11. aa_rss_to_discord/locale/ko_KR/LC_MESSAGES/django.po +2 -2
  12. aa_rss_to_discord/locale/nl_NL/LC_MESSAGES/django.po +2 -2
  13. aa_rss_to_discord/locale/pl_PL/LC_MESSAGES/django.po +2 -2
  14. aa_rss_to_discord/locale/ru/LC_MESSAGES/django.po +2 -2
  15. aa_rss_to_discord/locale/sk/LC_MESSAGES/django.po +2 -2
  16. aa_rss_to_discord/locale/uk/LC_MESSAGES/django.mo +0 -0
  17. aa_rss_to_discord/locale/uk/LC_MESSAGES/django.po +7 -7
  18. aa_rss_to_discord/locale/zh_Hans/LC_MESSAGES/django.po +2 -2
  19. aa_rss_to_discord/managers.py +1 -1
  20. aa_rss_to_discord/providers.py +43 -0
  21. aa_rss_to_discord/tasks.py +59 -135
  22. aa_rss_to_discord/tests/__init__.py +41 -0
  23. aa_rss_to_discord/tests/test_auth_hooks.py +49 -0
  24. aa_rss_to_discord/tests/test_managers.py +32 -0
  25. aa_rss_to_discord/tests/test_models.py +165 -0
  26. aa_rss_to_discord/tests/test_providers.py +99 -0
  27. aa_rss_to_discord/tests/test_tasks.py +176 -0
  28. {aa_rss_to_discord-2.3.32.3.3.dist-info → aa_rss_to_discord-2.4.0.dist-info}/METADATA +11 -9
  29. aa_rss_to_discord-2.4.0.dist-info/RECORD +51 -0
  30. {aa_rss_to_discord-2.3.32.3.3.dist-info → aa_rss_to_discord-2.4.0.dist-info}/WHEEL +1 -1
  31. aa_rss_to_discord-2.3.32.3.3.dist-info/RECORD +0 -44
  32. {aa_rss_to_discord-2.3.32.3.3.dist-info → aa_rss_to_discord-2.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,49 @@
1
+ # Standard Library
2
+ from unittest import mock
3
+
4
+ # AA RSS to Discord
5
+ from aa_rss_to_discord.auth_hooks import register_cogs
6
+ from aa_rss_to_discord.tests import BaseTestCase
7
+
8
+
9
+ class TestAuthHooks(BaseTestCase):
10
+ """
11
+ Test the auth hooks
12
+ """
13
+
14
+ def test_registers_discord_cogs_hook_correctly(self):
15
+ """
16
+ Tests that the discord cogs hook is registered correctly.
17
+
18
+ :return:
19
+ :rtype:
20
+ """
21
+
22
+ result = register_cogs()
23
+
24
+ self.assertIn("aa_rss_to_discord.discordbot.cogs.rss", result)
25
+
26
+ def test_returns_list_of_cogs(self):
27
+ """
28
+ Tests that the register_cogs function returns a list.
29
+
30
+ :return:
31
+ :rtype:
32
+ """
33
+
34
+ result = register_cogs()
35
+
36
+ self.assertIsInstance(result, list)
37
+
38
+ def test_handles_empty_hook_registration_gracefully(self):
39
+ """
40
+ Tests that the register_cogs function handles an empty hook registration gracefully.
41
+
42
+ :return:
43
+ :rtype:
44
+ """
45
+
46
+ with mock.patch("allianceauth.hooks.register", return_value=None):
47
+ result = register_cogs()
48
+
49
+ self.assertIsNotNone(result)
@@ -0,0 +1,32 @@
1
+ # AA RSS to Discord
2
+ from aa_rss_to_discord.models import RssFeeds
3
+ from aa_rss_to_discord.tests import BaseTestCase
4
+
5
+
6
+ class TestRssFeedManager(BaseTestCase):
7
+ """
8
+ Test the RSS Feed Manager
9
+ """
10
+
11
+ def test_returns_only_enabled_feeds(self):
12
+ RssFeeds.objects.create(name="Feed 1", enabled=True)
13
+ RssFeeds.objects.create(name="Feed 2", enabled=False)
14
+ RssFeeds.objects.create(name="Feed 3", enabled=True)
15
+
16
+ result = RssFeeds.objects.select_enabled()
17
+
18
+ self.assertEqual(result.count(), 2)
19
+ self.assertTrue(all(feed.enabled for feed in result))
20
+
21
+ def test_handles_no_enabled_feeds_gracefully(self):
22
+ RssFeeds.objects.create(name="Feed 1", enabled=False)
23
+ RssFeeds.objects.create(name="Feed 2", enabled=False)
24
+
25
+ result = RssFeeds.objects.select_enabled()
26
+
27
+ self.assertEqual(result.count(), 0)
28
+
29
+ def test_handles_empty_database_gracefully(self):
30
+ result = RssFeeds.objects.select_enabled()
31
+
32
+ self.assertEqual(result.count(), 0)
@@ -0,0 +1,165 @@
1
+ # Third Party
2
+ from aadiscordbot.models import Channels, Servers
3
+
4
+ # AA RSS to Discord
5
+ from aa_rss_to_discord.models import LastItem, RssFeeds
6
+ from aa_rss_to_discord.tests import BaseTestCase
7
+
8
+
9
+ class TestRssFeeds(BaseTestCase):
10
+ """
11
+ Test the RssFeeds model.
12
+ """
13
+
14
+ def test_saves_and_retrieves_rss_feed_correctly(self):
15
+ """
16
+ Tests that an RSS feed can be saved and retrieved correctly.
17
+
18
+ :return:
19
+ :rtype:
20
+ """
21
+
22
+ feed = RssFeeds.objects.create(
23
+ name="Feed 1", url="http://example.com/rss", enabled=True
24
+ )
25
+ retrieved_feed = RssFeeds.objects.get(id=feed.id)
26
+
27
+ self.assertEqual(retrieved_feed.name, "Feed 1")
28
+ self.assertEqual(retrieved_feed.url, "http://example.com/rss")
29
+ self.assertTrue(retrieved_feed.enabled)
30
+
31
+ def test_handles_null_discord_channel_gracefully(self):
32
+ """
33
+ Tests that the model handles a null discord_channel gracefully.
34
+
35
+ :return:
36
+ :rtype:
37
+ """
38
+
39
+ feed = RssFeeds.objects.create(
40
+ name="Feed 2", url="http://example.com/rss", discord_channel=None
41
+ )
42
+
43
+ self.assertIsNone(feed.discord_channel)
44
+
45
+ def test_string_representation_includes_name_and_channel(self):
46
+ """
47
+ Tests that the string representation of the RssFeeds model
48
+
49
+ :return:
50
+ :rtype:
51
+ """
52
+ server = Servers.objects.create(server=1, name="Test Server")
53
+ channel = Channels.objects.create(name="General", server=server, channel=1)
54
+ feed = RssFeeds.objects.create(name="Feed 3", discord_channel=channel)
55
+
56
+ self.assertEqual(
57
+ str(feed), 'RSS Feed "Feed 3" for channel "General" On "Test Server"'
58
+ )
59
+
60
+ def test_allows_disabling_rss_feed(self):
61
+ """
62
+ Tests that an RSS feed can be disabled.
63
+
64
+ :return:
65
+ :rtype:
66
+ """
67
+ feed = RssFeeds.objects.create(name="Feed 4", enabled=True)
68
+ feed.enabled = False
69
+ feed.save()
70
+
71
+ updated_feed = RssFeeds.objects.get(id=feed.id)
72
+
73
+ self.assertFalse(updated_feed.enabled)
74
+
75
+ def test_handles_empty_name_and_url_gracefully(self):
76
+ """
77
+ Tests that the model handles empty name and url fields gracefully.
78
+
79
+ :return:
80
+ :rtype:
81
+ """
82
+
83
+ feed = RssFeeds.objects.create(name="", url="")
84
+
85
+ self.assertEqual(feed.name, "")
86
+ self.assertEqual(feed.url, "")
87
+
88
+
89
+ class TestLastItem(BaseTestCase):
90
+ """
91
+ Test the LastItem model.
92
+ """
93
+
94
+ def test_string_representation_includes_item_title(self):
95
+ """
96
+ Tests that the string representation of the LastItem model includes the item title.
97
+
98
+ :return:
99
+ :rtype:
100
+ """
101
+
102
+ feed = RssFeeds.objects.create(name="Feed 1", url="http://example.com/rss")
103
+ item = LastItem.objects.create(
104
+ rss_feed=feed,
105
+ rss_item_title="Sample Item",
106
+ rss_item_link="http://example.com/item",
107
+ )
108
+
109
+ self.assertEqual(str(item), 'RSS Entry "Sample Item"')
110
+
111
+ def test_handles_empty_item_title_gracefully(self):
112
+ """
113
+ Tests that the model handles an empty item title gracefully.
114
+
115
+ :return:
116
+ :rtype:
117
+ """
118
+
119
+ feed = RssFeeds.objects.create(name="Feed 2", url="http://example.com/rss")
120
+ item = LastItem.objects.create(
121
+ rss_feed=feed,
122
+ rss_item_title="",
123
+ rss_item_link="http://example.com/item",
124
+ )
125
+
126
+ self.assertEqual(str(item), 'RSS Entry ""')
127
+
128
+ def test_saves_and_retrieves_last_item_correctly(self):
129
+ """
130
+ Tests that a LastItem can be saved and retrieved correctly.
131
+
132
+ :return:
133
+ :rtype:
134
+ """
135
+
136
+ feed = RssFeeds.objects.create(name="Feed 3", url="http://example.com/rss")
137
+ item = LastItem.objects.create(
138
+ rss_feed=feed,
139
+ rss_item_time="2023-10-01T12:00:00Z",
140
+ rss_item_title="Item Title",
141
+ rss_item_link="http://example.com/item",
142
+ rss_item_guid="12345",
143
+ )
144
+ retrieved_item = LastItem.objects.get(id=item.id)
145
+
146
+ self.assertEqual(retrieved_item.rss_item_time, "2023-10-01T12:00:00Z")
147
+ self.assertEqual(retrieved_item.rss_item_title, "Item Title")
148
+ self.assertEqual(retrieved_item.rss_item_link, "http://example.com/item")
149
+ self.assertEqual(retrieved_item.rss_item_guid, "12345")
150
+
151
+ def test_handles_null_guid_gracefully(self):
152
+ """
153
+ Tests that the model handles a null guid gracefully.
154
+
155
+ :return:
156
+ :rtype:
157
+ """
158
+
159
+ feed = RssFeeds.objects.create(name="Feed 4", url="http://example.com/rss")
160
+ item = LastItem.objects.create(
161
+ rss_feed=feed,
162
+ rss_item_guid="",
163
+ )
164
+
165
+ self.assertEqual(item.rss_item_guid, "")
@@ -0,0 +1,99 @@
1
+ """
2
+ Test for the providers module.
3
+ """
4
+
5
+ # Standard Library
6
+ import logging
7
+
8
+ # AA RSS to Discord
9
+ # AA Intel Tool
10
+ from aa_rss_to_discord.providers import AppLogger
11
+ from aa_rss_to_discord.tests import BaseTestCase
12
+
13
+
14
+ class TestAppLogger(BaseTestCase):
15
+ """
16
+ Test the AppLogger provider.
17
+ """
18
+
19
+ def test_adds_prefix_to_log_message(self):
20
+ """
21
+ Tests that the AppLogger correctly adds a prefix to log messages.
22
+
23
+ :return:
24
+ :rtype:
25
+ """
26
+
27
+ logger = logging.getLogger("test_logger")
28
+ app_logger = AppLogger(logger, "PREFIX")
29
+
30
+ with self.assertLogs("test_logger", level="INFO") as log:
31
+ app_logger.info("This is a test message")
32
+
33
+ self.assertIn("[PREFIX] This is a test message", log.output[0])
34
+
35
+ def test_handles_empty_prefix(self):
36
+ """
37
+ Tests that the AppLogger handles an empty prefix correctly.
38
+
39
+ :return:
40
+ :rtype:
41
+ """
42
+
43
+ logger = logging.getLogger("test_logger")
44
+ app_logger = AppLogger(logger, "")
45
+
46
+ with self.assertLogs("test_logger", level="INFO") as log:
47
+ app_logger.info("Message without prefix")
48
+
49
+ self.assertIn("Message without prefix", log.output[0])
50
+
51
+ def test_handles_non_string_prefix(self):
52
+ """
53
+ Tests that the AppLogger handles a non-string prefix correctly.
54
+
55
+ :return:
56
+ :rtype:
57
+ """
58
+
59
+ logger = logging.getLogger("test_logger")
60
+ app_logger = AppLogger(logger, 123)
61
+
62
+ with self.assertLogs("test_logger", level="INFO") as log:
63
+ app_logger.info("Message with numeric prefix")
64
+
65
+ self.assertIn("[123] Message with numeric prefix", log.output[0])
66
+
67
+ def test_handles_special_characters_in_prefix(self):
68
+ """
69
+ Tests that the AppLogger handles special characters in the prefix correctly.
70
+
71
+ :return:
72
+ :rtype:
73
+ """
74
+
75
+ logger = logging.getLogger("test_logger")
76
+ app_logger = AppLogger(logger, "!@#$%^&*()")
77
+
78
+ with self.assertLogs("test_logger", level="INFO") as log:
79
+ app_logger.info("Message with special characters in prefix")
80
+
81
+ self.assertIn(
82
+ "[!@#$%^&*()] Message with special characters in prefix", log.output[0]
83
+ )
84
+
85
+ def test_handles_empty_message(self):
86
+ """
87
+ Tests that the AppLogger handles an empty log message correctly.
88
+
89
+ :return:
90
+ :rtype:
91
+ """
92
+
93
+ logger = logging.getLogger("test_logger")
94
+ app_logger = AppLogger(logger, "PREFIX")
95
+
96
+ with self.assertLogs("test_logger", level="INFO") as log:
97
+ app_logger.info("")
98
+
99
+ self.assertIn("[PREFIX] ", log.output[0])
@@ -0,0 +1,176 @@
1
+ """
2
+ Test cases for the fetch_rss task
3
+ """
4
+
5
+ # Standard Library
6
+ from unittest.mock import MagicMock, patch
7
+
8
+ # AA RSS to Discord
9
+ from aa_rss_to_discord.models import LastItem
10
+ from aa_rss_to_discord.tasks import fetch_rss
11
+ from aa_rss_to_discord.tests import BaseTestCase
12
+
13
+
14
+ class TestFetchRss(BaseTestCase):
15
+ """
16
+ Test fetch_rss task
17
+ """
18
+
19
+ def test_logs_warning_if_no_rss_feeds_found(self):
20
+ """
21
+ Test that a warning is logged if no RSS feeds are found.
22
+
23
+ :return:
24
+ :rtype:
25
+ """
26
+
27
+ with (
28
+ patch(
29
+ "aa_rss_to_discord.tasks.RssFeeds.objects.select_enabled",
30
+ return_value=[],
31
+ ),
32
+ patch("aa_rss_to_discord.tasks.logger.debug") as mock_debug,
33
+ ):
34
+ fetch_rss()
35
+ mock_debug.assert_called_once_with("No RSS feeds found to parse.")
36
+
37
+ def test_processes_valid_rss_feed_entry(self):
38
+ """
39
+ Test that a valid RSS feed entry is processed and sent to Discord.
40
+
41
+ :return:
42
+ :rtype:
43
+ """
44
+
45
+ discord_channel = MagicMock()
46
+ discord_channel.channel = 12345
47
+
48
+ rss_feed = MagicMock()
49
+ rss_feed.name = "Valid Feed"
50
+ rss_feed.url = "http://example.com/rss"
51
+ rss_feed.discord_channel = discord_channel
52
+
53
+ # Use a MagicMock that acts like feedparser entry with both dict and attribute access
54
+ feed_entry = MagicMock()
55
+ feed_entry.get = lambda key, default=None: {
56
+ "title": "Valid Entry",
57
+ "link": "http://example.com/valid-entry",
58
+ "published": "2023-10-01T12:00:00Z",
59
+ "id": "entry-1",
60
+ }.get(key, default)
61
+ feed_entry.updated = (
62
+ "2023-10-01T12:00:00Z" # Fallback if 'published' is missing
63
+ )
64
+
65
+ parsed_feed = MagicMock()
66
+ parsed_feed.entries = [feed_entry]
67
+
68
+ with (
69
+ patch(
70
+ "aa_rss_to_discord.tasks.RssFeeds.objects.select_enabled",
71
+ return_value=[rss_feed],
72
+ ),
73
+ patch(
74
+ "aa_rss_to_discord.tasks.feedparser.parse",
75
+ return_value=parsed_feed,
76
+ ),
77
+ patch(
78
+ "aa_rss_to_discord.tasks.LastItem.objects.get",
79
+ side_effect=LastItem.DoesNotExist,
80
+ ),
81
+ patch(
82
+ "aa_rss_to_discord.tasks.LastItem.objects.update_or_create",
83
+ ),
84
+ patch(
85
+ "aa_rss_to_discord.tasks.send_message",
86
+ ) as mock_send_message,
87
+ ):
88
+ fetch_rss()
89
+
90
+ mock_send_message.assert_called_once()
91
+ call_args = mock_send_message.call_args
92
+ self.assertEqual(call_args.kwargs.get("channel_id"), 12345)
93
+ message = call_args.kwargs.get("message", "")
94
+ self.assertIn("http://example.com/valid-entry", message)
95
+ self.assertIn("Valid Feed", message)
96
+
97
+ def test_skips_duplicate_rss_feed_entry(self):
98
+ """
99
+ Test that a duplicate RSS feed entry is skipped.
100
+
101
+ :return:
102
+ :rtype:
103
+ """
104
+
105
+ rss_feed = MagicMock()
106
+ rss_feed.name = "Duplicate Feed"
107
+ rss_feed.url = "http://example.com/rss"
108
+
109
+ # Create feed entry that supports both dict-style and attribute access
110
+ feed_entry = MagicMock()
111
+ feed_entry.get = lambda key, default=None: {
112
+ "title": "Duplicate Entry",
113
+ "link": "http://example.com/duplicate-entry",
114
+ "published": "2023-10-01T12:00:00Z",
115
+ "id": "entry-1",
116
+ }.get(key, default)
117
+ feed_entry.updated = "2023-10-01T12:00:00Z"
118
+
119
+ last_item = MagicMock(
120
+ rss_item_time="2023-10-01T12:00:00Z",
121
+ rss_item_title="Duplicate Entry",
122
+ rss_item_link="http://example.com/duplicate-entry",
123
+ rss_item_guid="entry-1",
124
+ )
125
+
126
+ with (
127
+ patch(
128
+ "aa_rss_to_discord.tasks.RssFeeds.objects.select_enabled",
129
+ return_value=[rss_feed],
130
+ ),
131
+ patch(
132
+ "aa_rss_to_discord.tasks.feedparser.parse",
133
+ return_value=MagicMock(entries=[feed_entry]),
134
+ ),
135
+ patch(
136
+ "aa_rss_to_discord.tasks.LastItem.objects.get",
137
+ return_value=last_item,
138
+ ),
139
+ patch("aa_rss_to_discord.tasks.logger.debug") as mock_debug,
140
+ patch("aa_rss_to_discord.tasks.send_message") as mock_send_message,
141
+ ):
142
+ fetch_rss()
143
+
144
+ mock_send_message.assert_not_called()
145
+ mock_debug.assert_any_call(
146
+ 'News item "Duplicate Entry" for RSS Feed "Duplicate Feed" has already been posted to your Discord'
147
+ )
148
+
149
+ def test_skips_rss_feed_with_no_entries(self):
150
+ """
151
+ Test that an RSS feed with no entries is skipped.
152
+
153
+ :return:
154
+ :rtype:
155
+ """
156
+
157
+ rss_feed = MagicMock()
158
+ rss_feed.name = "Empty Feed"
159
+ rss_feed.url = "http://example.com/rss"
160
+
161
+ with (
162
+ patch(
163
+ "aa_rss_to_discord.tasks.RssFeeds.objects.select_enabled",
164
+ return_value=[rss_feed],
165
+ ),
166
+ patch(
167
+ "aa_rss_to_discord.tasks.feedparser.parse",
168
+ return_value=MagicMock(entries=[]),
169
+ ),
170
+ patch("aa_rss_to_discord.tasks.logger.debug") as mock_debug,
171
+ ):
172
+ fetch_rss()
173
+
174
+ mock_debug.assert_any_call(
175
+ 'Error processing feed "Empty Feed": list index out of range'
176
+ )
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aa-rss-to-discord
3
- Version: 2.3.32.3.3
3
+ Version: 2.4.0
4
4
  Summary: Alliance Auth module to post news from RSS feeds to your Discord
5
5
  Project-URL: Changelog, https://github.com/ppfeufer/aa-rss-to-discord/blob/master/CHANGELOG.md
6
+ Project-URL: Codecov, https://codecov.io/gh/ppfeufer/aa-rss-to-discord
7
+ Project-URL: Discord, https://discord.gg/fjnHAmk
6
8
  Project-URL: Documentation, https://github.com/ppfeufer/aa-rss-to-discord/blob/master/README.md
7
- Project-URL: Donations, https://ko-fi.com/ppfeufer
8
- Project-URL: Homepage, https://github.com/ppfeufer/aa-rss-to-discord
9
- Project-URL: Source, https://github.com/ppfeufer/aa-rss-to-discord.git
9
+ Project-URL: Donation, https://ko-fi.com/ppfeufer
10
+ Project-URL: GitHub, https://github.com/ppfeufer/aa-rss-to-discord
10
11
  Project-URL: Tracker, https://github.com/ppfeufer/aa-rss-to-discord/issues
12
+ Project-URL: Translation, https://weblate.ppfeufer.de/projects/alliance-auth-apps/aa-rss-to-discord/
11
13
  Author-email: Peter Pfeufer <develop@ppfeufer.de>
12
14
  License: GNU GENERAL PUBLIC LICENSE
13
15
  Version 3, 29 June 2007
@@ -699,11 +701,11 @@ Classifier: Programming Language :: Python :: 3.12
699
701
  Classifier: Programming Language :: Python :: 3.13
700
702
  Classifier: Topic :: Internet :: WWW/HTTP
701
703
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
702
- Requires-Python: >=3.10
703
- Requires-Dist: allianceauth-app-utils>=1.14.2
704
+ Requires-Python: <3.14,>=3.10
704
705
  Requires-Dist: allianceauth-discordbot>=3.0.5
705
706
  Requires-Dist: allianceauth<5,>=4.3.1
706
707
  Requires-Dist: feedparser
708
+ Requires-Dist: py-cord>=2.7.0rc2; python_version == '3.13'
707
709
  Provides-Extra: tests-allianceauth-latest
708
710
  Requires-Dist: coverage; extra == 'tests-allianceauth-latest'
709
711
  Requires-Dist: django-webtest; extra == 'tests-allianceauth-latest'
@@ -718,7 +720,7 @@ Description-Content-Type: text/markdown
718
720
  ![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)
719
721
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ppfeufer/aa-rss-to-discord/master.svg)](https://results.pre-commit.ci/latest/github/ppfeufer/aa-rss-to-discord/master)
720
722
  [![Code Style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](http://black.readthedocs.io/en/latest/)
721
- [![Discord](https://img.shields.io/discord/790364535294132234?label=discord)](https://discord.gg/zmh52wnfvM)
723
+ [![Discord](https://img.shields.io/discord/399006117012832262?label=discord)](https://discord.gg/fjnHAmk)
722
724
  [![Checks](https://github.com/ppfeufer/aa-rss-to-discord/actions/workflows/automated-checks.yml/badge.svg)](https://github.com/ppfeufer/aa-rss-to-discord/actions/workflows/automated-checks.yml)
723
725
  [![codecov](https://codecov.io/gh/ppfeufer/aa-rss-to-discord/branch/master/graph/badge.svg?token=LVEQ6W55ZB)](https://codecov.io/gh/ppfeufer/aa-rss-to-discord)
724
726
  [![Badge: Translation Status]][weblate engage]
@@ -771,7 +773,7 @@ Make sure you're in the virtual environment (venv) of your Alliance Auth
771
773
  installation Then install the latest release directly from PyPi.
772
774
 
773
775
  ```shell
774
- pip install aa-rss-to-discord
776
+ pip install aa-rss-to-discord==2.4.0
775
777
  ```
776
778
 
777
779
  ### Step 2: Configure Alliance Auth<a name="step-2-configure-alliance-auth"></a>
@@ -826,7 +828,7 @@ To update your existing installation of Alliance Auth RSS to Discord, first enab
826
828
  virtual environment (venv) of your Alliance Auth installation.
827
829
 
828
830
  ```bash
829
- pip install -U aa-rss-to-discord
831
+ pip install aa-rss-to-discord==2.4.0
830
832
 
831
833
  python manage.py migrate
832
834
  ```
@@ -0,0 +1,51 @@
1
+ aa_rss_to_discord/__init__.py,sha256=tshvB4Ib8of9KNQB_JuWVhJ7TJ0nePhojMS47YIbaT0,177
2
+ aa_rss_to_discord/admin.py,sha256=bbvD0w0vFgD4kR4riWIwpOWpb9t8R6BI7ofYqTZbjDw,440
3
+ aa_rss_to_discord/apps.py,sha256=jtIy1uXn92yURf7QG7TxzLniSbOAL41OzqjCLxTwMbA,458
4
+ aa_rss_to_discord/auth_hooks.py,sha256=k5mLzQR1NwtA5MCedLzPyULEhv4ylwqvFpTPZ62eWJ0,273
5
+ aa_rss_to_discord/constants.py,sha256=SD5aSlRanXsE07tV5q4PxVJXH0Xgivr8DjfNgyTDaqk,383
6
+ aa_rss_to_discord/managers.py,sha256=oERpP9OXZNg3_SJCKiMZrt7aa_NGxKswZntfgYkpRdA,307
7
+ aa_rss_to_discord/models.py,sha256=G-_LjShFDQFcbcg3nuR5mwjBl4zmANsgF8PIrokJOdU,1781
8
+ aa_rss_to_discord/providers.py,sha256=AwZ4QgWg_LNQsWjwHCurAFBHEFwTXGEDGUP6xTKxGJc,1029
9
+ aa_rss_to_discord/tasks.py,sha256=4L-vdX1570nyVp1NBl6yJ13a9hW2aT_ABK1YY_8y9Ss,3807
10
+ aa_rss_to_discord/discordbot/cogs/__init__.py,sha256=3u3OJfO3Xh1GightCaX0lhbUbP7Lq2c1nY4Tg1FsrZo,27
11
+ aa_rss_to_discord/discordbot/cogs/rss.py,sha256=aPov4y-ckTwzXdXfKtA5WZZKwy7myOlW2xyK5yWGhy4,8174
12
+ aa_rss_to_discord/locale/django.pot,sha256=4uKpgRNE82d-SXtbOIxfkhJ0yE_Yo146_XoneZPQNDQ,962
13
+ aa_rss_to_discord/locale/cs_CZ/LC_MESSAGES/django.mo,sha256=e5Bcjwg3sIKgDgHKifbl2DcH0atHMSygiZwgvKEaifs,565
14
+ aa_rss_to_discord/locale/cs_CZ/LC_MESSAGES/django.po,sha256=zeffaDdZEfeeIWiUetKO_Ft7muDgkHtMdVxecVC5yQQ,1187
15
+ aa_rss_to_discord/locale/de/LC_MESSAGES/django.mo,sha256=NGLMzDZwRKCvQH_RtK9wm6UXKWHLuipasTLnjaosbRE,717
16
+ aa_rss_to_discord/locale/de/LC_MESSAGES/django.po,sha256=Y1mynnUEi0AgHUQZUKVNSQL0EquILhK3RYPBnQkqz9g,1183
17
+ aa_rss_to_discord/locale/es/LC_MESSAGES/django.mo,sha256=BNXYj-j0OPYFuLpm0uJS_b28tdtbs1BOMYp_HCWcoC8,664
18
+ aa_rss_to_discord/locale/es/LC_MESSAGES/django.po,sha256=XVZ6DPLize4AS-LRH5ayMsXuY4txIHe7IBwHpib0EPU,1196
19
+ aa_rss_to_discord/locale/fr_FR/LC_MESSAGES/django.mo,sha256=zwgaGEqI-UlHpc9q1irjc4xDxZJZBheixtIWz-43q1U,721
20
+ aa_rss_to_discord/locale/fr_FR/LC_MESSAGES/django.po,sha256=XmpegMaCzAX5_y1YQzFbwPM6FpcoWFiK0ZjuGeA1s5A,1337
21
+ aa_rss_to_discord/locale/it_IT/LC_MESSAGES/django.mo,sha256=jyxuImpxL3PnEGxbqzYoLvrE014f-wPENwh0LMuWviM,487
22
+ aa_rss_to_discord/locale/it_IT/LC_MESSAGES/django.po,sha256=z4pSagMW-mF7R9XxQQNPKfMbKW7g-0y9Qh8oByNgWZk,1107
23
+ aa_rss_to_discord/locale/ja/LC_MESSAGES/django.mo,sha256=dzafm9mmWwOeYbuJWyMmxTKxqdBu7ptUXdhXllkSoY4,770
24
+ aa_rss_to_discord/locale/ja/LC_MESSAGES/django.po,sha256=zdH0HWBf8QcZl_t7Nuc5-QB0OqVDFLeypouFYh3c_Uk,1229
25
+ aa_rss_to_discord/locale/ko_KR/LC_MESSAGES/django.mo,sha256=1IW9bwgVPrzDl_0crEO0UMaPF4jP3SyTUrZeRjalzCk,738
26
+ aa_rss_to_discord/locale/ko_KR/LC_MESSAGES/django.po,sha256=AWBaC8P8bCMgZ7NYF2fRBLhDvAk9Qc7W9YMWbMfZpmI,1239
27
+ aa_rss_to_discord/locale/nl_NL/LC_MESSAGES/django.mo,sha256=JLv2IW5Kp2wqE7MqNn9P9blZ_I-Jp0CIWiHsadYgFUU,483
28
+ aa_rss_to_discord/locale/nl_NL/LC_MESSAGES/django.po,sha256=ClrnrkigalQgcw1UT8pDjtrxyWR8psEChhU4YXtTyIk,1105
29
+ aa_rss_to_discord/locale/pl_PL/LC_MESSAGES/django.mo,sha256=2ugIqkNNVUGxIgfxUjU8SCIBJvSCn6vGzAV9Xnqiyag,543
30
+ aa_rss_to_discord/locale/pl_PL/LC_MESSAGES/django.po,sha256=yIUIUBx_eDd_qT4zWCOd_VjDPKf9xxrGH-Pv0GQqkrc,1164
31
+ aa_rss_to_discord/locale/ru/LC_MESSAGES/django.mo,sha256=BzuRJnMwz-rpbr8rc_K755H-sstix0LsOpb7lSbkRuo,865
32
+ aa_rss_to_discord/locale/ru/LC_MESSAGES/django.po,sha256=CNH5PJAu9dFOgtgOttRhpHDgxvKIQx39P2mgIftCemw,1351
33
+ aa_rss_to_discord/locale/sk/LC_MESSAGES/django.mo,sha256=WPcDxnOaK_bCNIPoZQRxryi5F97g2RNn0Zuh9us90U0,562
34
+ aa_rss_to_discord/locale/sk/LC_MESSAGES/django.po,sha256=I2P3KN-Q0-EyywmQ_aO54i4zcQ5RGVeri0Dbtr4lLCc,1180
35
+ aa_rss_to_discord/locale/uk/LC_MESSAGES/django.mo,sha256=5DL6DnJ0kP7s9PbVr92PAeVQf-cGMSQibbnoJmZJHG8,858
36
+ aa_rss_to_discord/locale/uk/LC_MESSAGES/django.po,sha256=VUl9mZPHBA6kTyLhWO0pw9lmOozXPgaZ-EAbZwQhHlo,1352
37
+ aa_rss_to_discord/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=DaODnWQcYUSgtxUaPmZoEG6IQ190DS3WzCh6uNvhHh8,518
38
+ aa_rss_to_discord/locale/zh_Hans/LC_MESSAGES/django.po,sha256=7OkKDvfLIkZWOBi98fyPNVTlHXw_Kch8VIYzZejAQQs,1122
39
+ aa_rss_to_discord/migrations/0001_initial.py,sha256=TD8ZmWLleWEw1G3PlQh3HBU4pRXxwIfp_LMQj4IyTDE,2764
40
+ aa_rss_to_discord/migrations/0002_enable_disable_rss_feeds.py,sha256=apLeuxILrxvTntSc1bixfcDqpgt49VscyT6inLKU9k8,396
41
+ aa_rss_to_discord/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ aa_rss_to_discord/tests/__init__.py,sha256=hQ-yK-riPHikEE78PrbFZUKRToIZhif7-mD-yiHzjkM,873
43
+ aa_rss_to_discord/tests/test_auth_hooks.py,sha256=DZLEdVO9E82GEqPMv6_JOgSLPjU_DrB9uLWDgWgQ34w,1147
44
+ aa_rss_to_discord/tests/test_managers.py,sha256=Ar4Mc4z1k0701jjAWKTqe9BzxBjX-32KIdT4VgXfJ_s,1023
45
+ aa_rss_to_discord/tests/test_models.py,sha256=XtqGVZQbbtyDktYlHYsKFrLb3qB-UpU7wIpA6gXsKL8,4823
46
+ aa_rss_to_discord/tests/test_providers.py,sha256=ZF1zV3-_yFWwruiR_g2yB-RRy_t5W0WPbaF7guFRXF8,2677
47
+ aa_rss_to_discord/tests/test_tasks.py,sha256=tJykTvZ4FHTkZP0J6MUA5Me-0NP-mK_uPqNNrGWuv2Y,5584
48
+ aa_rss_to_discord-2.4.0.dist-info/METADATA,sha256=k40M2wasM80fl20MBe1DQeqKO4GPv_naRfZ2QixZ9Ow,50408
49
+ aa_rss_to_discord-2.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
50
+ aa_rss_to_discord-2.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
51
+ aa_rss_to_discord-2.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any