Add webhook notification agent

This commit is contained in:
JonnyWong16 2018-08-12 10:31:27 -07:00
parent 9dbb681f22
commit 4648e3df5f
5 changed files with 159 additions and 23 deletions

View file

@ -21,7 +21,13 @@
<li role="presentation" class="active"><a href="#tabs-notifier_config" aria-controls="tabs-notifier_config" role="tab" data-toggle="tab">Configuration</a></li> <li role="presentation" class="active"><a href="#tabs-notifier_config" aria-controls="tabs-notifier_config" role="tab" data-toggle="tab">Configuration</a></li>
<li role="presentation"><a href="#tabs-notify_triggers" aria-controls="tabs-notify_triggers" role="tab" data-toggle="tab">Triggers</a></li> <li role="presentation"><a href="#tabs-notify_triggers" aria-controls="tabs-notify_triggers" role="tab" data-toggle="tab">Triggers</a></li>
<li role="presentation"><a href="#tabs-notify_conditions" aria-controls="tabs-notify_conditions" role="tab" data-toggle="tab">Conditions</a></li> <li role="presentation"><a href="#tabs-notify_conditions" aria-controls="tabs-notify_conditions" role="tab" data-toggle="tab">Conditions</a></li>
<li role="presentation"><a href="#tabs-notify_text" aria-controls="tabs-notify_text" role="tab" data-toggle="tab">${'Arguments' if notifier['agent_name'] == 'scripts' else 'Text'}</a></li> % if notifier['agent_name'] == 'scripts':
<li role="presentation"><a href="#tabs-notify_text" aria-controls="tabs-notify_text" role="tab" data-toggle="tab">Arguments</a></li>
% elif notifier['agent_name'] == 'webhook':
<li role="presentation"><a href="#tabs-notify_text" aria-controls="tabs-notify_text" role="tab" data-toggle="tab">Data</a></li>
% else:
<li role="presentation"><a href="#tabs-notify_text" aria-controls="tabs-notify_text" role="tab" data-toggle="tab">Text</a></li>
% endif
<li role="presentation"><a href="#tabs-test_notifications" aria-controls="tabs-test_notifications" role="tab" data-toggle="tab">Test Notifications</a></li> <li role="presentation"><a href="#tabs-test_notifications" aria-controls="tabs-test_notifications" role="tab" data-toggle="tab">Test Notifications</a></li>
</ul> </ul>
</div> </div>
@ -184,6 +190,8 @@
<p class="help-block"> <p class="help-block">
% if notifier['agent_name'] == 'scripts': % if notifier['agent_name'] == 'scripts':
Set the custom arguments passed to the script for each type of notification. Set the custom arguments passed to the script for each type of notification.
% elif notifier['agent_name'] == 'webhook':
Set the custom JSON data sent to the webhook for each type of notification.
% else: % else:
Set the custom formatted text for each type of notification. Set the custom formatted text for each type of notification.
% endif % endif
@ -225,6 +233,32 @@
</ul> </ul>
</li> </li>
% endfor % endfor
% elif notifier['agent_name'] == 'webhook':
% for action in available_notification_actions:
<li>
<div class="link">
<span class="toggle-left"><i class="fa ${action['icon']} fa-fw"></i></span>&nbsp;
${action['label']}
<span class="toggle-right"><i class="fa fa-chevron-down"></i></span>
</div>
<ul class="submenu">
<li>
<div class="form-group">
<label for="${action['name']}_body">JSON Data</label>
<textarea class="form-control" id="${action['name']}_body" name="${action['name']}_body" data-parsley-trigger="change" data-autoresize required>${notifier['notify_text'][action['name']]['body']}</textarea>
<p class="help-block">Set custom JSON data.</p>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-12">
<input type="button" class="btn btn-bright notifier-text-preview" data-action="${action['name']}" value="Preview JSON Data">
</div>
</div>
</div>
</li>
</ul>
</li>
% endfor
% else: % else:
% for action in available_notification_actions: % for action in available_notification_actions:
<li> <li>
@ -291,6 +325,16 @@
</div> </div>
<p class="help-block">Set custom arguments passed to the script.</p> <p class="help-block">Set custom arguments passed to the script.</p>
</div> </div>
% elif notifier['agent_name'] == 'webhook':
<div class="form-group">
<label for="test_body">JSON Data</label>
<div class="row">
<div class="col-md-12">
<textarea class="form-control" id="test_body" name="test_body" data-autoresize></textarea>
</div>
</div>
<p class="help-block">Set custom JSON data sent to the webhook.</p>
</div>
% else: % else:
<div class="form-group"> <div class="form-group">
<label for="test_subject">Subject Line</label> <label for="test_subject">Subject Line</label>
@ -305,7 +349,7 @@
<label for="test_body">Message Body</label> <label for="test_body">Message Body</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input class="form-control" type="text" id="test_body" name="test_body" value="Test notification"> <textarea class="form-control" id="test_body" name="test_body" data-autoresize>Test Notification</textarea>
</div> </div>
</div> </div>
<p class="help-block">Set a custom body.</p> <p class="help-block">Set a custom body.</p>

