
diff --git a/gui/slick/js/configNotifications.js b/gui/slick/js/configNotifications.js
index bb064cc..9640c52 100644
--- a/gui/slick/js/configNotifications.js
+++ b/gui/slick/js/configNotifications.js
@@ -434,6 +434,25 @@
});
});
+ $("#testSlack").click(function () {
+ var slack_access_token = $("#slack_access_token").val();
+ var slack_channel = $("#slack_channel").val();
+ var slack_as_user = $("#slack_as_user").attr('checked');
+ var slack_bot_name = $("#slack_bot_name").val();
+ var slack_icon_url = $("#slack_icon_url").val();
+ if (!slack_access_token || !slack_channel || !(slack_as_user || slack_bot_name)) {
+ $("#testSlack-result").html("Please fill out the necessary fields above.");
+ return;
+ }
+ $(this).prop("disabled", true);
+ $("#testSlack-result").html(loading);
+ $.get(sbRoot + "/home/testSlack", {'accessToken': slack_access_token, 'channel': slack_channel, 'as_user': slack_as_user, 'bot_name': slack_bot_name, 'icon_url': slack_icon_url})
+ .done(function (data) {
+ $("#testSlack-result").html(data);
+ $("#testSlack").prop("disabled", false);
+ });
+ });
+
function get_pushbullet_devices (msg) {
var pushbullet_access_token = $.trim($('#pushbullet_access_token').val());
if (!pushbullet_access_token) {
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index dc25093..bf61078 100755
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -418,6 +418,15 @@ PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD = False
PUSHBULLET_ACCESS_TOKEN = None
PUSHBULLET_DEVICE_IDEN = None
+USE_SLACK = False
+SLACK_NOTIFY_ONSNATCH = False
+SLACK_NOTIFY_ONDOWNLOAD = False
+SLACK_ACCESS_TOKEN = None
+SLACK_CHANNEL = None
+SLACK_AS_USER = False
+SLACK_BOT_NAME = None
+SLACK_ICON_URL = None
+
USE_EMAIL = False
EMAIL_OLD_SUBJECTS = None
EMAIL_NOTIFY_ONSNATCH = False
@@ -631,6 +640,7 @@ def initialize(console_logging=True):
USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, \
TRAKT_USE_WATCHLIST, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, \
TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
+ USE_SLACK, SLACK_NOTIFY_ONSNATCH, SLACK_NOTIFY_ONDOWNLOAD, SLACK_ACCESS_TOKEN, SLACK_CHANNEL, SLACK_AS_USER, SLACK_BOT_NAME, SLACK_ICON_URL, \
USE_EMAIL, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_FROM, \
EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_LIST, EMAIL_OLD_SUBJECTS
# Anime Settings
@@ -640,7 +650,7 @@ def initialize(console_logging=True):
return False
for stanza in ('General', 'Blackhole', 'SABnzbd', 'NZBget', 'Emby', 'Kodi', 'XBMC', 'PLEX',
- 'Growl', 'Prowl', 'Twitter', 'Boxcar2', 'NMJ', 'NMJv2', 'Synology', 'SynologyNotifier',
+ 'Growl', 'Prowl', 'Twitter', 'Slack', 'Boxcar2', 'NMJ', 'NMJv2', 'Synology', 'SynologyNotifier',
'pyTivo', 'NMA', 'Pushalot', 'Pushbullet', 'Subtitles'):
CheckSection(CFG, stanza)
@@ -1033,6 +1043,15 @@ def initialize(console_logging=True):
PUSHBULLET_ACCESS_TOKEN = check_setting_str(CFG, 'Pushbullet', 'pushbullet_access_token', '')
PUSHBULLET_DEVICE_IDEN = check_setting_str(CFG, 'Pushbullet', 'pushbullet_device_iden', '')
+ USE_SLACK = bool(check_setting_int(CFG, 'Slack', 'use_slack', 0))
+ SLACK_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Slack', 'slack_notify_onsnatch', 0))
+ SLACK_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Slack', 'slack_notify_ondownload', 0))
+ SLACK_ACCESS_TOKEN = check_setting_str(CFG, 'Slack', 'slack_access_token', '')
+ SLACK_CHANNEL = check_setting_str(CFG, 'Slack', 'slack_channel', '')
+ SLACK_AS_USER = bool(check_setting_int(CFG, 'Slack', 'slack_as_user', 0))
+ SLACK_BOT_NAME = check_setting_str(CFG, 'Slack', 'slack_bot_name', '')
+ SLACK_ICON_URL = check_setting_str(CFG, 'Slack', 'slack_icon_url', '')
+
USE_EMAIL = bool(check_setting_int(CFG, 'Email', 'use_email', 0))
EMAIL_OLD_SUBJECTS = bool(check_setting_int(CFG, 'Email', 'email_old_subjects',
None is not EMAIL_HOST and any(EMAIL_HOST)))
@@ -1821,6 +1840,16 @@ def save_config():
new_config['Pushbullet']['pushbullet_access_token'] = PUSHBULLET_ACCESS_TOKEN
new_config['Pushbullet']['pushbullet_device_iden'] = PUSHBULLET_DEVICE_IDEN
+ new_config['Slack'] = {}
+ new_config['Slack']['use_slack'] = int(USE_SLACK)
+ new_config['Slack']['slack_notify_onsnatch'] = int(SLACK_NOTIFY_ONSNATCH)
+ new_config['Slack']['slack_notify_ondownload'] = int(SLACK_NOTIFY_ONDOWNLOAD)
+ new_config['Slack']['slack_access_token'] = SLACK_ACCESS_TOKEN
+ new_config['Slack']['slack_channel'] = SLACK_CHANNEL
+ new_config['Slack']['slack_as_user'] = int(SLACK_AS_USER)
+ new_config['Slack']['slack_bot_name'] = SLACK_BOT_NAME
+ new_config['Slack']['slack_icon_url'] = SLACK_ICON_URL
+
new_config['Email'] = {}
new_config['Email']['use_email'] = int(USE_EMAIL)
new_config['Email']['email_old_subjects'] = int(EMAIL_OLD_SUBJECTS)
diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py
index 7faf568..48ac704 100644
--- a/sickbeard/notifiers/__init__.py
+++ b/sickbeard/notifiers/__init__.py
@@ -36,6 +36,7 @@ import nma
import pushalot
import pushbullet
+import slack
import tweet
from lib import libtrakt
import emailnotify
@@ -60,6 +61,7 @@ nma_notifier = nma.NMA_Notifier()
pushalot_notifier = pushalot.PushalotNotifier()
pushbullet_notifier = pushbullet.PushbulletNotifier()
# social
+slack_notifier = slack.SlackNotifier()
twitter_notifier = tweet.TwitterNotifier()
trakt_notifier = trakt.TraktNotifier()
email_notifier = emailnotify.EmailNotifier()
@@ -81,6 +83,7 @@ notifiers = [
nma_notifier,
pushalot_notifier,
pushbullet_notifier,
+ slack_notifier,
twitter_notifier,
trakt_notifier,
email_notifier,
diff --git a/sickbeard/notifiers/slack.py b/sickbeard/notifiers/slack.py
new file mode 100644
index 0000000..6f7c371
--- /dev/null
+++ b/sickbeard/notifiers/slack.py
@@ -0,0 +1,90 @@
+# coding=utf-8
+#
+# This file is part of SickGear.
+#
+# Thanks to: mallen86
+#
+# SickGear is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickGear is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickGear. If not, see
.
+
+import json
+import sickbeard
+
+from sickbeard import logger, common
+
+class SlackNotifier:
+
+ def _send_to_slack(self, message, accessToken, channel, as_user, bot_name, icon_url):
+ SLACK_ENDPOINT = "https://slack.com/api/chat.postMessage"
+
+ data = {}
+ data["token"] = accessToken
+ data["channel"] = channel
+ data["username"] = bot_name
+ data["text"] = message
+ data["icon_url"] = icon_url
+ data["as_user"] = as_user
+
+ urlResp = sickbeard.helpers.getURL(url=SLACK_ENDPOINT, post_data=data)
+ if urlResp:
+ resp = json.loads(urlResp)
+ else:
+ return False
+
+ # if ("error" in resp):
+ # raise Exception(resp["error"])
+
+ if (resp["ok"] == True):
+ logger.log(u"Slack: Succeeded sending message.", logger.MESSAGE)
+ return True
+
+ logger.log(u"Slack: Failed sending message: " + resp["error"], logger.ERROR)
+ return False
+
+ def _notify(self, message, accessToken='', channel='', as_user='', bot_name='', icon_url='', force=False):
+ # suppress notifications if the notifier is disabled but the notify options are checked
+ if not sickbeard.USE_SLACK and not force:
+ return False
+
+ if not accessToken:
+ accessToken = sickbeard.SLACK_ACCESS_TOKEN
+ if not channel:
+ channel = sickbeard.SLACK_CHANNEL
+ if not as_user:
+ as_user = sickbeard.SLACK_AS_USER
+ if not bot_name:
+ bot_name = sickbeard.SLACK_BOT_NAME
+ if not icon_url:
+ icon_url = sickbeard.SLACK_ICON_URL
+
+ return self._send_to_slack(message, accessToken, channel, as_user, bot_name, icon_url)
+
+##############################################################################
+# Public functions
+##############################################################################
+
+ def notify_snatch(self, ep_name):
+ if sickbeard.SLACK_NOTIFY_ONSNATCH:
+ self._notify(common.notifyStrings[common.NOTIFY_SNATCH] + ': ' + ep_name)
+
+ def notify_download(self, ep_name):
+ if sickbeard.SLACK_NOTIFY_ONDOWNLOAD:
+ self._notify(common.notifyStrings[common.NOTIFY_DOWNLOAD] + ': ' + ep_name)
+
+ def test_notify(self, accessToken, channel, as_user, bot_name, icon_url):
+ return self._notify("This is a test notification from SickGear", accessToken, channel, as_user, bot_name, icon_url, force=True)
+
+ def update_library(self, ep_obj):
+ pass
+
+notifier = SlackNotifier
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 31486cf..bfe6f07 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -1150,6 +1150,15 @@ class Home(MainHandler):
return notifiers.pushbullet_notifier.get_devices(accessToken)
+ def testSlack(self, accessToken=None, channel=None, as_user=False, bot_name=None, icon_url=None):
+ self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
+
+ result = notifiers.slack_notifier.test_notify(accessToken, channel, as_user, bot_name, icon_url)
+ if result:
+ return "Slack notification succeeded. Check your Slack clients to make sure it worked"
+ else:
+ return "Error sending Slack notification"
+
def viewchanges(self):
t = PageTemplate(headers=self.request.headers, file='viewchanges.tmpl')
@@ -5750,7 +5759,8 @@ class ConfigNotifications(Config):
use_email=None, email_notify_onsnatch=None, email_notify_ondownload=None,
email_notify_onsubtitledownload=None, email_host=None, email_port=25, email_from=None,
email_tls=None, email_user=None, email_password=None, email_list=None, email_show_list=None,
- email_show=None, **kwargs):
+ email_show=None, use_slack=None, slack_notify_onsnatch=None, slack_notify_ondownload=None,
+ slack_access_token=None, slack_channel=None, slack_as_user=None, slack_bot_name=None, slack_icon_url=None, **kwargs):
results = []
@@ -5889,6 +5899,15 @@ class ConfigNotifications(Config):
# sickbeard.TRAKT_REMOVE_SERIESLIST = config.checkbox_to_value(trakt_remove_serieslist)
# sickbeard.TRAKT_START_PAUSED = config.checkbox_to_value(trakt_start_paused)
+ sickbeard.USE_SLACK = config.checkbox_to_value(use_slack)
+ sickbeard.SLACK_NOTIFY_ONSNATCH = config.checkbox_to_value(slack_notify_onsnatch)
+ sickbeard.SLACK_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(slack_notify_ondownload)
+ sickbeard.SLACK_ACCESS_TOKEN = slack_access_token
+ sickbeard.SLACK_CHANNEL = slack_channel
+ sickbeard.SLACK_AS_USER = config.checkbox_to_value(slack_as_user)
+ sickbeard.SLACK_BOT_NAME = slack_bot_name
+ sickbeard.SLACK_ICON_URL = slack_icon_url
+
sickbeard.USE_EMAIL = config.checkbox_to_value(use_email)
sickbeard.EMAIL_NOTIFY_ONSNATCH = config.checkbox_to_value(email_notify_onsnatch)
sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload)
@@ -6323,4 +6342,3 @@ class CachedImages(MainHandler):
self.set_header('Content-Type', mime_type)
with open(static_image_path, 'rb') as img:
return img.read()
-