class Service(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) secret = db.Column(db.VARCHAR(32), nullable=False) encryption_key = db.Column(db.VARCHAR(512), nullable=False) public = db.Column(db.VARCHAR(40), nullable=False) name = db.Column(db.VARCHAR(255), nullable=False) icon = db.Column(db.TEXT, nullable=False, default='') timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, name, icon='', encrypted=False): self.secret = hashlib.sha1(urandom(100)).hexdigest()[:32] if encrypted: self.encryption_key = hashlib.sha1(urandom(100)).hexdigest()[:32] else: self.encryption_key = "" self.name = name self.icon = icon pub = list(hashlib.new('ripemd160', self.secret).hexdigest())[:40] sep = [4, 11, 24, 30] for s in sep: pub[s] = '-' self.public = ''.join(pub) def __repr__(self): return '<Service {}: {}>'.format(self.id, self.name) def cleanup(self): threshold = self.subscribed().order_by( Subscription.last_read.asc()).first().last_read # Nothing to do if not threshold: return messages = Message.query \ .filter_by(service=self) \ .filter(threshold > Message.id) \ .all() map(db.session.delete, messages) def subscribed(self): return Subscription.query.filter_by(service=self) def as_dict(self, secret=False): data = { "public": self.public, "name": self.name, "created": int(self.timestamp_created.strftime("%s")), "icon": self.icon, "encrypted": True if self.encryption_key else False, } if secret: data["secret"] = self.secret data["encryption_key"] = self.encryption_key return data
class Service(db.Model): id = db.Column(Integer, primary_key=True) secret = db.Column(db.VARCHAR(32), nullable=False) public = db.Column(db.VARCHAR(40), nullable=False) name = db.Column(db.Unicode(length=255), nullable=False) icon = db.Column(db.TEXT, nullable=False, default='') timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, name, icon=''): self.secret = hashlib.sha1(urandom(100)).hexdigest()[:32] self.name = name self.icon = icon pub = list( hashlib.new('ripemd160', self.secret.encode("UTF-8")).hexdigest())[:40] sep = [4, 11, 24, 30] for s in sep: pub[s] = '-' self.public = ''.join(pub) def __repr__(self): return '<Service {}: {}>'.format(self.id, self.name) def cleanup(self): threshold = self.subscribed().order_by( Subscription.last_read.asc()).first().last_read # Nothing to do if not threshold: return messages = Message.query \ .filter_by(service=self) \ .filter(threshold > Message.id) \ .all() map(db.session.delete, messages) def subscribed(self): return Subscription.query.filter_by(service=self) def as_dict(self, secret=False): data = { "public": self.public, "name": self.name, "created": int((self.timestamp_created - datetime.utcfromtimestamp(0)).total_seconds()), "icon": self.icon, } if secret: data["secret"] = self.secret return data
class Subscription(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) device = db.Column(db.VARCHAR(40), nullable=False) service_id = db.Column(INTEGER(unsigned=True), db.ForeignKey('service.id'), nullable=False) service = db.relationship('Service', backref=db.backref('subscription', lazy='dynamic')) last_read = db.Column(INTEGER(unsigned=True), db.ForeignKey('message.id'), default=0) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) timestamp_checked = db.Column(db.TIMESTAMP) def __init__(self, device, service): last_message = Message.query.order_by(Message.id.desc()).first() self.device = device self.service = service self.timestamp_checked = datetime.utcnow() self.last_read = last_message.id if last_message else 0 def __repr__(self): return '<Subscription {}>'.format(self.id) def messages(self): return Message.query \ .filter_by(service_id=self.service_id) \ .filter(Message.id > self.last_read) def as_dict(self): data = { "uuid": self.device, "service": self.service.as_dict(), "timestamp": int((self.timestamp_created - datetime.utcfromtimestamp(0)).total_seconds()), "timestamp_checked": int((self.timestamp_checked - datetime.utcfromtimestamp(0)).total_seconds()) } return data
class Gcm(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) uuid = db.Column(db.VARCHAR(40), nullable=False) gcmid = db.Column(db.TEXT, nullable=False) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, device, gcmid): self.uuid = device self.gcmid = gcmid def __repr__(self): return '<Gcm {}>'.format(self.uuid) def as_dict(self): data = { "uuid": self.service.as_dict(), "gcm_registration_id": self.gcmId, "timestamp": int(self.timestamp_created.strftime('%s')), } return data @staticmethod def send_message(message): """ :type message: Message """ subscriptions = Subscription.query.filter_by( service=message.service).all() if len(subscriptions) == 0: return 0 gcm_devices = Gcm.query.filter( Gcm.uuid.in_([l.device for l in subscriptions])).all() if len(gcm_devices) > 0: data = dict(message=message.as_dict(), encrypted=False) Gcm.gcm_send([r.gcmid for r in gcm_devices], data) if len(gcm_devices) > 0: uuids = [g.uuid for g in gcm_devices] gcm_subscriptions = Subscription.query.filter_by( service=message.service).filter( Subscription.device.in_(uuids)).all() last_message = Message.query.order_by(Message.id.desc()).first() for l in gcm_subscriptions: l.timestamp_checked = datetime.utcnow() l.last_read = last_message.id if last_message else 0 db.session.commit() return len(gcm_devices) @staticmethod def gcm_send(ids, data): url = 'https://android.googleapis.com/gcm/send' headers = dict(Authorization='key={}'.format(google_api_key)) data = dict(registration_ids=ids, data=data) if current_app.config['TESTING'] is True: current_app.config['TESTING_GCM'].append(data) else: requests.post(url, json=data, headers=headers)
class Subscription(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) device = db.Column(db.VARCHAR(40), nullable=False) service_id = db.Column(INTEGER(unsigned=True), db.ForeignKey('service.id'), nullable=False) service = db.relationship('Service', backref=db.backref('subscription', lazy='dynamic')) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) timestamp_checked = db.Column(db.TIMESTAMP) def __init__(self, device, service): self.device = device self.service = service self.timestamp_checked = datetime.utcnow() - timedelta(minutes=30) def __repr__(self): return '<Subscription %r>' % self.id def messages(self): return Message.query \ .filter_by(service_id=self.service_id) \ .filter(Message.timestamp_created > self.timestamp_checked) def as_dict(self): data = { "uuid": self.device, "service": self.service.as_dict(), "timestamp": int(self.timestamp_created.strftime('%s')), "timestamp_checked": int(self.timestamp_checked.strftime('%s')) } return data
class Gcm(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) uuid = db.Column(db.VARCHAR(40), nullable=False) gcmid = db.Column(db.TEXT, nullable=False) pubkey = db.Column(db.TEXT, default=None) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, device, gcmid, pubkey=None): self.uuid = device self.gcmid = gcmid self.pubkey = pubkey def __repr__(self): return '<Gcm %r>' % self.uuid def as_dict(self): data = { "uuid": self.service.as_dict(), "gcm_registration_id": self.gcmId, "timestamp": int(self.timestamp_created.strftime('%s')), } return data @staticmethod def send_message(message): """ :type message: Message """ subscriptions = Subscription.query.filter_by(service=message.service).all() if len(subscriptions) == 0: return 0 gcm_filter = Gcm.query.filter(Gcm.uuid.in_([l.device for l in subscriptions])).all() devices_plain = [r.gcmid for r in gcm_filter if r.pubkey is None] devices_crypto = [r for r in gcm_filter if r.pubkey is not None] if len(devices_plain) > 0: data = {"message": dumps(message.as_dict()), "encrypted": True} Gcm.gcm_send(devices_plain, data) for device in devices_crypto: pubkey = rsa.PublicKey.load_pkcs1(b64decode(device.pubkey), 'DER') message_enc = rsa.encrypt(dumps(message.as_dict()), pubkey) data = {"message": message_enc, "encrypted": True} Gcm.gcm_send([device.gcmid], data) if len(gcm_filter) > 0: uuids = [g.uuid for g in gcm_filter] gcm_subscriptions = Subscription.query.filter_by(service=message.service).filter(Subscription.device.in_(uuids)).all() for l in gcm_subscriptions: l.timestamp_checked = datetime.utcnow() db.session.commit() return len(gcm_filter) @staticmethod def gcm_send(ids, data): data = dumps({ "registration_ids": ids, "data": data, }) headers = { 'Authorization': 'key=%s' % google_api_key, 'Content-Type': 'application/json', } req = urllib2.Request('https://android.googleapis.com/gcm/send', data, headers) urllib2.urlopen(req).read()
class Apns(db.Model): id = db.Column(INTEGER(unsigned=True), primary_key=True) uuid = db.Column(db.VARCHAR(40), nullable=False) device_token = db.Column(db.TEXT, nullable=False) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, uuid, token): self.uuid = uuid self.device_token = token def __repr__(self): return '<Apns {}>'.format(self.uuid) def as_dict(self): data = { "uuid": self.uuid, "device_token": self.device_token, "timestamp": int((self.timestamp_created - datetime.utcfromtimestamp(0)).total_seconds()), } return data @staticmethod def send_message(message): """ :type message: Message """ subscriptions = Subscription.query.filter_by( service=message.service).all() if len(subscriptions) == 0: return 0 apns_devices = Apns.query.filter( Apns.uuid.in_([l.device for l in subscriptions])).all() if len(apns_devices) > 0: Apns.apns_send([r.device_token for r in apns_devices], message.as_dict()) uuids = [g.uuid for g in apns_devices] apns_subscriptions = Subscription.query.filter_by( service=message.service).filter( Subscription.device.in_(uuids)).all() last_message = Message.query.order_by(Message.id.desc()).first() for l in apns_subscriptions: l.timestamp_checked = datetime.utcnow() l.last_read = last_message.id if last_message else 0 db.session.commit() return len(apns_devices) @staticmethod def apns_send(tokens, data): if current_app.config['TESTING'] is True: current_app.config['TESTING_APNS'].append(data) return payload = Payload(alert=data['service']['name'], sound="default", badge=1, custom=data) notifications = [ Notification(token=token, payload=payload) for token in tokens ] topic = 'me.elrod.iPushjet' client = APNsClient(apns_cert_path, use_sandbox=True, use_alternative_port=False) client.send_notification_batch(notifications, topic)
class MQTT(db.Model): id = db.Column(Integer, primary_key=True) uuid = db.Column(db.VARCHAR(40), nullable=False) timestamp_created = db.Column(db.TIMESTAMP, default=datetime.utcnow) def __init__(self, device): self.uuid = device def __repr__(self): return '<MQTT {}>'.format(self.uuid) def as_dict(self): data = { "uuid": self.service.as_dict(), "timestamp": int((self.timestamp_created - datetime.utcfromtimestamp(0)).total_seconds()), } return data @staticmethod def send_message(message): """ :type message: Message to send to mqtt subscribers """ subscriptions = Subscription.query.filter_by( service=message.service).all() if len(subscriptions) == 0: return 0 mqtt_devices = MQTT.query.filter( MQTT.uuid.in_([l.device for l in subscriptions])).all() if len(mqtt_devices) > 0: data = dict(message=message.as_dict(), encrypted=False) MQTT.mqtt_send([r.uuid for r in mqtt_devices], data) if len(mqtt_devices) > 0: uuids = [g.uuid for g in mqtt_devices] mqtt_subscriptions = Subscription.query.filter_by( service=message.service).filter( Subscription.device.in_(uuids)).all() last_message = Message.query.order_by(Message.id.desc()).first() for l in mqtt_subscriptions: l.timestamp_checked = datetime.utcnow() l.last_read = last_message.id if last_message else 0 db.session.commit() return len(mqtt_devices) @staticmethod def mqtt_send(uuids, data): url = cfg.mqtt_broker_address if ":" in url: port = url.split(":")[1] url = url.split(":")[0] else: # default port port = 1883 client = mqtt_api.Client() client.connect(url, port, 60) for uuid in uuids: client.publish(uuid, str(data)) client.disconnect()