Merge pull request #989 from JackDandy/feature/AddSlackNotifier

Feature/add slack notifier
This commit is contained in:
JackDandy 2017-09-16 15:16:02 +01:00 committed by GitHub
commit 326032023b
8 changed files with 254 additions and 3 deletions

View file

@ -110,6 +110,7 @@
* Add warn icon indicator of abandoned IDs to "Manage" menu bar and "Manage/Show Processes" menu item
* Add shows that have no replacement ID can be ignored at "Manage/Show Processes", the menu bar warn icon hides if all are ignored
* Change FreeBSD initscript to use command_interpreter
* Add Slack notifier
[develop changelog]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1683,6 +1683,112 @@
</fieldset>
</div><!-- /trakt component-group //-->
<div class="component-group">
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/slack.png" alt="" title="Slack" />
<h3><a href="<%= anon_url('https://slack.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Slack</a></h3>
<p>Team, group, and direct communication.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
<label for="use_slack">
<span class="component-title">Enable</span>
<span class="component-desc">
<input type="checkbox" class="enabler" name="use_slack" id="use_slack" #if $sickbeard.USE_SLACK then 'checked="checked"' else ''#>
<p>should SickGear send Slack notifications ?</p>
</span>
</label>
</div>
<div id="content_use_slack">
<div class="field-pair">
<label for="slack_notify_onsnatch">
<span class="component-title">Notify on snatch</span>
<span class="component-desc">
<input type="checkbox" name="slack_notify_onsnatch" id="slack_notify_onsnatch" #if $sickbeard.SLACK_NOTIFY_ONSNATCH then 'checked="checked"' else ''#>
<p>send a notification when a download starts ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="slack_notify_ondownload">
<span class="component-title">Notify on download</span>
<span class="component-desc">
<input type="checkbox" name="slack_notify_ondownload" id="slack_notify_ondownload" #if $sickbeard.SLACK_NOTIFY_ONDOWNLOAD then 'checked="checked"' else ''#>
<p>send a notification when a download finishes ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="slack_notify_onsubtitledownload">
<span class="component-title">Notify on subtitle download</span>
<span class="component-desc">
<input type="checkbox" name="slack_notify_onsubtitledownload" id="slack_notify_onsubtitledownload" #if $sickbeard.SLACK_NOTIFY_ONSUBTITLEDOWNLOAD then 'checked="checked"' else ''#>
<p>send a notification when subtitles are downloaded ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="slack_channel">
<span class="component-title">Slack channel</span>
<span class="component-desc">
<input type="text" name="slack_channel" id="slack_channel" value="$sickbeard.SLACK_CHANNEL" class="form-control input-sm input250">
<span style="float:left">channel to send notifications to</span>
</span>
</label>
</div>
<div class="field-pair">
<label for="slack_as_user">
<span class="component-title">Post as authed user</span>
<span class="component-desc">
<input type="checkbox" class="viewIf" name="slack_as_user" id="slack_as_user" #if $sickbeard.SLACK_AS_USER then 'checked="checked"' else ''#>
<p>send notifications using the profile name and photo of the access token</p>
</span>
</label>
</div>
<div class="hide_if_slack_as_user">
<div class="field-pair">
<label for="slack_bot_name">
<span class="component-title">Post as a custom name</span>
<span class="component-desc">
<input type="text" name="slack_bot_name" id="slack_bot_name" value="$sickbeard.SLACK_BOT_NAME" class="form-control input-sm input250">
<p>blank for 'SickGear'</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="slack_icon_url">
<span class="component-title">Custom icon image url</span>
<span class="component-desc">
<input type="text" name="slack_icon_url" id="slack_icon_url" value="$sickbeard.SLACK_ICON_URL" class="form-control input-sm input250">
<p>blank for SickGear image</p>
</span>
</label>
</div>
</div>
<div class="field-pair">
<label for="slack_access_token">
<span class="component-title">Slack oauth access token</span>
<span class="component-desc">
<input type="text" name="slack_access_token" id="slack_access_token" value="$sickbeard.SLACK_ACCESS_TOKEN" class="form-control input-sm input350">
<div class="clear-left"><p>1. <a href="<%= anon_url('https://api.slack.com/apps/new') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">create a Slack app</a> name for SickGear and select a workspace<br>
2. under the 'Features' section of the left sidebar, click 'OAuth & Perms'<br>
3. scroll page to section 'Scopes' and open the menu 'Select Permission Scopes'<br>
4. in the 'Chat' menu section, select 'Send messages as <em class="grey-text">appname</em>' - (<em class="grey-text">set in step 1</em>)<br>
5. also, in the 'Chat' menu section, select 'Send messages as user'<br>
6. after 'Save', go to page top, click 'Install App to Workspace'<br>
7. click 'Authorize' to view the 'OAuth Access Token', paste it to the above field</p></div>
</span>
</label>
</div>
<div class="testNotification" id="testSlack-result">Click below to test.</div>
<input type="button" class="btn" value="Test Slack" id="testSlack" />
<input type="submit" class="btn config_submitter" value="Save Changes" />
</div><!-- /content_use_slack //-->
</fieldset>
</div><!-- /slack component-group //-->
<div class="component-group">
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/email.png" alt="" title="Email" />

