Skip to content

Commit

Permalink
Refactored the way Notifiarr Discord users are mentioned (#1153)
Browse files Browse the repository at this point in the history
  • Loading branch information
caronc authored Jul 10, 2024
1 parent e053484 commit 631ce10
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 68 deletions.
87 changes: 32 additions & 55 deletions apprise/plugins/notifiarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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,#\\/]+')
Expand Down Expand Up @@ -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',
Expand All @@ -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
Expand All @@ -172,30 +164,6 @@ def __init__(self, apikey=None, include_image=None,
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)

Expand Down Expand Up @@ -244,12 +212,6 @@ def url(self, privacy=False, *args, **kwargs):
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

Expand Down Expand Up @@ -287,6 +249,29 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
# 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 = {
Expand All @@ -301,14 +286,17 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
'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,
},
Expand Down Expand Up @@ -410,17 +398,6 @@ def parse_url(url):
# 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'] = \
Expand Down
78 changes: 65 additions & 13 deletions test/test_plugin_notifiarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,12 +54,6 @@
# 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,
}),
Expand Down Expand Up @@ -131,11 +127,6 @@
# 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,
}),
Expand All @@ -160,11 +151,72 @@
)


def test_plugin_custom_notifiarr_urls():
def test_plugin_notifiarr_urls():
"""
NotifyNotifiarr() Apprise 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},
},
}

0 comments on commit 631ce10

Please sign in to comment.