diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index b9859ae1..ec384bcd 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -132,12 +132,17 @@ from plexpy import helpers $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); }); + $('#testSlack').click(function() { + $.get("/test_slack", function(data) { $('#ajaxMsg').html("" + data); }); + $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); + }); + $('#pushbullet_apikey').on('change', function () { doAjaxCall('set_notification_config', $(this), 'tabs', true); reloadModal(); return false; }); - + // Never send checkbox values directly, always substitute value in hidden input. $('.checkboxes').click(function () { var configToggle = $(this).data('id'); diff --git a/plexpy/config.py b/plexpy/config.py index 22219786..30947823 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -281,6 +281,22 @@ _CONFIG_DEFINITIONS = { 'PUSHOVER_ON_INTUP': (int, 'Pushover', 0), 'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12), 'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1), + 'SLACK_ENABLED': (int, 'Slack', 0), + 'SLACK_HOOK': (str, 'Slack', ''), + 'SLACK_CHANNEL': (str, 'Slack', ''), + 'SLACK_ICON_EMOJI': (str, 'Slack', ''), + 'SLACK_USERNAME': (str, 'Slack', ''), + 'SLACK_ON_PLAY': (int, 'Slack', 0), + 'SLACK_ON_STOP': (int, 'Slack', 0), + 'SLACK_ON_PAUSE': (int, 'Slack', 0), + 'SLACK_ON_RESUME': (int, 'Slack', 0), + 'SLACK_ON_BUFFER': (int, 'Slack', 0), + 'SLACK_ON_WATCHED': (int, 'Slack', 0), + 'SLACK_ON_CREATED': (int, 'Slack', 0), + 'SLACK_ON_EXTDOWN': (int, 'Slack', 0), + 'SLACK_ON_INTDOWN': (int, 'Slack', 0), + 'SLACK_ON_EXTUP': (int, 'Slack', 0), + 'SLACK_ON_INTUP': (int, 'Slack', 0), 'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''), 'TELEGRAM_ENABLED': (int, 'Telegram', 0), 'TELEGRAM_CHAT_ID': (str, 'Telegram', ''), @@ -448,4 +464,4 @@ class Config(object): if self.VIDEO_LOGGING_ENABLE == 0: self.MOVIE_LOGGING_ENABLE = 0 self.TV_LOGGING_ENABLE = 0 - self.CONFIG_VERSION = '1' \ No newline at end of file + self.CONFIG_VERSION = '1' diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 5fd913de..df024958 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -19,6 +19,7 @@ from plexpy.helpers import checked, radio from xml.dom import minidom from httplib import HTTPSConnection from urlparse import parse_qsl +from urlparse import urlparse from urllib import urlencode from pynma import pynma @@ -52,7 +53,8 @@ AGENT_IDS = {"Growl": 0, "Email": 10, "Twitter": 11, "IFTTT": 12, - "Telegram": 13} + "Telegram": 13, + "Slack":14} def available_notification_agents(): agents = [{'name': 'Growl', @@ -275,6 +277,23 @@ def available_notification_agents(): 'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN, 'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP, 'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP + }, + {'name': 'Slack', + 'id': AGENT_IDS['Slack'], + 'config_prefix': 'slack', + 'has_config': True, + 'state': checked(plexpy.CONFIG.SLACK_ENABLED), + 'on_play': plexpy.CONFIG.SLACK_ON_PLAY, + 'on_stop': plexpy.CONFIG.SLACK_ON_STOP, + 'on_resume': plexpy.CONFIG.SLACK_ON_RESUME, + 'on_pause': plexpy.CONFIG.SLACK_ON_PAUSE, + 'on_buffer': plexpy.CONFIG.SLACK_ON_BUFFER, + 'on_watched': plexpy.CONFIG.SLACK_ON_WATCHED, + 'on_created': plexpy.CONFIG.SLACK_ON_CREATED, + 'on_extdown': plexpy.CONFIG.SLACK_ON_EXTDOWN, + 'on_intdown': plexpy.CONFIG.SLACK_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.SLACK_ON_EXTUP, + 'on_intup': plexpy.CONFIG.SLACK_ON_INTUP } ] @@ -347,6 +366,9 @@ def get_notification_agent_config(config_id): elif config_id == 13: telegramClient = TELEGRAM() return telegramClient.return_config_options() + elif config_id == 14: + slackClient = SLACK() + return slackClient.return_config_options() else: return [] else: @@ -398,12 +420,14 @@ def send_notification(config_id, subject, body): elif config_id == 13: telegramClient = TELEGRAM() telegramClient.notify(message=body, event=subject) + elif config_id == 14: + slackClient = SLACK() + slackClient.notify(message=body, event=subject) else: logger.debug(u"PlexPy Notifier :: Unknown agent id received.") else: logger.debug(u"PlexPy Notifier :: Notification requested but no agent id received.") - class GROWL(object): """ Growl notifications, for OS X. @@ -860,7 +884,7 @@ class PUSHBULLET(object): 'Authorization': 'Basic %s' % base64.b64encode(plexpy.CONFIG.PUSHBULLET_APIKEY + ":")}) response = http_handler.getresponse() request_status = response.status - + if request_status == 200: data = json.loads(response.read()) devices = data.get('devices', []) @@ -1021,7 +1045,7 @@ class PUSHOVER(object): http_handler.request("GET", "/1/sounds.json?token=" + self.application_token) response = http_handler.getresponse() request_status = response.status - + if request_status == 200: data = json.loads(response.read()) sounds = data.get('sounds', {}) @@ -1445,7 +1469,6 @@ class Email(object): return config_option - class IFTTT(object): def __init__(self): @@ -1574,3 +1597,94 @@ class TELEGRAM(object): ] return config_option + +class SLACK(object): + """ + Slack Notifications + """ + def __init__(self): + self.enabled = plexpy.CONFIG.SLACK_ENABLED + self.slack_hook = plexpy.CONFIG.SLACK_HOOK + self.channel = plexpy.CONFIG.SLACK_CHANNEL + self.username = plexpy.CONFIG.SLACK_USERNAME + self.icon_emoji = plexpy.CONFIG.SLACK_ICON_EMOJI + + def conf(self, options): + return cherrypy.config['config'].get('Slack', options) + + def notify(self, message, event): + if not message or not event: + return + http_handler = HTTPSConnection("hooks.slack.com") + + data = {'text': event.encode('utf-8') + ': ' + message.encode("utf-8")} + if self.channel != '': data['channel'] = self.channel + if self.username != '': data['username'] = self.username + if self.icon_emoji != '': + if urlparse(self.icon_emoji).scheme == '': + data['icon_emoji'] = self.icon_emoji + else: + data['icon_url'] = self.icon_url + + url = urlparse(self.slack_hook).path + + http_handler.request("POST", + url, + headers={'Content-type': "application/x-www-form-urlencoded"}, + body=json.dumps(data)) + + response = http_handler.getresponse() + request_status = response.status + + if request_status == 200: + logger.info(u"Slack notifications sent.") + return True + elif request_status >= 400 and request_status < 500: + logger.info(u"Slack request failed: %s" % response.reason) + return False + else: + logger.info(u"Slack notification failed serverside.") + return False + + def updateLibrary(self): + #For uniformity reasons not removed + return + + def test(self): + self.enabled = True + return self.notify('Main Screen Activate', 'Test Message') + + def return_config_options(self): + config_option = [{'label': 'Slack Hook', + 'value': self.slack_hook, + 'name': 'slack_hook', + 'description': 'Your Slack incoming webhook.', + 'input_type': 'text' + }, + {'label': 'Slack Channel', + 'value': self.channel, + 'name': 'slack_channel', + 'description': 'Your slack channel name. (Begin with \'#\')', + 'input_type': 'text' + }, + {'label': 'Slack Username', + 'value': self.username, + 'name': 'slack_username', + 'description': 'Slack username which will be shown', + 'input_type': 'text' + }, + {'label': 'Slack Icon', + 'value': self.icon_emoji, + 'description': 'Your icon you wish to show, use Slack emoji or image url', + 'name': 'slack_icon_emoji', + 'input_type': 'text' + }, + {'label': 'Test Event', + 'value': 'Test Event', + 'name': 'testSlack', + 'description': 'Test if Slack notifications are working. See logs for troubleshooting.', + 'input_type': 'button' + } + ] + + return config_option diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 3c716caf..542147d7 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -499,8 +499,8 @@ class WebInterface(object): "tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start", "tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop", "tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup", - "ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable", - "pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive", + "ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable", + "pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive", "notify_recently_added", "notify_recently_added_grandparent", "monitor_remote_access" ] for checked_config in checked_configs: @@ -526,7 +526,7 @@ class WebInterface(object): if (kwargs['monitoring_interval'] != str(plexpy.CONFIG.MONITORING_INTERVAL)) or \ (kwargs['refresh_users_interval'] != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL)): reschedule = True - + if 'notify_recently_added' in kwargs and \ (kwargs['notify_recently_added'] != plexpy.CONFIG.NOTIFY_RECENTLY_ADDED): reschedule = True @@ -554,7 +554,7 @@ class WebInterface(object): # Get new server URLs for SSL communications. plextv.get_real_pms_url() - + # Get new server friendly name pmsconnect.get_server_friendly_name() @@ -696,6 +696,17 @@ class WebInterface(object): else: return "Error sending event." + @cherrypy.expose + def test_slack(self): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + event = notifiers.SLACK() + result = event.test() + + if result: + return "Notification successful." + else: + return "Error sending event." + @cherrypy.expose def osxnotifyregister(self, app): cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" @@ -1457,7 +1468,7 @@ class WebInterface(object): @cherrypy.expose def undelete_user(self, user_id=None, username=None, **kwargs): data_factory = datafactory.DataFactory() - + if user_id: delete_row = data_factory.undelete_user(user_id=user_id)