View file

@ -9,7 +9,9 @@
% for item in text: % for item in text:
<div style="padding-bottom: 10px;"> <div style="padding-bottom: 10px;">
<h4>${item['media_type'].capitalize()}</h4> <h4>${item['media_type'].capitalize()}</h4>
% if agent != 'webhook':
<pre>${item['subject']}</pre> <pre>${item['subject']}</pre>
% endif
% if agent != 'scripts': % if agent != 'scripts':
<pre>${item['body']}</pre> <pre>${item['body']}</pre>
% endif % endif

View file

@ -1115,3 +1115,23 @@ def grouper(iterable, n, fillvalue=None):
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args) return izip_longest(fillvalue=fillvalue, *args)
def traverse_map(obj, func):
if isinstance(obj, list):
new_obj = []
for i in obj:
new_obj.append(traverse_map(i, func))
elif isinstance(obj, dict):
new_obj = {}
for k, v in obj.iteritems():
new_obj[traverse_map(k, func)] = traverse_map(v, func)
elif isinstance(obj, basestring):
new_obj = func(obj)
else:
new_obj = obj
return new_obj

View file

@ -1039,6 +1039,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
# Remove the unwanted tags and strip any unmatch tags too. # Remove the unwanted tags and strip any unmatch tags too.
subject = strip_tag(re.sub(pattern, '', subject), agent_id).strip(' \t\n\r') subject = strip_tag(re.sub(pattern, '', subject), agent_id).strip(' \t\n\r')
body = strip_tag(re.sub(pattern, '', body), agent_id).strip(' \t\n\r') body = strip_tag(re.sub(pattern, '', body), agent_id).strip(' \t\n\r')
script_args = []
if test: if test:
return subject, body return subject, body
@ -1055,26 +1056,41 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e)
script_args = [] script_args = []
elif agent_id == 25:
try:
body = json.loads(body)
except ValueError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom webhook json data: %s. Using fallback." % e)
try:
body = json.dumps(helpers.traverse_map(body,
lambda x: custom_formatter.format(x, **parameters).decode(plexpy.SYS_ENCODING, 'ignore')))
except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in webhook data. Using fallback." % e)
body = ''
except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom webhook data: %s. Using fallback." % e)
body = ''
else: else:
script_args = [] try:
subject = custom_formatter.format(unicode(subject), **parameters)
except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification subject. Using fallback." % e)
subject = unicode(default_subject).format(**parameters)
except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e)
subject = unicode(default_subject).format(**parameters)
try: try:
subject = custom_formatter.format(unicode(subject), **parameters) body = custom_formatter.format(unicode(body), **parameters)
except LookupError as e: except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification subject. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification body. Using fallback." % e)
subject = unicode(default_subject).format(**parameters) body = unicode(default_body).format(**parameters)
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e)
subject = unicode(default_subject).format(**parameters) body = unicode(default_body).format(**parameters)
try:
body = custom_formatter.format(unicode(body), **parameters)
except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification body. Using fallback." % e)
body = unicode(default_body).format(**parameters)
except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e)
body = unicode(default_body).format(**parameters)
return subject, body, script_args return subject, body, script_args

