Add push notifications for forgotten departures #26
|
@ -30,3 +30,5 @@ json_indent = 4
|
|||
# sender info: usually mailto link to responsible party to contact about issues
|
||||
push_private_key = abcdefghijklm_NOPQRSTUVWXYZ-0123456789
|
||||
push_sender_info = mailto:it@fasttube.de
|
||||
# when to notify users, in hours after arrival
|
||||
notify_after_hrs = 10
|
||||
|
|
|
@ -30,3 +30,6 @@ class Config:
|
|||
|
||||
def __getitem__(self, key):
|
||||
return self.config['global'].get(key)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.config.items('global'))
|
||||
|
|
|
@ -22,14 +22,19 @@ from flask import Flask, request, redirect
|
|||
app = Flask(__name__, static_folder='../web')
|
||||
|
||||
|
||||
from pywebpush import webpush, WebPushException
|
||||
pushsubs = db.table('pushsubs')
|
||||
|
||||
|
||||
if config['delete_after_days']:
|
||||
from .deleter import Deleter
|
||||
deleter = Deleter(db, int(config['delete_after_days']))
|
||||
|
||||
|
||||
if config['notify_after_hrs']:
|
||||
from .notifier import Notifier
|
||||
notifier = Notifier(db, int(config['notify_after_hrs']), {
|
||||
'private_key': config['push_private_key'],
|
||||
'claims': {'sub': config['push_sender_info']}
|
||||
})
|
||||
|
||||
|
||||
def shutdown():
|
||||
print('\rReceived stop signal, stopping threads...')
|
||||
deleter.stop()
|
||||
|
@ -37,6 +42,7 @@ def shutdown():
|
|||
|
||||
atexit.register(shutdown)
|
||||
|
||||
|
||||
@app.route('/guidelines')
|
||||
def get_guidelines():
|
||||
dest = config['guideline_url'] or None
|
||||
|
@ -186,32 +192,25 @@ def post_pushsub():
|
|||
@app.route('/testpush/<name>')
|
||||
def testpush(name):
|
||||
|
||||
if not 'Authorization' in request.headers:
|
||||
return 'Error: No Authorization', 401, {'WWW-Authenticate': 'Basic'}
|
||||
|
||||
if request.authorization.username != config['admin_user']:
|
||||
return "Wrong username", 403
|
||||
|
||||
if request.authorization.password != config['admin_pass']:
|
||||
return "Wrong password", 403
|
||||
|
||||
Entry = Query()
|
||||
ps = pushsubs.search(Entry.name == name)[0]
|
||||
arrivals = db.search((Entry.name == name) & (Entry.departure == None))
|
||||
|
||||
arr = db.search((Entry.name == name) & (Entry.departure == None))
|
||||
arr = arr if len(arr) else None
|
||||
if len(arrivals) == 0:
|
||||
print("User is not logged in :(")
|
||||
return "Error: User is not logged in :(", 409
|
||||
|
||||
print(ps)
|
||||
error = notifier.notify_user(arrivals[0])
|
||||
|
||||
subscription = ps['sub']
|
||||
notification = {
|
||||
'title': "Forgot to sign out?",
|
||||
'body': "You didn't sign out of ftracker yet",
|
||||
'arr': arr
|
||||
}
|
||||
if error:
|
||||
return 'Error: ' + error, 201
|
||||
|
||||
try:
|
||||
webpush(
|
||||
subscription,
|
||||
json.dumps(notification, indent=SPACES),
|
||||
vapid_private_key = config['push_private_key'],
|
||||
vapid_claims = {
|
||||
'sub': config['push_sender_info']
|
||||
},
|
||||
verbose=True
|
||||
)
|
||||
return 'OK', 201
|
||||
except WebPushException as exc:
|
||||
print(exc)
|
||||
return 'Error', 500
|
||||
return 'OK', 201
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import json
|
||||
from threading import Thread, Event
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from tinydb import Query
|
||||
|
||||
from pywebpush import webpush, WebPushException
|
||||
|
||||
ONE_HOUR_IN_S = 60 * 60
|
||||
|
||||
class Notifier(Thread):
|
||||
|
||||
def notify_user(self, arrival):
|
||||
|
||||
pushsubs = self.db.table('pushsubs')
|
||||
|
||||
Entry = Query()
|
||||
ps = pushsubs.search(Entry.name == arrival['name'])
|
||||
if len(ps) == 0:
|
||||
print("User is not subscribed to notifications :(")
|
||||
return "User is not subscribed to notifications :("
|
||||
|
||||
ps = ps[0]
|
||||
|
||||
print("Sending notification", arrival, ps)
|
||||
|
||||
subscription = ps['sub']
|
||||
notification = {
|
||||
'title': "Forgot to sign out?",
|
||||
'body': "You didn't sign out of ftracker yet",
|
||||
'arr': arrival
|
||||
}
|
||||
|
||||
try:
|
||||
webpush(
|
||||
subscription,
|
||||
json.dumps(notification),
|
||||
vapid_private_key = self.vapid_creds['private_key'],
|
||||
vapid_claims = self.vapid_creds['claims']
|
||||
)
|
||||
print("Notification sent")
|
||||
return None
|
||||
except WebPushException as exc:
|
||||
print("Notification failed", exc)
|
||||
return repr(exc)
|
||||
|
||||
|
||||
def notify_logged_in_users(self):
|
||||
|
||||
print("Notifying users that aren't signed out yet...")
|
||||
|
||||
td = timedelta(hours=self.hours)
|
||||
|
||||
threshold = datetime.utcnow() - td
|
||||
|
||||
iso = threshold.isoformat() + 'Z'
|
||||
|
||||
Entry = Query()
|
||||
arrivals = self.db.search(
|
||||
(Entry.arrival < iso) & (Entry.departure == None)
|
||||
)
|
||||
|
||||
for arrival in arrivals:
|
||||
self.notify_user(arrival)
|
||||
|
||||
print("Notified everything until UTC", iso)
|
||||
|
||||
def __init__(self, db, hours, vapid_creds):
|
||||
|
||||
self.db = db
|
||||
self.hours = hours
|
||||
self.vapid_creds = vapid_creds
|
||||
|
||||
self.notify_logged_in_users()
|
||||
|
||||
Thread.__init__(self, daemon=True)
|
||||
self.stopped = Event()
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
|
||||
while not self.stopped.wait(ONE_HOUR_IN_S):
|
||||
self.notify_logged_in_users()
|
||||
|
||||
def stop(self):
|
||||
self.stopped.set()
|
Loading…
Reference in New Issue