Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to the new FCM HTTP v1 API #980

Merged
merged 3 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion conf/net/ext_service/push.json.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
"provider": "firebase",
"server_auth_token": "Get from firebase console",
"app_package_name": "full package name from config.xml. e.g. edu.berkeley.eecs.emission or edu.berkeley.eecs.embase. Defaults to edu.berkeley.eecs.embase",
"ios_token_format": "fcm"
"ios_token_format": "fcm",
"project_id": "Get from the General project settings (https://console.cloud.google.com/project/_/settings/general/) tab of the Firebase console.",
"service_account_file": "Download according to the instructions at https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually and put the path to the file here"
}
3 changes: 2 additions & 1 deletion emission/net/ext_service/push/notify_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

push_config = ecbc.get_config('conf/net/ext_service/push.json',
{"PUSH_PROVIDER": "provider", "PUSH_SERVER_AUTH_TOKEN": "server_auth_token",
"PUSH_APP_PACKAGE_NAME": "app_package_name", "PUSH_IOS_TOKEN_FORMAT": "ios_token_format"})
"PUSH_APP_PACKAGE_NAME": "app_package_name", "PUSH_IOS_TOKEN_FORMAT": "ios_token_format",
"PUSH_PROJECT_ID": "project_id", "PUSH_SERVICE_ACCOUNT_FILE": "service_account_file"})

try:
logging.warning(f"Push configured for app {push_config.get('PUSH_APP_PACKAGE_NAME')} using platform {push_config.get('PUSH_PROVIDER')} with token {push_config.get('PUSH_SERVER_AUTH_TOKEN')[:10]}... of length {len(push_config.get('PUSH_SERVER_AUTH_TOKEN'))}")
Expand Down
65 changes: 48 additions & 17 deletions emission/net/ext_service/push/notify_interface_impl/firebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import requests
import copy
import time
import pyfcm

# Our imports
import emission.core.get_database as edb
Expand All @@ -21,7 +22,8 @@ def get_interface(push_config):

class FirebasePush(pni.NotifyInterface):
def __init__(self, push_config):
self.server_auth_token = push_config.get("PUSH_SERVER_AUTH_TOKEN")
self.service_account_file = push_config.get("PUSH_SERVICE_ACCOUNT_FILE")
self.project_id = push_config.get("PUSH_PROJECT_ID")
if "PUSH_APP_PACKAGE_NAME" in push_config:
self.app_package_name = push_config.get("PUSH_APP_PACKAGE_NAME")
else:
Expand All @@ -33,6 +35,30 @@ def get_and_invalidate_entries(self):
# Need to figure out how to do this on firebase
pass

def notify_multiple_devices(self, push_service, fcm_token_list,
notification_title=None, notification_body=None, data_payload=None):
results = {}
n_success = 0
n_failure = 0
for t in fcm_token_list:
trunc_t = t[:10]
try:
result = push_service.notify(fcm_token=t,
notification_title=notification_title,
notification_body=notification_body,
data_payload=data_payload)
results.update({trunc_t: result['name']})
n_success = n_success + 1
print("Successfully sent to %s..." % (trunc_t))
except (pyfcm.errors.FCMNotRegisteredError, pyfcm.errors.InvalidDataError) as e:
results.update({trunc_t: str(e)})
n_failure = n_failure + 1
print("Found error %s while sending to token %s... skipping" % (e, trunc_t))
response = {"success": n_success, "failure": n_failure, "results": results}
print(response)
logging.debug(response)
return response

@staticmethod
def print_dev_flag_warning():
logging.warning("dev flag is ignored for firebase, since the API does not distinguish between production and dev")
Expand Down Expand Up @@ -128,20 +154,22 @@ def send_visible_notification(self, token_map, title, message, json_data, dev=Fa
# convert tokens if necessary
fcm_token_map = self.convert_to_fcm_if_necessary(token_map, dev)

push_service = FCMNotification(api_key=self.server_auth_token)
data_message = {
"data": json_data,
"payload": json_data
}
push_service = FCMNotification(
service_account_file=self.service_account_file,
project_id=self.project_id)
# Send android and iOS messages separately because they have slightly
# different formats
# https://github.com/e-mission/e-mission-server/issues/564#issuecomment-360720598
android_response = push_service.notify_multiple_devices(registration_ids=fcm_token_map["android"],
data_message=data_message)
ios_response = push_service.notify_multiple_devices(registration_ids=fcm_token_map["ios"],
message_body = message,
message_title = title,
data_message=data_message)
android_response = self.notify_multiple_devices(push_service,
fcm_token_map["android"],
notification_body = message,
notification_title = title,
data_payload = json_data)
ios_response = self.notify_multiple_devices(push_service,
fcm_token_map["ios"],
notification_body = message,
notification_title = title,
data_payload = json_data)
combo_response = {"ios": ios_response, "android": android_response}
logging.debug(combo_response)
return combo_response
Expand All @@ -155,20 +183,23 @@ def send_silent_notification(self, token_map, json_data, dev=False):
# multiplying by 10^6 gives us the maximum resolution possible while still
# being not a float. Have to see if that is too big.
# Hopefully we will never send a push notification a millisecond to a single phone
ios_raw_data.update({"notId": int(time.time() * 10**6)})
ios_raw_data.update({"notId": str(int(time.time() * 10**6))})
ios_raw_data.update({"payload": ios_raw_data["notId"]})

push_service = FCMNotification(api_key=self.server_auth_token)
push_service = FCMNotification(
service_account_file=self.service_account_file,
project_id=self.project_id)

# convert tokens if necessary
fcm_token_map = self.convert_to_fcm_if_necessary(token_map, dev)

response = {}
response["ios"] = push_service.notify_multiple_devices(registration_ids=fcm_token_map["ios"],
data_message=ios_raw_data,
content_available=True)
response["ios"] = self.notify_multiple_devices(push_service,
fcm_token_map["ios"], data_payload=ios_raw_data)

response["android"] = {"success": "skipped", "failure": "skipped",
"results": "skipped"}
print(response)
logging.debug(response)
return response

Expand Down
2 changes: 1 addition & 1 deletion setup/environment36.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies:
- utm=0.7.0
- pip:
- git+https://github.com/JGreenlee/e-mission-common@0.5.3
- pyfcm==1.5.4
- pyfcm==2.0.1
- pygeocoder==1.2.5
- pymongo==4.3.3
- pykov==0.1
Expand Down
Loading