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