Package pushnotify :: Module prowl
[hide private]
[frames] | no frames]

Source Code for Module pushnotify.prowl

  1  #!/usr/bin/env python 
  2  # vim: set fileencoding=utf-8 
  3   
  4  """Module for sending push notifications to iOS devices that have 
  5  Prowl installed. See http://www.prowlapp.com/ for more 
  6  information. 
  7   
  8  copyright: Copyright (c) Jeffrey Goettsch and other contributors. 
  9  license: BSD, see LICENSE for details. 
 10   
 11  """ 
 12   
 13   
 14  import logging 
 15  import urllib 
 16  import urllib2 
 17  try: 
 18      from xml.etree import cElementTree 
 19      ElementTree = cElementTree 
 20  except ImportError: 
 21      from xml.etree import ElementTree 
 22   
 23  from pushnotify import exceptions 
 24   
 25   
 26  PUBLIC_API_URL = u'https://api.prowlapp.com/publicapi' 
 27  VERIFY_URL = u'/'.join([PUBLIC_API_URL, 'verify']) 
 28  NOTIFY_URL = u'/'.join([PUBLIC_API_URL, 'add']) 
 29  RETRIEVE_TOKEN_URL = u'/'.join([PUBLIC_API_URL, 'retrieve', 'token']) 
 30  RETRIEVE_APIKEY_URL = u'/'.join([PUBLIC_API_URL, 'retrieve', 'apikey']) 
 31   
 32   
