Refactored the way Notifiarr Discord users are mentioned (#1153)

This commit is contained in:
Chris Caron 2024-07-10 06:11:58 -04:00 committed by GitHub
parent e053484fd1
commit 631ce107cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 97 additions and 68 deletions

View File

@ -37,6 +37,7 @@ from ..locale import gettext_lazy as _
from ..common import NotifyImageSize
from ..utils import parse_list, parse_bool
from ..utils import validate_regex
from .discord import USER_ROLE_DETECTION_RE
# Used to break path apart into list of channels
CHANNEL_LIST_DELIM = re.compile(r'[ \t\r\n,#\\/]+')
@ -118,14 +119,6 @@ class NotifyNotifiarr(NotifyBase):
'apikey': {
'alias_of': 'apikey',
},
'discord_user': {
'name': _('Ping Discord User'),
'type': 'int',
},
'discord_role': {
'name': _('Ping Discord Role'),
'type': 'int',
},
'event': {
'name': _('Discord Event ID'),
'type': 'int',
@ -149,7 +142,6 @@ class NotifyNotifiarr(NotifyBase):
})
def __init__(self, apikey=None, include_image=None,
discord_user=None, discord_role=None,
event=None, targets=None, source=None, **kwargs):
"""
Initialize Notifiarr Object
@ -172,30 +164,6 @@ class NotifyNotifiarr(NotifyBase):
if isinstance(include_image, bool) \
else self.template_args['image']['default']
# Set up our user if specified
self.discord_user = 0
if discord_user:
try:
self.discord_user = int(discord_user)
except (ValueError, TypeError):
msg = 'An invalid Notifiarr User ID ' \
'({}) was specified.'.format(discord_user)
self.logger.warning(msg)
raise TypeError(msg)
# Set up our role if specified
self.discord_role = 0
if discord_role:
try:
self.discord_role = int(discord_role)
except (ValueError, TypeError):
msg = 'An invalid Notifiarr Role ID ' \
'({}) was specified.'.format(discord_role)
self.logger.warning(msg)
raise TypeError(msg)
# Prepare our source (if set)
self.source = validate_regex(source)
@ -244,12 +212,6 @@ class NotifyNotifiarr(NotifyBase):
if self.source:
params['source'] = self.source
if self.discord_user:
params['discord_user'] = self.discord_user
if self.discord_role:
params['discord_role'] = self.discord_role
if self.event:
params['event'] = self.event
@ -287,6 +249,29 @@ class NotifyNotifiarr(NotifyBase):
# Acquire image_url
image_url = self.image_url(notify_type)
# Define our mentions
mentions = {
'pingUser': [],
'pingRole': [],
'content': [],
}
# parse for user id's <@123> and role IDs <@&456>
results = USER_ROLE_DETECTION_RE.findall(body)
if results:
for (is_role, no, value) in results:
if value:
# @everybody, @admin, etc - unsupported
mentions['content'].append(f'@{value}')
elif is_role:
mentions['pingRole'].append(no)
mentions['content'].append(f'<@&{no}>')
else: # is_user
mentions['pingUser'].append(no)
mentions['content'].append(f'<@{no}>')
for idx, channel in enumerate(self.targets['channels']):
# prepare Notifiarr Object
payload = {
@ -301,14 +286,17 @@ class NotifyNotifiarr(NotifyBase):
'discord': {
'color': self.color(notify_type),
'ping': {
'pingUser': self.discord_user
if not idx and self.discord_user else 0,
'pingRole': self.discord_role
if not idx and self.discord_role else 0,
# Only 1 user is supported, so truncate the rest
'pingUser': 0 if not mentions['pingUser']
else mentions['pingUser'][0],
# Only 1 role is supported, so truncate the rest
'pingRole': 0 if not mentions['pingRole']
else mentions['pingRole'][0],
},
'text': {
'title': title,
'content': '',
'content': '' if not mentions['content']
else '👉 ' + ' '.join(mentions['content']),
'description': body,
'footer': self.app_desc,
},
@ -410,17 +398,6 @@ class NotifyNotifiarr(NotifyBase):
# Get channels
results['targets'] = NotifyNotifiarr.split_path(results['fullpath'])
if 'discord_user' in results['qsd'] and \
len(results['qsd']['discord_user']):
results['discord_user'] = \
NotifyNotifiarr.unquote(
results['qsd']['discord_user'])
if 'discord_role' in results['qsd'] and \
len(results['qsd']['discord_role']):
results['discord_role'] = \
NotifyNotifiarr.unquote(results['qsd']['discord_role'])
if 'event' in results['qsd'] and \
len(results['qsd']['event']):
results['event'] = \

View File

@ -27,9 +27,11 @@
# POSSIBILITY OF SUCH DAMAGE.
import requests
from unittest import mock
from apprise.plugins.notifiarr import NotifyNotifiarr
from helpers import AppriseURLTester
from json import loads
from inspect import cleandoc
# Disable logging for a cleaner testing output
import logging
@ -52,12 +54,6 @@ apprise_url_tests = (
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'notifiarr://a...y',
}),
('notifiarr://apikey/1234/?discord_user=invalid', {
'instance': TypeError,
}),
('notifiarr://apikey/1234/?discord_role=invalid', {
'instance': TypeError,
}),
('notifiarr://apikey/1234/?event=invalid', {
'instance': TypeError,
}),
@ -131,11 +127,6 @@ apprise_url_tests = (
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'notifiarr://m...y/#123/#325',
}),
('notifiarr://12/?key=myapikey&discord_user=23'
'&discord_role=12&event=123', {
'instance': NotifyNotifiarr,
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'notifiarr://m...y/#12'}),
('notifiarr://apikey/123/', {
'instance': NotifyNotifiarr,
}),
@ -160,7 +151,7 @@ apprise_url_tests = (
)
def test_plugin_custom_notifiarr_urls():
def test_plugin_notifiarr_urls():
"""
NotifyNotifiarr() Apprise URLs
@ -168,3 +159,64 @@ def test_plugin_custom_notifiarr_urls():
# Run our general tests
AppriseURLTester(tests=apprise_url_tests).run_all()
@mock.patch('requests.post')
def test_plugin_notifiarr_notifications(mock_post):
"""
NotifyNotifiarr() Notifications/Ping Support
"""
# Test our header parsing when not lead with a header
body = cleandoc("""
# Heading
@everyone and @admin, wake and meet our new user <@123> and <@987>;
Attention Roles: <@&456> and <@&765>
""")
# Prepare a good response
response = mock.Mock()
response.status_code = requests.codes.ok
# Prepare Mock return object
mock_post.return_value = response
results = NotifyNotifiarr.parse_url('notifiarr://apikey/12345')
instance = NotifyNotifiarr(**results)
assert isinstance(instance, NotifyNotifiarr)
response = instance.send(body=body)
assert response is True
assert mock_post.call_count == 1
details = mock_post.call_args_list[0]
assert details[0][0] == \
'https://notifiarr.com/api/v1/notification/apprise'
payload = loads(details[1]['data'])
# First role and first user stored
assert payload == {
'source': 'Apprise',
'type': 'info',
'notification': {'update': False, 'name': 'Apprise', 'event': ''},
'discord': {
'color': '#3AA3E3', 'ping': {
# Only supports 1 entry each; so first one is parsed
'pingUser': '123',
'pingRole': '456',
},
'text': {
'title': '',
'content': '👉 @everyone @admin <@123> <@987> <@&456> <@&765>',
'description':
'# Heading\n@everyone and @admin, wake and meet our new '
'user <@123> and <@987>;\nAttention Roles: <@&456> and '
'<@&765>\n ',
'footer': 'Apprise Notifications',
},
'ids': {'channel': 12345},
},
}