Notifications

This commit is contained in:
Ruud committed 2011-04-09 20:18:51 +02:00
1 parent 288aa8c0a1
commit 85fc1c01ee
67 files changed
+3270 -400

No files matched your search

Whitespace-only changes.
+29
View File
@@ -0,0 +1,29 @@
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.request import jsonified
from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
class Notification(Plugin):
default_title = 'CouchPotato'
test_message = 'ZOMG Lazors Pewpewpew!'
def __init__(self):
addEvent('notify', self.notify)
def notify(self, message = '', data = {}):
pass
def conf(self, attr):
return Env.setting(attr, self.__class__.__name__.lower())
def isDisabled(self):
return not self.isEnabled()
def isEnabled(self):
return self.conf('enabled', True)
def test(self):
success = self.notify(message = self.test_message)
return jsonified({'success': success})
@@ -0,0 +1,33 @@
from .main import Growl
def start():
return Growl()
config = [{
'name': 'growl',
'groups': [
{
'tab': 'notifications',
'name': 'growl',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'password',
'default': '',
'type': 'password',
'description': '',
},
],
}
],
}]
@@ -0,0 +1,111 @@
# Based on netprowl by the following authors.
# Altered 1st October 2010 - Tim Child.
# Have added the ability for the command line arguments to take a password.
# Altered 1-17-2010 - Tanner Stokes - www.tannr.com
# Added support for command line arguments
# ORIGINAL CREDITS
# """Growl 0.6 Network Protocol Client for Python"""
# __version__ = "0.6.3"
# __author__ = "Rui Carmo (http://the.taoofmac.com)"
# __copyright__ = "(C) 2004 Rui Carmo. Code under BSD License."
# __contributors__ = "Ingmar J Stein (Growl Team), John Morrissey (hashlib patch)"
import struct
try:
import hashlib
md5_constructor = hashlib.md5
except ImportError:
import md5
md5_constructor = md5.new
GROWL_UDP_PORT = 9887
GROWL_PROTOCOL_VERSION = 1
GROWL_TYPE_REGISTRATION = 0
GROWL_TYPE_NOTIFICATION = 1
class GrowlRegistrationPacket:
"""Builds a Growl Network Registration packet.
Defaults to emulating the command-line growlnotify utility."""
def __init__(self, application = "CouchPotato", password = None):
self.notifications = []
self.defaults = [] # array of indexes into notifications
self.application = application.encode("utf-8")
self.password = password
def addNotification(self, notification = "General Notification", enabled = True):
"""Adds a notification type and sets whether it is enabled on the GUI"""
self.notifications.append(notification)
if enabled:
self.defaults.append(len(self.notifications) - 1)
def payload(self):
"""Returns the packet payload."""
self.data = struct.pack("!BBH",
GROWL_PROTOCOL_VERSION,
GROWL_TYPE_REGISTRATION,
len(self.application)
)
self.data += struct.pack("BB",
len(self.notifications),
len(self.defaults)
)
self.data += self.application
for notification in self.notifications:
encoded = notification.encode("utf-8")
self.data += struct.pack("!H", len(encoded))
self.data += encoded
for default in self.defaults:
self.data += struct.pack("B", default)
self.checksum = md5()
self.checksum.update(self.data)
if self.password:
self.checksum.update(self.password)
self.data += self.checksum.digest()
return self.data
class GrowlNotificationPacket:
"""Builds a Growl Network Notification packet.
Defaults to emulating the command-line growlnotify utility."""
def __init__(self, application = "CouchPotato",
notification = "General Notification", title = "Title",
description = "Description", priority = 0, sticky = False, password = None):
self.application = application.encode("utf-8")
self.notification = notification.encode("utf-8")
self.title = title.encode("utf-8")
self.description = description.encode("utf-8")
flags = (priority & 0x07) * 2
if priority < 0:
flags |= 0x08
if sticky:
flags = flags | 0x0100
self.data = struct.pack("!BBHHHHH",
GROWL_PROTOCOL_VERSION,
GROWL_TYPE_NOTIFICATION,
flags,
len(self.notification),
len(self.title),
len(self.description),
len(self.application)
)
self.data += self.notification
self.data += self.title
self.data += self.description
self.data += self.application
self.checksum = md5_constructor()
self.checksum.update(self.data)
if password:
self.checksum.update(password)
self.data += self.checksum.digest()
def payload(self):
"""Returns the packet payload."""
return self.data
@@ -0,0 +1,45 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from couchpotato.core.notifications.growl.growl import GROWL_UDP_PORT, \
GrowlRegistrationPacket, GrowlNotificationPacket
from couchpotato.environment import Env
from socket import AF_INET, SOCK_DGRAM, socket
log = CPLog(__name__)
class Growl(Notification):
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.growl', self.notify)
addApiView('notify.growl.test', self.test)
def conf(self, attr):
return Env.setting(attr, 'growl')
def notify(self, message = '', data = {}):
if self.isDisabled():
return
hosts = [x.strip() for x in self.conf('host').split(",")]
password = self.conf('password')
for curHost in hosts:
addr = (curHost, GROWL_UDP_PORT)
s = socket(AF_INET, SOCK_DGRAM)
p = GrowlRegistrationPacket(password = password)
p.addNotification()
s.sendto(p.payload(), addr)
# send notification
p = GrowlNotificationPacket(title = self.default_title, description = message, priority = 0, sticky = False, password = password)
s.sendto(p.payload(), addr)
s.close()
log.info('Growl notifications sent.')
@@ -0,0 +1,37 @@
from .main import NMJ
def start():
return NMJ()
config = [{
'name': 'nmj',
'groups': [
{
'tab': 'notifications',
'name': 'nmj',
'label': 'NMJ',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'database',
'default': '',
'description': '',
},
{
'name': 'mount',
'default': '',
'description': '',
},
],
}
],
}]
+131
View File
@@ -0,0 +1,131 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from couchpotato.environment import Env
import re
import telnetlib
import urllib
import urllib2
try:
import xml.etree.cElementTree as etree
except ImportError:
import xml.etree.ElementTree as etree
log = CPLog(__name__)
class NMJ(Notification):
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.nmj', self.notify)
addApiView('notify.nmj.test', self.test)
addApiView('notify.nmj.auto_config', self.autoConfig)
def conf(self, attr):
return Env.setting(attr, 'nmj')
def autoConfig(self):
params = getParams()
host = params.get('host', 'localhost')
database = ''
mount = ''
try:
terminal = telnetlib.Telnet(host)
except Exception:
log.error('Warning: unable to get a telnet session to %s' % (host))
return self.failed()
log.debug('Connected to %s via telnet' % (host))
terminal.read_until('sh-3.00# ')
terminal.write('cat /tmp/source\n')
terminal.write('cat /tmp/netshare\n')
terminal.write('exit\n')
tnoutput = terminal.read_all()
match = re.search(r'(.+\.db)\r\n?(.+)(?=sh-3.00# cat /tmp/netshare)', tnoutput)
if match:
database = match.group(1)
device = match.group(2)
log.info('Found NMJ database %s on device %s' % (database, device))
else:
log.error('Could not get current NMJ database on %s, NMJ is probably not running!' % (host))
return self.failed()
if device.startswith('NETWORK_SHARE/'):
match = re.search('.*(?=\r\n?%s)' % (re.escape(device[14:])), tnoutput)
if match:
mount = match.group().replace('127.0.0.1', host)
log.info('Found mounting url on the Popcorn Hour in configuration: %s' % (mount))
else:
log.error('Detected a network share on the Popcorn Hour, but could not get the mounting url')
return self.failed()
return jsonified({
'success': True,
'database': database,
'mount': mount,
})
def notify(self, message = '', data = {}):
if self.isDisabled():
return False
host = self.conf('host')
mount = self.conf('mount')
database = self.conf('database')
if self.mount:
try:
req = urllib2.Request(mount)
log.debug('Try to mount network drive via url: %s' % (mount))
handle = urllib2.urlopen(req)
except IOError, e:
log.error('Warning: Couldn\'t contact popcorn hour on host %s: %s' % (host, e))
return False
params = {
'arg0': 'scanner_start',
'arg1': database,
'arg2': 'background',
'arg3': '',
}
params = urllib.urlencode(params)
UPDATE_URL = 'http://%(host)s:8008/metadata_database?%(params)s'
updateUrl = UPDATE_URL % {'host': host, 'params': params}
try:
req = urllib2.Request(updateUrl)
log.debug('Sending NMJ scan update command via url: %s' % (updateUrl))
handle = urllib2.urlopen(req)
response = handle.read()
except IOError, e:
log.error('Warning: Couldn\'t contact Popcorn Hour on host %s: %s' % (host, e))
return False
try:
et = etree.fromstring(response)
result = et.findtext('returnValue')
except SyntaxError, e:
log.error('Unable to parse XML returned from the Popcorn Hour: %s' % (e))
return False
if int(result) > 0:
log.error('Popcorn Hour returned an errorcode: %s' % (result))
return False
else:
log.info('NMJ started background scan')
return True
def failed(self):
return jsonified({'success': False})
@@ -0,0 +1,32 @@
from .main import Notifo
def start():
return Notifo()
config = [{
'name': 'notifo',
'groups': [
{
'tab': 'notifications',
'name': 'notifo',
'description': '',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
},
{
'name': 'username',
'default': '',
'type': 'string',
},
{
'name': 'password',
'default': '',
'type': 'password',
},
],
}
],
}]
@@ -0,0 +1,53 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from couchpotato.environment import Env
from flask.helpers import json
import base64
import urllib
import urllib2
log = CPLog(__name__)
class Notifo(Notification):
url = 'https://api.notifo.com/v1/send_notification'
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.notifo', self.notify)
addApiView('notify.notifo.test', self.test)
def conf(self, attr):
return Env.setting(attr, 'notifo')
def notify(self, message = '', data = {}):
if self.isDisabled():
return False
try:
data = urllib.urlencode({
'msg': toUnicode(message),
})
req = urllib2.Request(self.url)
authHeader = "Basic %s" % base64.encodestring('%s:%s' % (self.conf('username'), self.conf('api_key')))[:-1]
req.add_header("Authorization", authHeader)
handle = urllib2.urlopen(req, data)
result = json.load(handle)
if result['status'] != 'success' or result['response_message'] != 'OK':
raise Exception
except Exception, e:
log.error('Notification failed: %s' % e)
return False
log.info('Notifo notification successful.')
return True
@@ -0,0 +1,33 @@
from .main import Plex
def start():
return Plex()
config = [{
'name': 'plex',
'groups': [
{
'tab': 'notifications',
'name': 'plex',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'password',
'default': '',
'type': 'password',
'description': '',
},
],
}
],
}]
@@ -0,0 +1,43 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from xml.dom import minidom
import urllib
log = CPLog(__name__)
class Plex(Notification):
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.plex', self.notify)
addApiView('notify.plex.test', self.test)
def notify(self, message = '', data = {}):
if self.isDisabled():
return
log.info('Sending notification to Plex')
hosts = [x.strip() for x in self.conf('host').split(",")]
for host in hosts:
source_type = ['movie']
base_url = 'http://%s/library/sections' % host
refresh_url = '%s/%%s/refresh' % base_url
try:
xml_sections = minidom.parse(urllib.urlopen(base_url))
sections = xml_sections.getElementsByTagName('Directory')
for s in sections:
if s.getAttribute('type') in source_type:
url = refresh_url % s.getAttribute('key')
x = urllib.urlopen(url)
except:
log.error('Plex library update failed for %s.' % host)
return True
@@ -0,0 +1,35 @@
from .main import Prowl
def start():
return Prowl()
config = [{
'name': 'prowl',
'groups': [
{
'tab': 'notifications',
'name': 'prowl',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
'description': '',
},
{
'name': 'api_key',
'default': '',
'label': 'Api key',
'description': '',
},
{
'name': 'priority',
'default': '0',
'type': 'dropdown',
'description': '',
'values': [('Very Low', -2), ('Moderate', -1), ('Normal', 0), ('High', 1), ('Emergency', 2)]
},
],
}
],
}]
@@ -0,0 +1,51 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from httplib import HTTPSConnection
from urllib import urlencode
log = CPLog(__name__)
class Prowl(Notification):
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.prowl', self.notify)
addApiView('notify.prowl.test', self.test)
def notify(self, message = '', data = {}):
if self.isDisabled():
return
http_handler = HTTPSConnection('api.prowlapp.com')
data = {
'apikey': self.conf('api_key'),
'application': self.default_title,
'event': self.default_title,
'description': toUnicode(message),
'priority': self.conf('priority'),
}
http_handler.request('POST',
'/publicapi/add',
headers = {'Content-type': 'application/x-www-form-urlencoded'},
body = urlencode(data)
)
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
log.info('Prowl notifications sent.')
return True
elif request_status == 401:
log.error('Prowl auth failed: %s' % response.reason)
return False
else:
log.error('Prowl notification failed.')
return False
@@ -0,0 +1,38 @@
from .main import XBMC
def start():
return XBMC()
config = [{
'name': 'xbmc',
'groups': [
{
'tab': 'notifications',
'name': 'xbmc',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost:8080',
'description': '',
},
{
'name': 'username',
'default': 'xbmc',
'description': '',
},
{
'name': 'password',
'default': 'xbmc',
'type': 'password',
'description': '',
},
],
}
],
}]
@@ -0,0 +1,47 @@
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
import base64
import urllib
import urllib2
log = CPLog(__name__)
class XBMC(Notification):
def __init__(self):
addEvent('notify', self.notify)
addEvent('notify.xbmc', self.notify)
addApiView('notify.xbmc.test', self.test)
def notify(self, message = '', data = {}):
if self.isDisabled():
return
for host in [x.strip() for x in self.conf('host').split(",")]:
self.send({'command': 'ExecBuiltIn', 'parameter': 'Notification(CouchPotato, %s)' % message}, host)
self.send({'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}, host)
return True
def send(self, command, host):
url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, urllib.urlencode(command))
try:
req = urllib2.Request(url)
if self.password:
authHeader = "Basic %s" % base64.encodestring('%s:%s' % (self.conf('username'), self.conf('password')))[:-1]
req.add_header("Authorization", authHeader)
urllib2.urlopen(req, timeout = 10).read()
except Exception, e:
log.error("Couldn't sent command to XBMC. %s" % e)
return False
log.info('XBMC notification to %s successful.' % host)
return True