33 -class Client(object):
34 """Client for sending push notificiations to iOS devices with 35 the Prowl application installed. 36 37 Member Vars: 38 apikeys: A list of strings, each containing a 40 character api 39 key. 40 providerkey: A string containing a 40 character provider key. 41 42 """ 43
44 - def __init__(self, apikeys=None, providerkey=None):
45 """Initialize the Prowl client. 46 47 Args: 48 apikeys: A list of strings of 40 characters each, each 49 containing a valid api key. 50 providerkey: A string of 40 characters containing a valid 51 provider key. 52 53 """ 54 55 self.logger = logging.getLogger('{0}.{1}'.format( 56 self.__module__, self.__class__.__name__)) 57 58 self._browser = urllib2.build_opener(urllib2.HTTPSHandler()) 59 self._last_type = None 60 self._last_code = None 61 self._last_message = None 62 self._last_remaining = None 63 self._last_resetdate = None 64 self._last_token = None 65 self._last_token_url = None 66 self._last_apikey = None 67 68 self.apikeys = [] if apikeys is None else apikeys 69 self.providerkey = providerkey
70
71 - def _get(self, url):
72 73 self.logger.debug('_get requesting url: {0}'.format(url)) 74 75 request = urllib2.Request(url) 76 try: 77 response_stream = self._browser.open(request) 78 except urllib2.HTTPError, exc: 79 return exc 80 else: 81 return response_stream
82
83 - def _parse_response(self, response, verify=False):
84 85 xmlresp = response.read() 86 self.logger.info('received response: {0}'.format(xmlresp)) 87 88 root = ElementTree.fromstring(xmlresp) 89 90 self._last_type = root[0].tag.lower() 91 self._last_code = root[0].attrib['code'] 92 93 if self._last_type == 'success': 94 self._last_message = None 95 self._last_remaining = root[0].attrib['remaining'] 96 self._last_resetdate = root[0].attrib['resetdate'] 97 elif self._last_type == 'error': 98 self._last_message = root[0].text 99 self._last_remaining = None 100 self._last_resetdate = None 101 102 if (not verify or 103 (self._last_code != '400' and self._last_code != '401')): 104 self._raise_exception() 105 else: 106 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 107 108 if len(root) > 1: 109 if root[1].tag.lower() == 'retrieve': 110 if 'token' in root[1].attrib: 111 self._last_token = root[1].attrib['token'] 112 self._last_token_url = root[1].attrib['url'] 113 self._last_apikey = None 114 elif 'apikey' in root[1].attrib: 115 self._last_token = None 116 self.last_token_url = None 117 self._last_apikey = root[1].attrib['apikey'] 118 else: 119 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 120 else: 121 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 122 123 return root
124
125 - def _post(self, url, data):
126 127 self.logger.debug('_post sending data: {0}'.format(data)) 128 self.logger.debug('_post sending to url: {0}'.format(url)) 129 130 request = urllib2.Request(url, data) 131 try: 132 response_stream = self._browser.open(request) 133 except urllib2.HTTPError, exc: 134 return exc 135 else: 136 return response_stream
137
138 - def _raise_exception(self):
139 140 if self._last_code == '400': 141 raise exceptions.FormatError(self._last_message, 142 int(self._last_code)) 143 elif self._last_code == '401': 144 if 'provider' not in self._last_message.lower(): 145 raise exceptions.ApiKeyError(self._last_message, 146 int(self._last_code)) 147 else: 148 raise exceptions.ProviderKeyError(self._last_message, 149 int(self._last_code)) 150 elif self._last_code == '406': 151 raise exceptions.RateLimitExceeded(self._last_message, 152 int(self._last_code)) 153 elif self._last_code == '409': 154 raise exceptions.PermissionDenied(self._last_message, 155 int(self._last_code)) 156 elif self._last_code == '500': 157 raise exceptions.ServerError(self._last_message, 158 int(self._last_code)) 159 else: 160 raise exceptions.UnknownError(self._last_message, 161 int(self._last_code))
162
163 - def notify(self, app, event, desc, kwargs=None):
164 """Send a notification to each apikey in self.apikeys. 165 166 Args: 167 app: A string of up to 256 characters containing the name 168 of the application sending the notification. 169 event: A string of up to 1024 characters containing the 170 event that is being notified (i.e. subject or brief 171 description.) 172 desc: A string of up to 10000 characters containing the 173 notification text. 174 kwargs: A dictionary with any of the following strings as 175 keys: 176 priority: An integer between -2 and 2, indicating the 177 priority of the notification. -2 is the lowest, 2 is 178 the highest, and 0 is normal. 179 url: A string of up to 512 characters containing a URL 180 to attach to the notification. 181 (default: None) 182 183 Raises: 184 pushnotify.exceptions.FormatError 185 pushnotify.exceptions.ApiKeyError 186 pushnotify.exceptions.RateLimitExceeded 187 pushnotify.exceptions.ServerError 188 pushnotify.exceptions.UnknownError 189 pushnotify.exceptions.UnrecognizedResponseError 190 191 """ 192 193 data = {'apikey': ','.join(self.apikeys), 194 'application': app, 195 'event': event, 196 'description': desc} 197 198 if self.providerkey: 199 data['providerkey'] = self.providerkey 200 201 if kwargs: 202 data.update(kwargs) 203 204 data = urllib.urlencode(data) 205 206 response = self._post(NOTIFY_URL, data) 207 self._parse_response(response)
208
209 - def retrieve_apikey(self, token):
210 """Get an API key for a given token. 211 212 Once a user has approved you sending them push notifications, 213 you can supply the returned token here and get an API key. 214 215 Args: 216 token: A string containing a registration token returned 217 from the retrieve_token method. 218 219 Raises: 220 pushnotify.exceptions.ProviderKeyError 221 222 Returns: 223 A string containing the API key. 224 225 """ 226 227 data = {'providerkey': self.providerkey, 228 'token': token} 229 230 querystring = urllib.urlencode(data) 231 url = '?'.join([RETRIEVE_APIKEY_URL, querystring]) 232 233 response = self._get(url) 234 self._parse_response(response) 235 236 return self._last_apikey
237
238 - def retrieve_token(self):
239 """Get a registration token and approval URL. 240 241 A user follows the URL and logs in to the Prowl website to 242 approve you sending them push notifications. If you've 243 associated a 'Retrieve success URL' with your provider key, they 244 will be redirected there. 245 246 Raises: 247 pushnotify.exceptions.ProviderKeyError 248 249 Returns: 250 A two-item tuple where the first item is a string containing 251 a registration token, and the second item is a string 252 containing the associated URL. 253 """ 254 255 data = {'providerkey': self.providerkey} 256 257 querystring = urllib.urlencode(data) 258 url = '?'.join([RETRIEVE_TOKEN_URL, querystring]) 259 260 response = self._get(url) 261 self._parse_response(response) 262 263 return self._last_token, self._last_token_url
264
265 - def verify_user(self, apikey):
266 """Verify an API key for a user. 267 268 Args: 269 apikey: A string of 40 characters containing an API key. 270 271 Raises: 272 pushnotify.exceptions.RateLimitExceeded 273 pushnotify.exceptions.ServerError 274 pushnotify.exceptions.UnknownError 275 pushnotify.exceptions.UnrecognizedResponseError 276 277 Returns: 278 A boolean containing True if the API key is valid, and False 279 if it is not. 280 281 """ 282 283 data = {'apikey': apikey} 284 285 if self.providerkey: 286 data['providerkey'] = self.providerkey 287 288 querystring = urllib.urlencode(data) 289 url = '?'.join([VERIFY_URL, querystring]) 290 291 response = self._get(url) 292 self._parse_response(response, True) 293 294 return self._last_code == '200'
295 296 if __name__ == '__main__': 297 pass 298