diff --git a/README.rst b/README.rst index 451e996..aa7d76c 100644 --- a/README.rst +++ b/README.rst @@ -23,10 +23,10 @@ Looking for a Django version? Checkout fcm-django - Link: https://github.com/xtrinch/fcm-django -Updates -------- +Updates (Breaking Changes) +-------------------------- -- MAJOR API UPDATES (DECEMBER 2016): https://github.com/olucurious/PyFCM/releases/tag/1.2.0 +- MAJOR UPDATES (AUGUST 2017): https://github.com/olucurious/PyFCM/releases/tag/1.4.0 Quickstart @@ -199,21 +199,16 @@ Access response data. .. code-block:: python + # Response from PyFCM. + response = dict() # Response from FCM Server. - response['multicast_id'] #Unique ID (number) identifying the multicast message. + response['multicast_ids'] # List of Unique ID (number) identifying the multicast message. response['success'] #Number of messages that were processed without an error. response['failure'] #Number of messages that could not be processed. response['canonical_ids'] #Number of results that contain a canonical registration token. response['results'] #Array of objects representing the status of the messages processed. - # For notify_multiple_devices - result = [{response dict},...] #list of response dicts is returned - - # For notify_single_device or notify_topic_subscribers - result = {response dict} #single response dict is returned - # The response objects are listed in the same order as the request (i.e., for each registration ID in the request, - # its response is listed in the same index in the response). # message_id: String specifying a unique ID for each successfully processed message. # registration_id: Optional string specifying the canonical registration token for the client app that the message # was processed and sent to. Sender should use this value as the registration token for future requests. Otherwise, diff --git a/pyfcm/__meta__.py b/pyfcm/__meta__.py index d7f27da..7135dbc 100644 --- a/pyfcm/__meta__.py +++ b/pyfcm/__meta__.py @@ -5,7 +5,7 @@ __summary__ = 'Python client for FCM - Firebase Cloud Messaging (Android & iOS)..' __url__ = 'https://github.com/olucurious/pyfcm' -__version__ = '1.3.2' +__version__ = '1.4.0' __install_requires__ = ['requests', 'requests-toolbelt'] diff --git a/pyfcm/baseapi.py b/pyfcm/baseapi.py index 23ec11f..7765595 100644 --- a/pyfcm/baseapi.py +++ b/pyfcm/baseapi.py @@ -6,6 +6,7 @@ from .errors import * + class BaseAPI(object): """ Base class for the pyfcm API wrapper for FCM @@ -141,10 +142,10 @@ def parse_payload(self, fcm_payload['notification'] = {} if message_icon: fcm_payload['notification']['icon'] = message_icon - #If body is present, use it + # If body is present, use it if message_body: fcm_payload['notification']['body'] = message_body - #Else use body_loc_key and body_loc_args for body + # Else use body_loc_key and body_loc_args for body else: if body_loc_key: fcm_payload['notification']['body_loc_key'] = body_loc_key @@ -153,10 +154,10 @@ def parse_payload(self, fcm_payload['notification']['body_loc_args'] = body_loc_args else: raise InvalidDataError('body_loc_args should be an array') - #If title is present, use it + # If title is present, use it if message_title: fcm_payload['notification']['title'] = message_title - #Else use title_loc_key and title_loc_args for title + # Else use title_loc_key and title_loc_args for title else: if title_loc_key: fcm_payload['notification']['title_loc_key'] = title_loc_key @@ -188,49 +189,57 @@ def parse_payload(self, return self.json_dumps(fcm_payload) - def do_request(self, payload): + def do_request(self, payload, timeout): if self.FCM_REQ_PROXIES: response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload, - proxies=self.FCM_REQ_PROXIES) + proxies=self.FCM_REQ_PROXIES, timeout=timeout) else: - response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload) + response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload, timeout=timeout) if 'Retry-After' in response.headers and int(response.headers['Retry-After']) > 0: sleep_time = int(response.headers['Retry-After']) time.sleep(sleep_time) - return self.do_request(payload) + return self.do_request(payload, timeout) return response - def send_request(self, payloads=None): + def send_request(self, payloads=None, timeout=None): self.send_request_responses = [] for payload in payloads: - response = self.do_request(payload) + response = self.do_request(payload, timeout) self.send_request_responses.append(response) def clean_registration_ids(self, registration_ids=[]): """Return list of active IDS from the list of registration_ids - """ valid_registration_ids = [] for registration_id in registration_ids: - details = requests.get('https://iid.googleapis.com/iid/info/'+registration_id, + details = requests.get('https://iid.googleapis.com/iid/info/' + registration_id, headers=self.request_headers(), - params={'details':'true'}) + params={'details': 'true'}) if details.status_code == 200: valid_registration_ids.append(registration_id) return valid_registration_ids def parse_responses(self): - response_list = list() + """ + Returns a python dict of multicast_ids(list), success(int), failure(int), canonical_ids(int), results(list) and optional topic_message_id(str but None by default) + """ + response_dict = { + 'multicast_ids': list(), + 'success': 0, + 'failure': 0, + 'canonical_ids': 0, + 'results': list(), + 'topic_message_id': None + } + for response in self.send_request_responses: if response.status_code == 200: """ Parses the json response sent back by the server and tries to get out the important return variables - - Returns a python dict of multicast_id(long), success(int), failure(int), canonical_ids(int), results(list) """ if 'content-length' in response.headers and int(response.headers['content-length']) <= 0: - response_list.append({}) + FCMServerError("FCM server connection error, the response is empty") else: parsed_response = response.json() @@ -242,15 +251,17 @@ def parse_responses(self): message_id = parsed_response.get('message_id', None) # for topic messages if message_id: success = 1 - response_list.append({'multicast_id': multicast_id, - 'success': success, - 'failure': failure, - 'canonical_ids': canonical_ids, - 'results': results}) + if multicast_id: + response_dict['multicast_id'].append(multicast_id) + response_dict['success'] += success + response_dict['failure'] += failure + response_dict['canonical_ids'] += canonical_ids + response_dict['results'].extend(results) + response_dict['topic_message_id'] = message_id elif response.status_code == 401: raise AuthenticationError("There was an error authenticating the sender account") elif response.status_code == 400: raise InternalPackageError(response.text) else: raise FCMServerError("FCM server is temporarily unavailable") - return response_list + return response_dict diff --git a/pyfcm/fcm.py b/pyfcm/fcm.py index 5646da5..81c6fde 100644 --- a/pyfcm/fcm.py +++ b/pyfcm/fcm.py @@ -26,6 +26,7 @@ def notify_single_device(self, title_loc_key=None, title_loc_args=None, content_available=None, + timeout=5, extra_kwargs={}): """ @@ -54,6 +55,7 @@ def notify_single_device(self, receive the message. Defaults to ``None``. dry_run (bool, optional): If ``True`` no message will be sent but request will be tested. + timeout (int, optional): set time limit for the request Returns: :dict:`multicast_id(long), success(int), failure(int), canonical_ids(int), results(list)`: Response from FCM server. @@ -89,8 +91,8 @@ def notify_single_device(self, content_available=content_available, **extra_kwargs) - self.send_request([payload]) - return self.parse_responses()[-1:][0] + self.send_request([payload], timeout) + return self.parse_responses() def notify_multiple_devices(self, registration_ids=None, @@ -115,6 +117,7 @@ def notify_multiple_devices(self, title_loc_key=None, title_loc_args=None, content_available=None, + timeout=5, extra_kwargs={}): """ @@ -154,59 +157,34 @@ def notify_multiple_devices(self, InvalidDataError: Invalid data provided InternalPackageError: JSON parsing error, mostly from changes in the response of FCM, create a new github issue to resolve it. """ - if len(registration_ids) > self.FCM_MAX_RECIPIENTS: - payloads = list() - registration_id_chunks = self.registration_id_chunks(registration_ids) - for registration_ids in registration_id_chunks: - # appends a payload with a chunk of registration ids here - payloads.append(self.parse_payload(registration_ids=registration_ids, - message_body=message_body, - message_title=message_title, - message_icon=message_icon, - sound=sound, - condition=condition, - collapse_key=collapse_key, - delay_while_idle=delay_while_idle, - time_to_live=time_to_live, - restricted_package_name=restricted_package_name, - low_priority=low_priority, - dry_run=dry_run, data_message=data_message, - click_action=click_action, - badge=badge, - color=color, - tag=tag, - body_loc_key=body_loc_key, - body_loc_args=body_loc_args, - title_loc_key=title_loc_key, - title_loc_args=title_loc_args, - content_available=content_available, - **extra_kwargs)) - self.send_request(payloads) - return self.parse_responses() - else: - payload = self.parse_payload(registration_ids=registration_ids, - message_body=message_body, - message_title=message_title, - message_icon=message_icon, - sound=sound, - condition=condition, - collapse_key=collapse_key, - delay_while_idle=delay_while_idle, - time_to_live=time_to_live, - restricted_package_name=restricted_package_name, - low_priority=low_priority, - dry_run=dry_run, data_message=data_message, click_action=click_action, - badge=badge, - color=color, - tag=tag, - body_loc_key=body_loc_key, - body_loc_args=body_loc_args, - title_loc_key=title_loc_key, - title_loc_args=title_loc_args, - content_available=content_available, - **extra_kwargs) - self.send_request([payload]) - return self.parse_responses() + payloads = list() + registration_id_chunks = self.registration_id_chunks(registration_ids) + for registration_ids in registration_id_chunks: + # appends a payload with a chunk of registration ids here + payloads.append(self.parse_payload(registration_ids=registration_ids, + message_body=message_body, + message_title=message_title, + message_icon=message_icon, + sound=sound, + condition=condition, + collapse_key=collapse_key, + delay_while_idle=delay_while_idle, + time_to_live=time_to_live, + restricted_package_name=restricted_package_name, + low_priority=low_priority, + dry_run=dry_run, data_message=data_message, + click_action=click_action, + badge=badge, + color=color, + tag=tag, + body_loc_key=body_loc_key, + body_loc_args=body_loc_args, + title_loc_key=title_loc_key, + title_loc_args=title_loc_args, + content_available=content_available, + **extra_kwargs)) + self.send_request(payloads, timeout) + return self.parse_responses() def notify_topic_subscribers(self, topic_name=None, @@ -231,10 +209,11 @@ def notify_topic_subscribers(self, title_loc_key=None, title_loc_args=None, content_available=None, + timeout=5, extra_kwargs={}): """ - Sends push notification to multiple devices subscribe to a topic + Sends push notification to multiple devices subscribed to a topic Args: topic_name (topic_name): Name of the topic to deliver messages to @@ -292,5 +271,5 @@ def notify_topic_subscribers(self, title_loc_args=title_loc_args, content_available=content_available, **extra_kwargs) - self.send_request([payload]) - return self.parse_responses()[-1:][0] + self.send_request([payload], timeout) + return self.parse_responses()