mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-10 23:42:37 -07:00
Add webhook notification agent
This commit is contained in:
parent
9dbb681f22
commit
4648e3df5f
5 changed files with 159 additions and 23 deletions
|
@ -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>
|
||||||
|
${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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue