notificationhubs-rest-python 1.0.1__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: notificationhubs-rest-python
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: A Python library for sending notifications using Azure Notification Hubs REST API.
5
5
  Home-page: https://github.com/panevo/notificationhubs-rest-python
6
6
  Author: Karlo Krakan
@@ -0,0 +1,256 @@
1
+ import time
2
+ import hmac
3
+ import base64
4
+ import hashlib
5
+ import json
6
+ import requests
7
+
8
+ # allows urllib.parse.quote below to be compatible with Python 2
9
+ import six.moves.urllib as urllib
10
+
11
+
12
+ # Reference for REST API:
13
+ # https://msdn.microsoft.com/en-us/library/mt608572.aspx
14
+ # Reference for
15
+ class AzureNotification:
16
+ def __init__(self, notification_format=None, payload=None):
17
+ valid_formats = ['template', 'apple', 'gcm', 'windows', 'windowsphone', "adm", "baidu"]
18
+ if not any(x in notification_format for x in valid_formats):
19
+ raise Exception(
20
+ "Invalid Notification format. " +
21
+ "Must be one of the following - 'template', 'apple', 'gcm', 'windows', 'windowsphone', 'adm', 'baidu'")
22
+
23
+ self.format = notification_format
24
+ self.payload = payload
25
+
26
+ # array with keynames for headers
27
+ # Note: Some headers are mandatory: Windows: X-WNS-Type, WindowsPhone: X-NotificationType
28
+ # Note: For Apple you can set Expiry with header: ServiceBusNotification-ApnsExpiry
29
+ # in W3C DTF, YYYY-MM-DDThh:mmTZD (for example, 1997-07-16T19:20+01:00).
30
+ self.headers = None
31
+
32
+
33
+ class AzureNotificationHub:
34
+ DEVICE_TOKEN_HEADER_NAME = "ServiceBusNotification-DeviceHandle"
35
+ TAGS_HEADER_NAME = "ServiceBusNotification-Tags"
36
+ API_VERSION = "api-version=2015-04"
37
+ DEBUG_SEND = "&test"
38
+ SCHEDULED_NOTIFICATION_HEADER_NAME = 'ServiceBusNotification-ScheduleTime'
39
+ SCHEDULED_NOTIFICATION_FORMAT_HEADER_NAME = 'ServiceBusNotification-Format'
40
+
41
+ @classmethod
42
+ def __init__(cls, connection_string=None, hub_name=None, debug=0):
43
+ cls.HubName = hub_name
44
+ cls.Debug = debug
45
+
46
+ # Parse connection string
47
+ parts = connection_string.split(';')
48
+ if len(parts) != 3:
49
+ raise Exception("Invalid ConnectionString.")
50
+
51
+ for part in parts:
52
+ if part.startswith('Endpoint'):
53
+ cls.Endpoint = 'https' + part[11:]
54
+ if part.startswith('SharedAccessKeyName'):
55
+ cls.SasKeyName = part[20:]
56
+ if part.startswith('SharedAccessKey'):
57
+ cls.SasKeyValue = part[16:]
58
+
59
+ @staticmethod
60
+ def __get_expiry():
61
+ # By default returns an expiration of 5 minutes (=300 seconds) from now
62
+ return int(round(time.time() + 300))
63
+
64
+ @staticmethod
65
+ def __encode_base64(data):
66
+ return base64.b64encode(data)
67
+
68
+ @classmethod
69
+ def __sign_string(cls, to_sign):
70
+ key = cls.SasKeyValue.encode('utf-8')
71
+ to_sign = to_sign.encode('utf-8')
72
+ signed_hmac_sha256 = hmac.HMAC(key, to_sign, hashlib.sha256)
73
+ digest = signed_hmac_sha256.digest()
74
+ encoded_digest = cls.__encode_base64(digest)
75
+ return encoded_digest
76
+
77
+ @classmethod
78
+ def __generate_sas_token(cls):
79
+ target_uri = cls.Endpoint + cls.HubName
80
+ my_uri = urllib.parse.quote(target_uri, '').lower()
81
+ expiry = str(cls.__get_expiry())
82
+ to_sign = my_uri + '\n' + expiry
83
+ signature = urllib.parse.quote(cls.__sign_string(to_sign))
84
+ auth_format = 'SharedAccessSignature sig={0}&se={1}&skn={2}&sr={3}'
85
+ sas_token = auth_format.format(signature, expiry, cls.SasKeyName, my_uri)
86
+ return sas_token
87
+
88
+ @classmethod
89
+ def __make_http_request(cls, url, payload, headers):
90
+ if cls.Debug > 0:
91
+ # adding this querystring parameter gets detailed information about the PNS send notification outcome
92
+ url += cls.DEBUG_SEND
93
+ print("--- REQUEST ---")
94
+ print("URI: " + url)
95
+ print("Headers: " + json.dumps(headers, sort_keys=True, indent=4, separators=(' ', ': ')))
96
+ print("--- END REQUEST ---\n")
97
+
98
+ response = requests.post(url, data=payload, headers=headers)
99
+
100
+ if cls.Debug > 0:
101
+ # print out detailed response information for debugging purpose
102
+ print("\n\n--- RESPONSE ---")
103
+ print("Code:" + str(response.status_code) + " / Reason: " + response.reason)
104
+ print("Headers: " + json.dumps(dict(response.headers), sort_keys=True, indent=4, separators=(' ', ': ')))
105
+ print("Content:\n" + response.text)
106
+ print("--- END RESPONSE ---")
107
+
108
+ elif response.status_code != 201:
109
+ # Successful outcome of send message is HTTP 201 - Created
110
+ raise Exception(
111
+ "Error sending notification. Received HTTP code " + str(response.status_code) + " " + response.reason)
112
+
113
+ response.close()
114
+ return response.status_code
115
+
116
+ # scheduled_time must be UTC
117
+ @classmethod
118
+ def __send_notification(cls, notification, is_direct, tag_or_tag_expression=None, scheduled_time=None,
119
+ device_handle=None, skip_send=False):
120
+ json_platforms = ['template', 'apple', 'gcm', 'adm', 'baidu']
121
+
122
+ if any(x in notification.format for x in json_platforms):
123
+ content_type = "application/json"
124
+ payload_to_send = json.dumps(notification.payload)
125
+ else:
126
+ content_type = "application/xml"
127
+ payload_to_send = notification.payload
128
+
129
+ headers = {
130
+ 'Content-type': content_type,
131
+ 'Authorization': cls.__generate_sas_token(),
132
+ cls.SCHEDULED_NOTIFICATION_FORMAT_HEADER_NAME: notification.format
133
+ }
134
+
135
+ url = cls.Endpoint + cls.HubName
136
+
137
+ if scheduled_time is None:
138
+ url += '/messages'
139
+ else:
140
+ url += '/schedulednotifications'
141
+ # format for UTC date:
142
+ # "yyyy-MM-dd'T'HH:mm:ss" Ex: 2001-07-04T12:08:56
143
+ scheduled_time_header = scheduled_time.strftime("%Y-%m-%d'T'%H:%M:%S")
144
+ headers[cls.SCHEDULED_NOTIFICATION_HEADER_NAME] = scheduled_time_header
145
+
146
+ if is_direct:
147
+ if device_handle is None:
148
+ raise Exception("Error sending direct notification. Missing PNS device handle.")
149
+
150
+ url += "/?direct"
151
+ url += "&" + cls.API_VERSION
152
+
153
+ headers[cls.DEVICE_TOKEN_HEADER_NAME] = device_handle
154
+ else:
155
+ url += "/?" + cls.API_VERSION
156
+
157
+ if isinstance(tag_or_tag_expression, set):
158
+ tag_list = ' || '.join(tag_or_tag_expression)
159
+ else:
160
+ tag_list = tag_or_tag_expression
161
+
162
+ # add the tags/tag expressions to the headers collection
163
+ if tag_list != "":
164
+ headers.update({cls.TAGS_HEADER_NAME: tag_list})
165
+
166
+ # add any custom headers to the headers collection that the user may have added
167
+ if notification.headers is not None:
168
+ headers.update(notification.headers)
169
+
170
+ if skip_send:
171
+ status = 201
172
+ else:
173
+ status = cls.__make_http_request(url, payload_to_send, headers)
174
+
175
+ return status, headers
176
+
177
+ def send_apple_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
178
+ skip_send=False):
179
+ nh = AzureNotification("apple", payload)
180
+
181
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
182
+ device_handle=device_handle,
183
+ scheduled_time=scheduled_time,
184
+ skip_send=skip_send)
185
+ return status, headers
186
+
187
+ def send_google_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
188
+ skip_send=False):
189
+ nh = AzureNotification("gcm", payload)
190
+
191
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
192
+ device_handle=device_handle,
193
+ scheduled_time=scheduled_time, skip_send=skip_send)
194
+ return status, headers
195
+
196
+ def send_amazon_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
197
+ skip_send=False):
198
+ nh = AzureNotification("adm", payload)
199
+
200
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
201
+ device_handle=device_handle,
202
+ scheduled_time=scheduled_time,
203
+ skip_send=skip_send)
204
+ return status, headers
205
+
206
+ def send_baidu_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
207
+ skip_send=False):
208
+ nh = AzureNotification("baidu", payload)
209
+
210
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
211
+ device_handle=device_handle,
212
+ scheduled_time=scheduled_time,
213
+ skip_send=skip_send)
214
+ return status, headers
215
+
216
+ def send_windows_phone_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
217
+ skip_send=False):
218
+ nh = AzureNotification("windowsphone", payload)
219
+
220
+ if "<wp:Toast>" in payload:
221
+ nh.headers = {'X-WindowsPhone-Target': 'toast', 'X-NotificationClass': '2'}
222
+ elif "<wp:Tile>" in payload:
223
+ nh.headers = {'X-WindowsPhone-Target': 'tile', 'X-NotificationClass': '1'}
224
+
225
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
226
+ device_handle=device_handle,
227
+ scheduled_time=scheduled_time,
228
+ skip_send=skip_send)
229
+ return status, headers
230
+
231
+ # example wns payload =
232
+ # """<toast><visual><binding template=\"ToastText01\"><text id=\"1\">Python Test</text></binding></visual></toast>"""
233
+ def send_windows_notification(self, is_direct, payload, tags="", device_handle=None, scheduled_time=None,
234
+ skip_send=False):
235
+ nh = AzureNotification("windows", payload)
236
+
237
+ if "<toast>" in payload:
238
+ nh.headers = {'X-WNS-Type': 'wns/toast'}
239
+ elif "<tile>" in payload:
240
+ nh.headers = {'X-WNS-Type': 'wns/tile'}
241
+ elif "<badge>" in payload:
242
+ nh.headers = {'X-WNS-Type': 'wns/badge'}
243
+
244
+ status, headers = self.__send_notification(nh, is_direct=is_direct, tag_or_tag_expression=tags,
245
+ device_handle=device_handle,
246
+ scheduled_time=scheduled_time,
247
+ skip_send=skip_send)
248
+ return status, headers
249
+
250
+ def send_template_notification(self, properties, tags="", scheduled_time=None, skip_send=False):
251
+ nh = AzureNotification("template", properties)
252
+ status, headers = self.__send_notification(nh, is_direct=False, tag_or_tag_expression=tags,
253
+ scheduled_time=scheduled_time,
254
+ skip_send=skip_send)
255
+
256
+ return status, headers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: notificationhubs-rest-python
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: A Python library for sending notifications using Azure Notification Hubs REST API.
5
5
  Home-page: https://github.com/panevo/notificationhubs-rest-python
6
6
  Author: Karlo Krakan
@@ -1,6 +1,7 @@
1
1
  LICENSE
2
2
  README.md
3
3
  setup.py
4
+ notificationhubs_rest_python/NotificationHub.py
4
5
  notificationhubs_rest_python.egg-info/PKG-INFO
5
6
  notificationhubs_rest_python.egg-info/SOURCES.txt
6
7
  notificationhubs_rest_python.egg-info/dependency_links.txt
@@ -4,7 +4,7 @@ from setuptools import setup
4
4
 
5
5
  setup(
6
6
  name="notificationhubs-rest-python",
7
- version="1.0.1",
7
+ version="1.0.2",
8
8
  include_package_data=True,
9
9
  license="Apache-2.0 License",
10
10
  description="A Python library for sending notifications using Azure Notification Hubs REST API.",