View file

@ -91,7 +91,8 @@ AGENT_IDS = {'growl': 0,
'androidapp': 21, 'androidapp': 21,
'groupme': 22, 'groupme': 22,
'mqtt': 23, 'mqtt': 23,
'zapier': 24 'zapier': 24,
'webhook': 25
} }
DEFAULT_CUSTOM_CONDITIONS = [{'parameter': '', 'operator': '', 'value': ''}] DEFAULT_CUSTOM_CONDITIONS = [{'parameter': '', 'operator': '', 'value': ''}]
@ -190,6 +191,10 @@ def available_notification_agents():
'name': 'twitter', 'name': 'twitter',
'id': AGENT_IDS['twitter'] 'id': AGENT_IDS['twitter']
}, },
{'label': 'Webhook',
'name': 'webhook',
'id': AGENT_IDS['webhook']
},
{'label': 'Zapier', {'label': 'Zapier',
'name': 'zapier', 'name': 'zapier',
'id': AGENT_IDS['zapier'] 'id': AGENT_IDS['zapier']
@ -386,6 +391,8 @@ def get_agent_class(agent_id=None, config=None):
return MQTT(config=config) return MQTT(config=config)
elif agent_id == 24: elif agent_id == 24:
return ZAPIER(config=config) return ZAPIER(config=config)
elif agent_id == 25:
return WEBHOOK(config=config)
else: else:
return Notifier(config=config) return Notifier(config=config)
else: else:
@ -513,7 +520,7 @@ def add_notifier_config(agent_id=None, **kwargs):
'custom_conditions_logic': '' 'custom_conditions_logic': ''
} }
if agent['name'] == 'scripts': if agent['name'] in ('scripts', 'webhook'):
for a in available_notification_actions(): for a in available_notification_actions():
values[a['name'] + '_subject'] = '' values[a['name'] + '_subject'] = ''
values[a['name'] + '_body'] = '' values[a['name'] + '_body'] = ''
@ -774,7 +781,7 @@ class Notifier(object):
return self._DEFAULT_CONFIG.copy() return self._DEFAULT_CONFIG.copy()
def notify(self, subject='', body='', action='', **kwargs): def notify(self, subject='', body='', action='', **kwargs):
if self.NAME != 'Script': if self.NAME not in ('Script', 'Webhook'):
if not subject and self.config.get('incl_subject', True): if not subject and self.config.get('incl_subject', True):
logger.error(u"Tautulli Notifiers :: %s notification subject cannot be blank." % self.NAME) logger.error(u"Tautulli Notifiers :: %s notification subject cannot be blank." % self.NAME)
return return
@ -3534,6 +3541,53 @@ class TWITTER(Notifier):
return config_option return config_option
class WEBHOOK(Notifier):
"""
Webhook notifications
"""
NAME = 'Webhook'
_DEFAULT_CONFIG = {'hook': '',
'method': ''
}
def agent_notify(self, subject='', body='', action='', **kwargs):
if body:
try:
webhook_data = json.loads(body)
except ValueError as e:
logger.error(u"Tautulli Notifiers :: Invalid {name} json data: {e}".format(name=self.NAME, e=e))
return False
else:
webhook_data = None
headers = {'Content-type': 'application/json'}
return self.make_request(self.config['hook'], method=self.config['method'], headers=headers, json=webhook_data)
def return_config_options(self):
config_option = [{'label': 'Webhook URL',
'value': self.config['hook'],
'name': 'webhook_hook',
'description': 'Your Webhook URL.',
'input_type': 'text'
},
{'label': 'Webhook Method',
'value': self.config['method'],
'name': 'webhook_method',
'description': 'The Webhook HTTP request method.',
'input_type': 'select',
'select_options': {'': '',
'GET': 'GET',
'POST': 'POST',
'PUT': 'PUT',
'DELETE': 'DELETE'}
}
]
return config_option
class XBMC(Notifier): class XBMC(Notifier):
""" """
Kodi notifications Kodi notifications