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  try: 
 15      from xml.etree import cElementTree 
 16      ElementTree = cElementTree 
 17  except ImportError: 
 18      from xml.etree import ElementTree 
 19   
 20  from pushnotify import abstract 
 21  from pushnotify import exceptions 
 22   
 23   
 24  PUBLIC_API_URL = u'https://api.prowlapp.com/publicapi' 
 25  VERIFY_URL = u'/'.join([PUBLIC_API_URL, 'verify']) 
 26  NOTIFY_URL = u'/'.join([PUBLIC_API_URL, 'add']) 
 27  RETRIEVE_TOKEN_URL = u'/'.join([PUBLIC_API_URL, 'retrieve', 'token']) 
 28  RETRIEVE_APIKEY_URL = u'/'.join([PUBLIC_API_URL, 'retrieve', 'apikey']) 
 29   
 30  DESC_LIMIT = 10000 
 31   
 32   
33 -class Client(abstract.AbstractClient):
34 """Client for sending push notificiations to iOS devices with 35 the Prowl application installed. 36 37 Member Vars: 38 developerkey: A string containing a valid provider key for the 39 Prowl application. 40 application: A string containing the name of the application on 41 behalf of whom the Prowl client will be sending messages. 42 apikeys: A dictionary where the keys are strings containing 43 valid user API keys, and the values are lists of strings, 44 each containing a valid user device key. Device keys are not 45 used by this client. 46 47 """ 48
49 - def __init__(self, developerkey='', application=''):
50 """Initialize the Prowl client. 51 52 Args: 53 developerkey: A string containing a valid provider key for 54 the Prowl application. 55 application: A string containing the name of the application 56 on behalf of whom the Prowl client will be sending 57 messages. 58 59 """ 60 61 super(self.__class__, self).__init__(developerkey, application) 62 63 self._type = 'prowl' 64 self._urls = {'notify': NOTIFY_URL, 'verify': VERIFY_URL, 65 'retrieve_token': RETRIEVE_TOKEN_URL, 66 'retrieve_apikey': RETRIEVE_APIKEY_URL}
67
68 - def _parse_response_stream(self, response_stream, verify=False):
69 70 xmlresp = response_stream.read() 71 self.logger.info('received response: {0}'.format(xmlresp)) 72 73 root = ElementTree.fromstring(xmlresp) 74 75 self._last['type'] = root[0].tag.lower() 76 self._last['code'] = root[0].attrib['code'] 77 78 if self._last['type'] == 'success': 79 self._last['message'] = None 80 self._last['remaining'] = root[0].attrib['remaining'] 81 self._last['resetdate'] = root[0].attrib['resetdate'] 82 elif self._last['type'] == 'error': 83 self._last['message'] = root[0].text 84 self._last['remaining'] = None 85 self._last['resetdate'] = None 86 87 if (not verify or 88 (self._last['code'] != '400' and 89 self._last['code'] != '401')): 90 self._raise_exception() 91 else: 92 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 93 94 if len(root) > 1: 95 if root[1].tag.lower() == 'retrieve': 96 if 'token' in root[1].attrib: 97 self._last['token'] = root[1].attrib['token'] 98 self._last['token_url'] = root[1].attrib['url'] 99 self._last['apikey'] = None 100 elif 'apikey' in root[1].attrib: 101 self._last['token'] = None 102 self._last['token_url'] = None 103 self._last['apikey'] = root[1].attrib['apikey'] 104 else: 105 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 106 else: 107 raise exceptions.UnrecognizedResponseError(xmlresp, -1) 108 109 return root
110
111 - def _raise_exception(self):
112 113 if self._last['code'] == '400': 114 raise exceptions.FormatError(self._last['message'], 115 int(self._last['code'])) 116 elif self._last['code'] == '401': 117 if 'provider' not in self._last['message'].lower(): 118 raise exceptions.ApiKeyError(self._last['message'], 119 int(self._last['code'])) 120 else: 121 raise exceptions.ProviderKeyError(self._last['message'], 122 int(self._last['code'])) 123 elif self._last['code'] == '406': 124 raise exceptions.RateLimitExceeded(self._last['message'], 125 int(self._last['code'])) 126 elif self._last['code'] == '409': 127 raise exceptions.PermissionDenied(self._last['message'], 128 int(self._last['code'])) 129 elif self._last['code'] == '500': 130 raise exceptions.ServerError(self._last['message'], 131 int(self._last['code'])) 132 else: 133 raise exceptions.UnknownError(self._last['message'], 134 int(self._last['code']))
135
136 - def notify(self, description, event, split=True, kwargs=None):
137 """Send a notification to each user's apikey in self.apikeys. 138 139 Args: 140 description: A string of up to DESC_LIMIT characters 141 containing the notification text. 142 event: A string of up to 1024 characters containing a 143 subject or brief description of the event. 144 split: A boolean indicating whether to split long 145 descriptions among multiple notifications (True) or to 146 possibly raise an exception (False). (default True) 147 kwargs: A dictionary with any of the following strings as 148 keys: 149 priority: An integer between -2 and 2, indicating the 150 priority of the notification. -2 is the lowest, 2 is 151 the highest, and 0 is normal. 152 url: A string of up to 512 characters containing a URL 153 to attach to the notification. 154 (default: None) 155 156 Raises: 157 pushnotify.exceptions.ApiKeyError 158 pushnotify.exceptions.FormatError 159 pushnotify.exceptions.RateLimitExceeded 160 pushnotify.exceptions.ServerError 161 pushnotify.exceptions.UnknownError 162 pushnotify.exceptions.UnrecognizedResponseError 163 164 """ 165 166 def send_notify(description, event, kwargs): 167 data = {'apikey': ','.join(self.apikeys), 168 'application': self.application, 169 'event': event, 170 'description': description} 171 172 if self.developerkey: 173 data['providerkey'] = self.developerkey 174 175 if kwargs: 176 data.update(kwargs) 177 178 response_stream = self._post(self._urls['notify'], data) 179 self._parse_response_stream(response_stream)
180 181 if not self.apikeys: 182 self.logger.warn('notify called with no users set') 183 return 184 185 if split: 186 while description: 187 this_desc = description[0:DESC_LIMIT] 188 description = description[DESC_LIMIT:] 189 send_notify(this_desc, event, kwargs) 190 else: 191 send_notify(description, event, kwargs)
192
193 - def retrieve_apikey(self, reg_token):
194 """Get a user's API key for a given registration token. 195 196 Once a user has approved you sending them push notifications, 197 you can supply the returned token here and get an API key. 198 199 Args: 200 reg_token: A string containing a registration token returned 201 from the retrieve_token method. 202 203 Raises: 204 pushnotify.exceptions.ProviderKeyError 205 206 Returns: 207 A string containing the API key. 208 209 """ 210 211 data = {'providerkey': self.developerkey, 212 'token': reg_token} 213 214 response_stream = self._get(self._urls['retrieve_apikey'], data) 215 self._parse_response_stream(response_stream) 216 217 return self._last['apikey']
218
219 - def retrieve_token(self):
220 """Get a registration token and approval URL. 221 222 A user follows the approval URL and logs in to the Prowl website 223 to approve you sending them push notifications. If you have 224 associated a 'Retrieve success URL' with your provider key, they 225 will be redirected there. 226 227 Raises: 228 pushnotify.exceptions.ProviderKeyError 229 230 Returns: 231 A two-item tuple where the first item is a string containing 232 a registration token, and the second item is a string 233 containing the associated URL. 234 """ 235 236 data = {'providerkey': self.developerkey} 237 238 response_stream = self._get(self._urls['retrieve_token'], data) 239 self._parse_response_stream(response_stream) 240 241 return self._last['token'], self._last['token_url']
242
243 - def verify_user(self, apikey):
244 """Verify a user's API key. 245 246 Args: 247 apikey: A string of 40 characters containing a user's API 248 key. 249 250 Raises: 251 pushnotify.exceptions.RateLimitExceeded 252 pushnotify.exceptions.ServerError 253 pushnotify.exceptions.UnknownError 254 pushnotify.exceptions.UnrecognizedResponseError 255 256 Returns: 257 A boolean containing True if the user's API key is valid, 258 and False if it is not. 259 260 """ 261 262 data = {'apikey': apikey} 263 264 if self.developerkey: 265 data['providerkey'] = self.developerkey 266 267 response_stream = self._get(self._urls['verify'], data) 268 self._parse_response_stream(response_stream, True) 269 270 return self._last['code'] == '200'
271 272 if __name__ == '__main__': 273 pass 274