View file

@ -434,6 +434,32 @@
});
});
$('#testSlack').click(function () {
var channel = '#slack_channel', slack_channel = $(channel).val(),
slack_as_user = $('#slack_as_user').prop('checked'),
slack_bot_name = $('#slack_bot_name').val(), slack_icon_url = $('#slack_icon_url').val(),
access_token = '#slack_access_token', slack_access_token = $(access_token).val();
$(channel + ', ' + access_token).removeClass('warning');
if (!slack_channel || !slack_access_token) {
$('#testSlack-result').html('Please fill out the necessary fields above.');
if (!slack_channel)
$(channel).addClass('warning');
if (!slack_access_token)
$(access_token).addClass('warning');
} else {
$(this).prop('disabled', true);
$('#testSlack-result').html(loading);
$.get(sbRoot + '/home/testSlack', {
'channel': slack_channel, 'as_user': slack_as_user,
'bot_name': slack_bot_name, 'icon_url': slack_icon_url, 'access_token': slack_access_token})
.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) {

View file

@ -418,6 +418,16 @@ PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD = False
PUSHBULLET_ACCESS_TOKEN = None
PUSHBULLET_DEVICE_IDEN = None
USE_SLACK = False
SLACK_NOTIFY_ONSNATCH = False
SLACK_NOTIFY_ONDOWNLOAD = False
SLACK_NOTIFY_ONSUBTITLEDOWNLOAD = False
SLACK_CHANNEL = None
SLACK_AS_USER = False
SLACK_BOT_NAME = None
SLACK_ICON_URL = None
SLACK_ACCESS_TOKEN = None
USE_EMAIL = False
EMAIL_OLD_SUBJECTS = None
EMAIL_NOTIFY_ONSNATCH = False
@ -631,6 +641,8 @@ 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_NOTIFY_ONSUBTITLEDOWNLOAD, \
SLACK_CHANNEL, SLACK_AS_USER, SLACK_BOT_NAME, SLACK_ICON_URL, SLACK_ACCESS_TOKEN, \
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 +652,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 +1045,16 @@ 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_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Slack', 'slack_notify_onsubtitledownload', 0))
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', '')
SLACK_ACCESS_TOKEN = check_setting_str(CFG, 'Slack', 'slack_access_token', '')
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 +1843,17 @@ 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_notify_onsubtitledownload'] = int(SLACK_NOTIFY_ONSUBTITLEDOWNLOAD)
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['Slack']['slack_access_token'] = SLACK_ACCESS_TOKEN
new_config['Email'] = {}
new_config['Email']['use_email'] = int(USE_EMAIL)
new_config['Email']['email_old_subjects'] = int(EMAIL_OLD_SUBJECTS)

View file

@ -36,6 +36,7 @@ import nma
import pushalot
import pushbullet
import slack
import tweet
from lib import libtrakt
import emailnotify
@ -62,6 +63,7 @@ pushbullet_notifier = pushbullet.PushbulletNotifier()
# social
twitter_notifier = tweet.TwitterNotifier()
trakt_notifier = trakt.TraktNotifier()
slack_notifier = slack.SlackNotifier()
email_notifier = emailnotify.EmailNotifier()
notifiers = [
@ -83,6 +85,7 @@ notifiers = [
pushbullet_notifier,
twitter_notifier,
trakt_notifier,
slack_notifier,
email_notifier,
]

View file

@ -0,0 +1,66 @@
# coding=utf-8
#
# This file is part of SickGear.
#
# Thanks to: mallen86, generica
#
# 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 <http://www.gnu.org/licenses/>.
import sickbeard
from sickbeard import common, logger
class SlackNotifier:
def __init__(self):
self.sg_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master' + \
'/gui/slick/images/ico/apple-touch-icon-precomposed.png'
def _notify(self, msg, channel='', as_user=False, bot_name='', icon_url='', access_token='', force=False):
custom = (force and not as_user) or (not (force or sickbeard.SLACK_AS_USER))
resp = (sickbeard.USE_SLACK or force) and sickbeard.helpers.getURL(
url='https://slack.com/api/chat.postMessage',
post_data=dict(
[('text', msg), ('token', (access_token, sickbeard.SLACK_ACCESS_TOKEN)[not access_token]),
('channel', (channel, sickbeard.SLACK_CHANNEL)[not channel]), ('as_user', not custom)] +
([], [('username', (bot_name, sickbeard.SLACK_BOT_NAME or 'SickGear')[not bot_name]),
('icon_url', (icon_url, sickbeard.SLACK_ICON_URL or self.sg_logo_url)[not icon_url])])[custom]),
json=True)
result = resp and resp['ok'] or resp['error']
if True is not result:
logger.log(u'Slack failed sending message, response: "%s"' % resp['error'], logger.ERROR)
return result
def _notify_str(self, pre_text, post_text):
return self._notify('%s: %s' % (common.notifyStrings[pre_text].strip('#: '), post_text))
def test_notify(self, channel, as_user, bot_name, icon_url, access_token):
return self._notify('This is a test notification from SickGear',
channel, as_user, bot_name, icon_url, access_token, force=True)
def notify_snatch(self, ep_name):
return sickbeard.SLACK_NOTIFY_ONSNATCH and self._notify_str(common.NOTIFY_SNATCH, ep_name)
def notify_download(self, ep_name):
return sickbeard.SLACK_NOTIFY_ONDOWNLOAD and self._notify_str(common.NOTIFY_DOWNLOAD, ep_name)
def notify_subtitle_download(self, ep_name, lang):
return sickbeard.SLACK_NOTIFY_ONSUBTITLEDOWNLOAD and \
self._notify_str(common.NOTIFY_SUBTITLE_DOWNLOAD, '%s: %s' % (ep_name, lang))
def notify_git_update(self, new_version='??'):
return self._notify_str(common.NOTIFY_GIT_UPDATE_TEXT, new_version)
notifier = SlackNotifier

View file

@ -1150,6 +1150,13 @@ class Home(MainHandler):
return notifiers.pushbullet_notifier.get_devices(accessToken)
def testSlack(self, access_token=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(channel, 'true' == as_user, bot_name, icon_url, access_token)
return ('Error sending notification, Slack response: "%s"' % result,
'Successful test notice sent. (Note: Slack clients display icon once in a sequence)')[True is result]
def viewchanges(self):
t = PageTemplate(headers=self.request.headers, file='viewchanges.tmpl')
@ -5750,7 +5757,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 +5897,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 +6340,3 @@ class CachedImages(MainHandler):
self.set_header('Content-Type', mime_type)
with open(static_image_path, 'rb') as img:
return img.read()