diff --git a/sylk/applications/webrtcgateway/models/firebase.py b/sylk/applications/webrtcgateway/models/firebase.py new file mode 100644 index 0000000..0826ea5 --- /dev/null +++ b/sylk/applications/webrtcgateway/models/firebase.py @@ -0,0 +1,80 @@ + +from sipsimple.util import ISOTimestamp + +from .jsonobjects import BooleanProperty, IntegerProperty, StringProperty, ObjectProperty, FixedValueProperty, LimitedChoiceProperty, AbstractObjectProperty +from .jsonobjects import JSONObject + + +class NotificationData(JSONObject): + body = StringProperty(optional=True) + sound = StringProperty(optional=True) + title = StringProperty(optional=True) + subtitle = StringProperty(optional=True) + + +class ApplicationData(JSONObject): + sylkrtc = AbstractObjectProperty() + + +class FirebaseRequest(JSONObject): + priority = LimitedChoiceProperty(['normal', 'high'], optional=True, default='high') + content_available = BooleanProperty(optional=True, default=True) + time_to_live = IntegerProperty(optional=True) # for how long should the system try to deliver the notification (default is 4 weeks) + to = StringProperty() + notification = ObjectProperty(NotificationData) + data = ObjectProperty(ApplicationData) + + def __init__(self, token, event, **kw): + super(FirebaseRequest, self).__init__(to=token, data=dict(sylkrtc=event), notification=event.notification, **kw) + + +# Event base classes (abstract, should not be used directly) + +class SylkRTCEventBase(JSONObject): + timestamp = StringProperty() + + def __init__(self, **kw): + kw['timestamp'] = str(ISOTimestamp.utcnow()) + super(SylkRTCEventBase, self).__init__(**kw) + self.notification = NotificationData(body=self.notification_body) + if self.notification_sound is not None: + self.notification.sound = self.notification_sound + + @property + def notification_body(self): + raise NotImplementedError + + notification_sound = None + + +class CallEventBase(SylkRTCEventBase): + event = None # specified by subclass + originator = StringProperty() + destination = StringProperty() + + @property + def notification_body(self): + return '{0.event_description} from {0.originator}'.format(self) + + @property + def event_description(self): + return self.event.replace('-', ' ').capitalize() + + +# Events to use used in a FirebaseRequest + +class IncomingCallEvent(CallEventBase): + event = FixedValueProperty('incoming-call') + + +class MissedCallEvent(CallEventBase): + event = FixedValueProperty('missed-call') + + +class ConferenceInviteEvent(CallEventBase): + event = FixedValueProperty('conference-invite') + room = StringProperty() + + @property + def notification_body(self): + return '{0.event_description} from {0.originator} to room {0.room}'.format(self) diff --git a/sylk/applications/webrtcgateway/push.py b/sylk/applications/webrtcgateway/push.py index 937ff76..c6a83e5 100644 --- a/sylk/applications/webrtcgateway/push.py +++ b/sylk/applications/webrtcgateway/push.py @@ -1,81 +1,65 @@ import json -from sipsimple.util import ISOTimestamp from twisted.internet import defer, reactor from twisted.web.client import Agent from twisted.web.iweb import IBodyProducer from twisted.web.http_headers import Headers from zope.interface import implementer -from sylk.applications.webrtcgateway.configuration import GeneralConfig -from sylk.applications.webrtcgateway.logger import log +from .configuration import GeneralConfig +from .logger import log +from .models import firebase __all__ = 'incoming_session', 'missed_session' agent = Agent(reactor) headers = Headers({'User-Agent': ['SylkServer'], 'Content-Type': ['application/json'], 'Authorization': ['key=%s' % GeneralConfig.firebase_server_key]}) FIREBASE_API_URL = 'https://fcm.googleapis.com/fcm/send' @implementer(IBodyProducer) class StringProducer(object): def __init__(self, data): self.body = data self.length = len(data) def startProducing(self, consumer): consumer.write(self.body) return defer.succeed(None) def pauseProducing(self): pass def stopProducing(self): pass def incoming_session(originator, destination, tokens): for token in tokens: - data = dict(to=token, notification={}, data={'sylkrtc': {}}, content_available=True) - data['notification']['body'] = 'Incoming call from %s' % originator - data['notification']['sound'] = 'Blow' - data['priority'] = 'high' - data['time_to_live'] = 60 # don't deliver if phone is out for over a minute - data['data']['sylkrtc']['event'] = 'incoming_session' - data['data']['sylkrtc']['originator'] = originator - data['data']['sylkrtc']['destination'] = destination - data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.utcnow()) - _send_push_notification(json.dumps(data)) + request = firebase.FirebaseRequest(token, event=firebase.IncomingCallEvent(originator=originator, destination=destination), time_to_live=60) + _send_push_notification(json.dumps(request.__data__)) def missed_session(originator, destination, tokens): for token in tokens: - data = dict(to=token, notification={}, data={'sylkrtc': {}}, content_available=True) - data['notification']['body'] = 'Missed call from %s' % originator - data['notification']['sound'] = 'Blow' - data['priority'] = 'high' - # No TTL, default is 4 weeks - data['data']['sylkrtc']['event'] = 'missed_session' - data['data']['sylkrtc']['originator'] = originator - data['data']['sylkrtc']['destination'] = destination - data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.utcnow()) - _send_push_notification(json.dumps(data)) + request = firebase.FirebaseRequest(token, event=firebase.MissedCallEvent(originator=originator, destination=destination)) + _send_push_notification(json.dumps(request.__data__)) @defer.inlineCallbacks def _send_push_notification(payload): if GeneralConfig.firebase_server_key: try: r = yield agent.request('POST', FIREBASE_API_URL, headers, StringProducer(payload)) except Exception as e: log.info('Error sending Firebase message: %s', e) else: if r.code != 200: log.warn('Error sending Firebase message: %s' % r.phrase) else: log.warn('Cannot send push notification: no Firebase server key configured')