diff --git a/couchpotato/core/notifications/notifymywp/__init__.py b/couchpotato/core/notifications/notifymywp/__init__.py new file mode 100644 index 0000000..76228e6 --- /dev/null +++ b/couchpotato/core/notifications/notifymywp/__init__.py @@ -0,0 +1,43 @@ +from .main import NotifyMyWP + +def start(): + return NotifyMyWP() + +config = [{ + 'name': 'notifymywp', + 'groups': [ + { + 'tab': 'notifications', + 'name': 'notifymywp', + 'label': 'Notify My Windows Phone', + 'options': [ + { + 'name': 'enabled', + 'default': 0, + 'type': 'enabler', + }, + { + 'name': 'api_key', + 'description': 'Multiple keys seperated by a comma. Maximum of 5.' + }, + { + 'name': 'dev_key', + 'advanced': True, + }, + { + 'name': 'priority', + 'default': 0, + 'type': 'dropdown', + 'values': [('Very Low', -2), ('Moderate', -1), ('Normal', 0), ('High', 1), ('Emergency', 2)], + }, + { + 'name': 'on_snatch', + 'default': 0, + 'type': 'bool', + 'advanced': True, + 'description': 'Also send message when movie is snatched.', + }, + ], + } + ], +}] diff --git a/couchpotato/core/notifications/notifymywp/main.py b/couchpotato/core/notifications/notifymywp/main.py new file mode 100644 index 0000000..4445af6 --- /dev/null +++ b/couchpotato/core/notifications/notifymywp/main.py @@ -0,0 +1,24 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.notifications.base import Notification +from pynmwp import PyNMWP + +log = CPLog(__name__) + + +class NotifyMyWP(Notification): + + def notify(self, message = '', data = {}): + + if self.isDisabled(): + return + + keys = self.conf('api_key').split(',') + p = PyNMWP(keys, self.conf('dev_key')) + + response = p.push(application = self.default_title, event = message, description = message, priority = self.conf('priority'), batch_mode = len(keys) > 1) + + for key in keys: + if not response[key]['Code'] == u'200': + log.error('Could not send notification to NotifyMyWindowsPhone (%s). %s' % (key, response[key]['message'])) + + return response diff --git a/libs/pynmwp/__init__.py b/libs/pynmwp/__init__.py new file mode 100644 index 0000000..de724b9 --- /dev/null +++ b/libs/pynmwp/__init__.py @@ -0,0 +1,134 @@ +from xml.dom.minidom import parseString +from httplib import HTTPSConnection +from urllib import urlencode + +__version__ = "0.1" + +API_SERVER = 'notifymywindowsphone.com' +ADD_PATH = '/publicapi/notify' + +USER_AGENT = "PyNMWP/v%s" % __version__ + +def uniq_preserve(seq): # Dave Kirby + # Order preserving + seen = set() + return [x for x in seq if x not in seen and not seen.add(x)] + +def uniq(seq): + # Not order preserving + return {}.fromkeys(seq).keys() + +class PyNMWP(object): + """PyNMWP(apikey=[], developerkey=None) +takes 2 optional arguments: + - (opt) apykey: might me a string containing 1 key or an array of keys + - (opt) developerkey: where you can store your developer key +""" + + def __init__(self, apikey = [], developerkey = None): + self._developerkey = None + self.developerkey(developerkey) + if apikey: + if type(apikey) == str: + apikey = [apikey] + self._apikey = uniq(apikey) + + def addkey(self, key): + "Add a key (register ?)" + if type(key) == str: + if not key in self._apikey: + self._apikey.append(key) + elif type(key) == list: + for k in key: + if not k in self._apikey: + self._apikey.append(k) + + def delkey(self, key): + "Removes a key (unregister ?)" + if type(key) == str: + if key in self._apikey: + self._apikey.remove(key) + elif type(key) == list: + for k in key: + if key in self._apikey: + self._apikey.remove(k) + + def developerkey(self, developerkey): + "Sets the developer key (and check it has the good length)" + if type(developerkey) == str and len(developerkey) == 48: + self._developerkey = developerkey + + def push(self, application = "", event = "", description = "", url = "", priority = 0, batch_mode = False): + """Pushes a message on the registered API keys. +takes 5 arguments: + - (req) application: application name [256] + - (req) event: event name [1000] + - (req) description: description [10000] + - (opt) url: url [512] + - (opt) priority: from -2 (lowest) to 2 (highest) (def:0) + - (opt) batch_mode: call API 5 by 5 (def:False) + +Warning: using batch_mode will return error only if all API keys are bad + cf: http://nma.usk.bz/api.php +""" + datas = { + 'application': application[:256].encode('utf8'), + 'event': event[:1024].encode('utf8'), + 'description': description[:10000].encode('utf8'), + 'priority': priority + } + + if url: + datas['url'] = url[:512] + + if self._developerkey: + datas['developerkey'] = self._developerkey + + results = {} + + if not batch_mode: + for key in self._apikey: + datas['apikey'] = key + res = self.callapi('POST', ADD_PATH, datas) + results[key] = res + else: + for i in range(0, len(self._apikey), 5): + datas['apikey'] = ",".join(self._apikey[i:i + 5]) + res = self.callapi('POST', ADD_PATH, datas) + results[datas['apikey']] = res + return results + + def callapi(self, method, path, args): + headers = { 'User-Agent': USER_AGENT } + if method == "POST": + headers['Content-type'] = "application/x-www-form-urlencoded" + http_handler = HTTPSConnection(API_SERVER) + http_handler.request(method, path, urlencode(args), headers) + resp = http_handler.getresponse() + + try: + res = self._parse_reponse(resp.read()) + except Exception, e: + res = {'type': "pynmwperror", + 'code': 600, + 'message': str(e) + } + pass + + return res + + def _parse_reponse(self, response): + root = parseString(response).firstChild + for elem in root.childNodes: + if elem.nodeType == elem.TEXT_NODE: continue + if elem.tagName == 'success': + res = dict(elem.attributes.items()) + res['message'] = "" + res['type'] = elem.tagName + return res + if elem.tagName == 'error': + res = dict(elem.attributes.items()) + res['message'] = elem.firstChild.nodeValue + res['type'] = elem.tagName + return res +