Skip to content

Commit a1e6060

Browse files
committed
Switch back to firebase-admin
Google finally updated their own library to support the efficient notifications without relying on the API they deprecated 3 years ago and removed 2 years ago.
1 parent 8dbf50c commit a1e6060

File tree

1 file changed

+48
-25
lines changed

1 file changed

+48
-25
lines changed

spns/notifiers/firebase.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,29 @@
33
from .. import config
44
from ..config import logger
55
from ..core import SUBSCRIBE
6-
from .util import encrypt_notify_payload, derive_notifier_key, warn_on_except, NotifyStats
6+
from .util import (
7+
encrypt_notify_payload,
8+
derive_notifier_key,
9+
warn_on_except,
10+
NotifyStats,
11+
)
712

8-
from pyfcm import FCMNotification
13+
import firebase_admin
14+
from firebase_admin import messaging
15+
from firebase_admin.exceptions import *
916

1017
import oxenc
1118
from oxenmq import OxenMQ, Message, Address, AuthLevel
1219

20+
import asyncio
1321
import datetime
1422
import time
1523
import json
1624
import signal
1725
import systemd.daemon
1826
from threading import Lock
1927

28+
loop = None
2029
omq = None
2130
hivemind = None
2231
firebase_app = None
@@ -75,18 +84,17 @@ def push_notification(msg: Message):
7584
# data-too-big messages and config updates which definitely won't notify.
7685
priority = "high" if b"~" in data and data[b"n"] in (0, 11) else "normal"
7786

78-
device_token = data[b"&"].decode() # unique service id, as we returned from validate
87+
# unique service id, as we returned from validate
88+
device_token = data[b"&"].decode()
7989

80-
msg = {
81-
"fcm_token": device_token,
82-
"data_payload": {
90+
msg = messaging.Message(
91+
data={
8392
"enc_payload": oxenc.to_base64(enc_payload),
84-
"spns": f"{SPNS_FIREBASE_VERSION}"
93+
"spns": f"{SPNS_FIREBASE_VERSION}",
8594
},
86-
"android_config": {
87-
"priority": priority,
88-
},
89-
}
95+
token=device_token,
96+
android=messaging.AndroidConfig(priority=priority),
97+
)
9098

9199
global notify_queue, queue_lock
92100
with queue_lock:
@@ -95,27 +103,38 @@ def push_notification(msg: Message):
95103

96104
@warn_on_except
97105
def send_pending():
98-
global notify_queue, queue_lock, firebase_app, stats
106+
global notify_queue, queue_lock, firebase_app, loop, stats
99107
with queue_lock:
100108
queue, notify_queue = notify_queue, []
101109

102110
i = 0
111+
results = []
103112
while i < len(queue):
104-
results = firebase_app.async_notify_multiple_devices(params_list=queue[i : i + MAX_NOTIFIES])
105-
with stats.lock:
106-
stats.notifies += min(len(queue) - i, MAX_NOTIFIES)
113+
results.append(
114+
asyncio.run_coroutine_threadsafe(
115+
messaging.send_each_async(
116+
messages=queue[i : i + MAX_NOTIFIES], app=firebase_app
117+
),
118+
loop,
119+
)
120+
)
121+
i += MAX_NOTIFIES
107122

108-
# FIXME: process/reschedule failures?
123+
results = [f.result() for f in results]
124+
# FIXME: process/reschedule failures?
109125

110-
i += MAX_NOTIFIES
126+
with stats.lock:
127+
stats.notifies += len(queue)
111128

112129

113130
@warn_on_except
114131
def ping():
115132
"""Makes sure we are registered and reports updated stats to hivemind; called every few seconds"""
116133
global omq, hivemind, stats
117134
omq.send(hivemind, "admin.register_service", "firebase")
118-
omq.send(hivemind, "admin.service_stats", "firebase", oxenc.bt_serialize(stats.collect()))
135+
omq.send(
136+
hivemind, "admin.service_stats", "firebase", oxenc.bt_serialize(stats.collect())
137+
)
119138
systemd.daemon.notify(
120139
f"WATCHDOG=1\nSTATUS=Running; {stats.total_notifies} notifications, "
121140
f"{stats.total_retries} retries, {stats.total_failures} failures"
@@ -150,11 +169,13 @@ def start():
150169
omq.start()
151170

152171
hivemind = omq.connect_remote(
153-
Address(config.config.hivemind_sock), auth_level=AuthLevel.basic, ephemeral_routing_id=False
172+
Address(config.config.hivemind_sock),
173+
auth_level=AuthLevel.basic,
174+
ephemeral_routing_id=False,
154175
)
155176

156-
firebase_app = FCMNotification(
157-
service_account_file=conf["token_file"], project_id="loki-5a81e"
177+
firebase_app = firebase_admin.initialize_app(
178+
firebase_admin.credentials.Certificate(conf["token_file"])
158179
)
159180

160181
omq.send(hivemind, "admin.register_service", "firebase")
@@ -177,14 +198,15 @@ def disconnect(flush_pending=True):
177198
def run(startup_delay=4.0):
178199
"""Runs the firebase notifier, forever."""
179200

180-
global omq
201+
global omq, loop
181202

182203
if startup_delay > 0:
183204
time.sleep(startup_delay)
184205

185206
logger.info("Starting firebase notifier")
186207
systemd.daemon.notify("STATUS=Initializing firebase notifier...")
187208
try:
209+
loop = asyncio.new_event_loop()
188210
start()
189211
except Exception as e:
190212
logger.critical(f"Failed to start firebase notifier: {e}")
@@ -194,16 +216,17 @@ def run(startup_delay=4.0):
194216
systemd.daemon.notify("READY=1\nSTATUS=Started")
195217

196218
def sig_die(signum, frame):
219+
loop.stop()
197220
raise OSError(f"Caught signal {signal.Signals(signum).name}")
198221

199222
try:
200223
signal.signal(signal.SIGHUP, sig_die)
201224
signal.signal(signal.SIGINT, sig_die)
202225

203-
while omq is not None:
204-
time.sleep(3600)
226+
loop.run_forever()
227+
205228
except Exception as e:
206-
logger.error(f"firebase notifier mule died via exception: {e}")
229+
logger.error(f"firebase notifier died via exception: {e}")
207230

208231

209232
if __name__ == "__main__":

0 commit comments

Comments
 (0)