diff --git a/data/interfaces/default/notifier_config.html b/data/interfaces/default/notifier_config.html index 7a164d0f..168cf47e 100644 --- a/data/interfaces/default/notifier_config.html +++ b/data/interfaces/default/notifier_config.html @@ -21,7 +21,13 @@
% if notifier['agent_name'] == 'scripts': 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: Set the custom formatted text for each type of notification. % endif @@ -225,6 +233,32 @@ % endfor + % elif notifier['agent_name'] == 'webhook': + % for action in available_notification_actions: +
Set custom arguments passed to the script.
+ % elif notifier['agent_name'] == 'webhook': +Set custom JSON data sent to the webhook.
+Set a custom body.
diff --git a/data/interfaces/default/notifier_text_preview.html b/data/interfaces/default/notifier_text_preview.html index 26cba29f..cb097213 100644 --- a/data/interfaces/default/notifier_text_preview.html +++ b/data/interfaces/default/notifier_text_preview.html @@ -9,7 +9,9 @@ % for item in text:${item['subject']}+ % endif % if agent != 'scripts':
${item['body']}% endif diff --git a/plexpy/helpers.py b/plexpy/helpers.py index 8b07b1e5..40257948 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -1115,3 +1115,23 @@ def grouper(iterable, n, fillvalue=None): # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx args = [iter(iterable)] * n 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 diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 25ede050..07498ccd 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -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. 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') + script_args = [] if test: return subject, body @@ -1055,26 +1056,41 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None, except Exception as e: logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e) 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: - 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: - 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: - 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) + 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 diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index cde7fbe7..dad34179 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -91,7 +91,8 @@ AGENT_IDS = {'growl': 0, 'androidapp': 21, 'groupme': 22, 'mqtt': 23, - 'zapier': 24 + 'zapier': 24, + 'webhook': 25 } DEFAULT_CUSTOM_CONDITIONS = [{'parameter': '', 'operator': '', 'value': ''}] @@ -190,6 +191,10 @@ def available_notification_agents(): 'name': 'twitter', 'id': AGENT_IDS['twitter'] }, + {'label': 'Webhook', + 'name': 'webhook', + 'id': AGENT_IDS['webhook'] + }, {'label': 'Zapier', 'name': 'zapier', 'id': AGENT_IDS['zapier'] @@ -386,6 +391,8 @@ def get_agent_class(agent_id=None, config=None): return MQTT(config=config) elif agent_id == 24: return ZAPIER(config=config) + elif agent_id == 25: + return WEBHOOK(config=config) else: return Notifier(config=config) else: @@ -513,7 +520,7 @@ def add_notifier_config(agent_id=None, **kwargs): 'custom_conditions_logic': '' } - if agent['name'] == 'scripts': + if agent['name'] in ('scripts', 'webhook'): for a in available_notification_actions(): values[a['name'] + '_subject'] = '' values[a['name'] + '_body'] = '' @@ -774,7 +781,7 @@ class Notifier(object): return self._DEFAULT_CONFIG.copy() 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): logger.error(u"Tautulli Notifiers :: %s notification subject cannot be blank." % self.NAME) return @@ -3534,6 +3541,53 @@ class TWITTER(Notifier): 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): """ Kodi notifications