1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Module for sending push notifications to Android devices that have
22 Notify My Android installed. See www.notifymyandroid.com/ for more
23 information.
24
25 """
26
27
28 import warnings
29 try:
30 from xml.etree import cElementTree
31 ElementTree = cElementTree
32 except ImportError:
33 from xml.etree import ElementTree
34
35 from pushnotify import abstract
36 from pushnotify import exceptions
37
38
39 PUBLIC_API_URL = u'https://www.notifymyandroid.com/publicapi'
40 VERIFY_URL = u'/'.join([PUBLIC_API_URL, 'verify'])
41 NOTIFY_URL = u'/'.join([PUBLIC_API_URL, 'notify'])
42
43 DESC_LIMIT = 10000
44
45
46 -class Client(abstract.AbstractClient):
47 """Client for sending push notificiations to Android devices with
48 the Notify My Android (NMA) application installed.
49
50 Member Vars:
51 developerkey: A string containing a valid developer key for the
52 NMA application.
53 application: A string containing the name of the application on
54 behalf of whom the NMA client will be sending messages.
55 apikeys: A dictionary where the keys are strings containing
56 valid user API keys, and the values are lists of strings,
57 each containing a valid user device key. Device keys are not
58 used by this client.
59
60 """
61
62 - def __init__(self, developerkey='', application=''):
63 """Initialize the Notify My Android client.
64
65 Args:
66 developerkey: A string containing a valid developer key for
67 the NMA application.
68 application: A string containing the name of the application
69 on behalf of whom the NMA client will be sending
70 messages.
71
72 """
73
74 super(self.__class__, self).__init__(developerkey, application)
75
76 self._type = 'nma'
77 self._urls = {'notify': NOTIFY_URL, 'verify': VERIFY_URL}
78
80
81 xmlresp = response.text
82 root = ElementTree.fromstring(xmlresp)
83
84 self._last['type'] = root[0].tag.lower()
85 self._last['code'] = root[0].attrib['code']
86
87 if self._last['type'] == 'success':
88 self._last['message'] = None
89 self._last['remaining'] = root[0].attrib['remaining']
90 self._last['resettimer'] = root[0].attrib['resettimer']
91 elif self._last['type'] == 'error':
92 self._last['message'] = root[0].text
93 self._last['remaining'] = None
94 self._last['resettimer'] = None
95
96 if (not verify or
97 (self._last['code'] != '400' and
98 self._last['code'] != '401')):
99 self._raise_exception()
100 else:
101 raise exceptions.UnrecognizedResponseError(xmlresp, -1)
102
103 return root
104
106
107 if self._last['code'] == '400':
108 raise exceptions.FormatError(self._last['message'],
109 int(self._last['code']))
110 elif self._last['code'] == '401':
111 raise exceptions.ApiKeyError(self._last['message'],
112 int(self._last['code']))
113 elif self._last['code'] == '402':
114 raise exceptions.RateLimitExceeded(self._last['message'],
115 int(self._last['code']))
116 elif self._last['code'] == '500':
117 raise exceptions.ServerError(self._last['message'],
118 int(self._last['code']))
119 else:
120 raise exceptions.UnknownError(self._last['message'],
121 int(self._last['code']))
122
123 - def notify(self, description, event, split=True, kwargs=None):
124 """Send a notification to each user's apikey in self.apikeys.
125
126 Args:
127 description: A string of up to DESC_LIMIT characters
128 containing the main notification text.
129 event: A string of up to 1000 characters containing a
130 subject or brief description of the event.
131 split: A boolean indicating whether to split long
132 descriptions among multiple notifications (True) or to
133 possibly raise an exception (False). (default True)
134 kwargs: A dictionary with any of the following strings as
135 keys:
136 priority: An integer between -2 and 2, indicating the
137 priority of the notification. -2 is the lowest, 2 is
138 the highest, and 0 is normal.
139 url: A string of up to 2000 characters containing a URL
140 to attach to the notification.
141 content_type: A string containing "text/html" (without
142 the quotes) that then allows some basic HTML to be
143 used while displaying the notification.
144 (default: None)
145
146 Raises:
147 pushnotify.exceptions.ApiKeyError
148 pushnotify.exceptions.FormatError
149 pushnotify.exceptions.RateLimitExceeded
150 pushnotify.exceptions.ServerError
151 pushnotify.exceptions.UnknownError
152 pushnotify.exceptions.UnrecognizedResponseError
153
154 """
155
156 def send_notify(description, event, kwargs):
157 data = {'apikey': ','.join(self.apikeys),
158 'application': self.application,
159 'event': event,
160 'description': description}
161
162 if self.developerkey:
163 data['developerkey'] = self.developerkey
164
165 if kwargs:
166 data.update(kwargs)
167
168 response = self._post(self._urls['notify'], data)
169 self._parse_response(response)
170
171 if not self.apikeys:
172 self.logger.warn('notify called with no users set')
173 return
174
175 if split:
176 while description:
177 this_desc = description[0:DESC_LIMIT]
178 description = description[DESC_LIMIT:]
179 send_notify(this_desc, event, kwargs)
180 else:
181 send_notify(description, event, kwargs)
182
184 """This method is deprecated. Use verify_user instead.
185
186 """
187
188 msg = 'The verify method is deprecated. User verify_user instead.'
189 self.logger.warn(msg)
190 warnings.warn(msg, DeprecationWarning)
191
192 return self.verify_user(apikey)
193
195 """Verify a user's API key.
196
197 Args:
198 apikey: A string of 48 characters containing a user's API
199 key.
200
201 Raises:
202 pushnotify.exceptions.RateLimitExceeded
203 pushnotify.exceptions.ServerError
204 pushnotify.exceptions.UnknownError
205 pushnotify.exceptions.UnrecognizedResponseError
206
207 Returns:
208 A boolean containing True if the user's API key is valid,
209 and False if it is not.
210
211 """
212
213 data = {'apikey': apikey}
214
215 if self.developerkey:
216 data['developerkey'] = self.developerkey
217
218 response = self._get(self._urls['verify'], data)
219 self._parse_response(response, True)
220
221 return self._last['code'] == '200'
222
223 if __name__ == '__main__':
224 pass
225