def broadcast(cls, client_types, title, message, url=None, app_version=None): from models.notifications.broadcast import BroadcastNotification notification = BroadcastNotification(title, message, url, app_version) # Send to FCM clients fcm_client_types = [ct for ct in client_types if ct in ClientType.FCM_CLIENTS] if fcm_client_types: clients = MobileClient.query(MobileClient.client_type.IN(fcm_client_types)).fetch() if clients: cls._defer_fcm(clients, notification) # Send to webhooks if ClientType.WEBHOOK in client_types: clients = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() if clients: cls._defer_webhook(clients, notification) if ClientType.OS_ANDROID in client_types: clients = MobileClient.query(MobileClient.client_type == ClientType.OS_ANDROID).fetch() from helpers.push_helper import PushHelper keys = PushHelper.get_client_ids_for_clients(clients) from notifications.broadcast import BroadcastNotification notification = BroadcastNotification(title, message, url, app_version) notification.send(keys)
def test_delete_for_messaging_id(self): user_id_one = 'user_id_one' messaging_id_one = 'messaging_id1' messaging_id_two = 'messaging_id2' user_id_two = 'user_id_two' messaging_id_three = 'messaging_id3' for (user_id, messaging_ids) in [(user_id_one, [messaging_id_one, messaging_id_two]), (user_id_two, [messaging_id_three])]: for messaging_id in messaging_ids: MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=messaging_id, client_type=ClientType.OS_IOS, device_uuid=messaging_id[::-1], display_name='Phone').put() MobileClient.delete_for_messaging_id(messaging_id_one) clients_one = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_one').fetch()] clients_two = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_two').fetch()] self.assertEqual(clients_one, [messaging_id_two]) self.assertEqual(clients_two, [messaging_id_three]) MobileClient.delete_for_messaging_id(messaging_id_two) clients_one = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_one').fetch()] clients_two = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_two').fetch()] self.assertEqual(clients_one, []) self.assertEqual(clients_two, [messaging_id_three]) MobileClient.delete_for_messaging_id('does_not_exist')
def post(self): self._require_login("/account/register") self._require_registration("/account/register") # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get("account_id") if target_account_id == current_user_account_id: url = self.request.get("url") secret_key = self.request.get("secret") query = MobileClient.query( MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id) ) if query.count() == 0: # Webhook doesn't exist, add it verification_key = NotificationHelper.verify_webhook(url, secret_key) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=self.request.get("name"), secret=secret_key, client_type=ClientType.WEBHOOK, verified=False, verification_code=verification_key, ) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret_key current.put() self.redirect("/account") else: self.redirect("/")
def ping_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to ping client") user_id = PushHelper.user_email_to_id(current_user.email()) gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] response = NotificationHelper.send_ping(client) # If we got a response from the send_ping method, it was sent via TBANS # We'll bubble up any errors we got back if response: if response.code == 200: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse(code=response.code, message="Error pinging client - {}".format(response.message)) else: return BaseResponse(code=200, message="Ping sent")
def get(self): self._require_admin() all_clients = MobileClient.query() android = all_clients.filter( MobileClient.client_type == ClientType.OS_ANDROID).count() ios = all_clients.filter( MobileClient.client_type == ClientType.OS_IOS).count() web = all_clients.filter( MobileClient.client_type == ClientType.WEB).count() webhook = all_clients.filter( MobileClient.client_type == ClientType.WEBHOOK).count() push_enabled = NotificationsEnable.notifications_enabled() self.template_values.update({ 'mobile_users': all_clients.count(), 'android_users': android, 'ios_users': ios, 'web_users': web, 'webhooks': webhook, 'broadcast_success': self.request.get('broadcast_success'), 'push_enabled': push_enabled, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def get_client_ids_for_users(cls, os_type, user_list): output = [] for user in user_list: client_list = MobileClient.query(MobileClient.user_id == user, MobileClient.client_type == ClientType.enums[os_type]).fetch() for client in client_list: output.append(client.messaging_id) return output
def post(self): if not self.user_bundle.user: self.response.set_status(401) return user_id = self.user_bundle.user.user_id() fcm_token = self.request.get('fcm_token') uuid = self.request.get('uuid') display_name = self.request.get('display_name') client_type = ClientType.WEB query = MobileClient.query( MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == client_type) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=fcm_token, client_type=client_type, device_uuid=uuid, display_name=display_name).put() else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = fcm_token client.display_name = display_name client.put()
def register_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to register") gcm_id = request.mobile_id os = ClientType.enums[request.operating_system] name = request.name uuid = request.device_uuid query = MobileClient.query( MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == os) # trying to figure out an elusive dupe bug logging.info("DEBUGGING") logging.info("User ID: {}".format(user_id)) logging.info("UUID: {}".format(uuid)) logging.info("Count: {}".format(query.count())) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=gcm_id, client_type=os, device_uuid=uuid, display_name=name).put() return BaseResponse(code=200, message="Registration successful") else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = gcm_id client.display_name = name client.put() return BaseResponse(code=304, message="Client already exists")
def ping_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to ping client") user_id = PushHelper.user_email_to_id(current_user.email()) gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] from helpers.tbans_helper import TBANSHelper success = TBANSHelper.ping(client) if success: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse(code=500, message="Failed to ping client")
def post(self): self._require_login() self._require_registration() # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: url = self.request.get('url') secret_key = self.request.get('secret') query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key( Account, current_user_account_id)) if query.count() == 0: # Webhook doesn't exist, add it verification_key = NotificationHelper.verify_webhook( url, secret_key) client = MobileClient(parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=self.request.get('name'), secret=secret_key, client_type=ClientType.WEBHOOK, verified=False, verification_code=verification_key) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret_key current.put() self.redirect('/account') else: self.redirect('/')
def post(self): if not self.user_bundle.user: self.response.set_status(401) return user_id = self.user_bundle.user.user_id() fcm_token = self.request.get('fcm_token') uuid = self.request.get('uuid') display_name = self.request.get('display_name') client_type = ClientType.WEB query = MobileClient.query(MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == client_type) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient(parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=fcm_token, client_type=client_type, device_uuid=uuid, display_name=display_name).put() else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = fcm_token client.display_name = display_name client.put()
def ping_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to ping client") gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] response = NotificationHelper.send_ping(client) # If we got a response from the send_ping method, it was sent via TBANS # We'll bubble up any errors we got back if response: if response.code == 200: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse( code=response.code, message="Error pinging client - {}".format( response.message)) else: return BaseResponse(code=200, message="Ping sent")
def register_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to register") gcm_id = request.mobile_id os = ClientType.enums[request.operating_system] name = request.name uuid = request.device_uuid query = MobileClient.query(MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == os) # trying to figure out an elusive dupe bug logging.info("DEBUGGING") logging.info("User ID: {}".format(user_id)) logging.info("UUID: {}".format(uuid)) logging.info("Count: {}".format(query.count())) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient(parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=gcm_id, client_type=os, device_uuid=uuid, display_name=name).put() return BaseResponse(code=200, message="Registration successful") else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = gcm_id client.display_name = name client.put() return BaseResponse(code=304, message="Client already exists")
def get(self): self._require_admin() all_clients = MobileClient.query() android = all_clients.filter(MobileClient.client_type == ClientType.OS_ANDROID).count() ios = all_clients.filter(MobileClient.client_type == ClientType.OS_IOS).count() webhook = all_clients.filter(MobileClient.client_type == ClientType.WEBHOOK).count() var = Sitevar.get_by_id('notifications.enable') if var is None or not var.values_json == "true": push_enabled = False else: push_enabled = True self.template_values.update({ 'mobile_users': all_clients.count(), 'android_users': android, 'ios_users': ios, 'webhooks': webhook, 'broadcast_success': self.request.get('broadcast_success'), 'push_enabled': push_enabled, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def get_all_mobile_clients(cls, client_types=[]): output = [] if client_types == []: return output clients = MobileClient.query(MobileClient.client_type.IN(client_types)) for user in clients: output.append(user.user_id) return output
def get_client_ids_for_users(cls, user_list, os_types=None): if not user_list: return defaultdict(list) if os_types is None: os_types = ClientType.names.keys() clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() return cls.get_client_ids_for_clients(clients)
def get(self): self._require_admin() webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() self.template_values.update({ 'webhooks': webhooks, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_webhooks_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def get_client_ids_for_users(cls, user_list, os_types=None): if os_types is None: os_types = ClientType.names.keys() output = defaultdict(list) clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() for client in clients: if client.client_type == ClientType.WEBHOOK: output[client.client_type].append((client.messaging_id, client.secret)) else: output[client.client_type].append(client.messaging_id) return output
def unregister_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to unregister") userID = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcmId, ancestor=ndb.Key(Account, userID)).fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def unregister_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to unregister") gcm_id = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id))\ .fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def unregister_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to unregister") userID = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcmId, MobileClient.user_id == userID).fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def get_client_ids_for_users(cls, user_list, os_types=None): if os_types is None: os_types = ClientType.names.keys() output = defaultdict(list) clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() for client in clients: if client.client_type == ClientType.WEBHOOK: output[client.client_type].append( (client.messaging_id, client.secret)) else: output[client.client_type].append(client.messaging_id) return output
def broadcast(cls, client_types, title, message, url=None, app_version=None): from models.notifications.broadcast import BroadcastNotification notification = BroadcastNotification(title, message, url, app_version) # Send to FCM clients fcm_client_types = [ct for ct in client_types if ct in ClientType.FCM_CLIENTS] if fcm_client_types: clients = MobileClient.query(MobileClient.client_type.IN(fcm_client_types)).fetch() if clients: deferred.defer( cls._send_fcm, clients, notification, _queue="push-notifications", _url='/_ah/queue/deferred_notification_send' ) # Send to webhooks if ClientType.WEBHOOK in client_types: clients = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() if clients: deferred.defer( cls._send_webhook, clients, notification, _queue="push-notifications", _url='/_ah/queue/deferred_notification_send' ) if ClientType.OS_ANDROID in client_types: from helpers.push_helper import PushHelper users = PushHelper.get_all_mobile_clients([ClientType.OS_ANDROID]) keys = PushHelper.get_client_ids_for_users(users) from notifications.broadcast import BroadcastNotification notification = BroadcastNotification(title, message, url, app_version) notification.send(keys)
def register_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to register") userId = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id os = ClientType.enums[request.operating_system] if MobileClient.query( MobileClient.messaging_id==gcmId ).count() == 0: # Record doesn't exist yet, so add it MobileClient( messaging_id = gcmId, user_id = userId, client_type = os ).put() return BaseResponse(code=200, message="Registration successful") else: # Record already exists, don't bother updating it again return BaseResponse(code=304, message="Client already exists")
def get(self): webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() failures = [] for client in webhooks: response = TBANSHelper.ping_webhook(client) if not response.code == 200: failures.append(client.key) count = len(failures) if failures: ndb.delete_multi(failures) logging.info("Deleted {} broken webhooks".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html') self.response.out.write(template.render(path, template_values))
def get(self): webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() failures = [] notification = PingNotification()._render_webhook() for key in webhooks: if not NotificationSender.send_webhook(notification, [(key.messaging_id, key.secret)]): failures.append(key.key) count = len(failures) if failures: ndb.delete_multi(failures) logging.info("Deleted {} broken webhooks".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html') self.response.out.write(template.render(path, template_values))
def get(self): clients = MobileClient.query().fetch() clients = sorted(clients, key=lambda x: (x.messaging_id, x.updated)) last = None to_remove = [] last = None for client in clients: if last is not None and client.messaging_id == last.messaging_id: logging.info("Removing") to_remove.append(client.key) last = client count = len(to_remove) if to_remove: ndb.delete_multi(to_remove) logging.info("Removed {} duplicate mobile clients".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_clear_do.html') self.response.out.write(template.render(path, template_values))
def get(self): webhooks = MobileClient.query( MobileClient.client_type == ClientType.WEBHOOK).fetch() failures = [] from helpers.tbans_helper import TBANSHelper for client in webhooks: if not TBANSHelper.ping(client): failures.append(client.key) count = len(failures) if failures: ndb.delete_multi(failures) logging.info("Deleted {} broken webhooks".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html') self.response.out.write(template.render(path, template_values))
def post(self): self._require_registration() self._require_request_user_is_bundle_user() # Name and URL must be non-None url = self.request.get('url', None) name = self.request.get('name', None) if not url or not name: return self.redirect('/webhooks/add?error=1') # Secret may be none - but we'll generate a secret for the user secret = self.request.get('secret', None) if not secret: import uuid secret = uuid.uuid4().hex current_user_account_id = self.user_bundle.account.key.id() query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id)) if query.count() == 0: # Webhook doesn't exist, add it from helpers.tbans_helper import TBANSHelper response = TBANSHelper.verify_webhook(url, secret) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=name, secret=secret, client_type=ClientType.WEBHOOK, verified=False, verification_code=response.verification_key) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret current.put() self.redirect('/account')
def post(self): self._require_registration() self._require_request_user_is_bundle_user() # Name and URL must be non-None url = self.request.get('url', None) name = self.request.get('name', None) if not url or not name: return self.redirect('/webhooks/add?error=1') # Always generate secret server-side; previously allowed clients to set the secret import uuid secret = uuid.uuid4().hex current_user_account_id = self.user_bundle.account.key.id() query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id)) if query.count() == 0: # Webhook doesn't exist, add it from helpers.tbans_helper import TBANSHelper verification_key = TBANSHelper.verify_webhook(url, secret) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=name, secret=secret, client_type=ClientType.WEBHOOK, verified=False, verification_code=verification_key) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret current.put() self.redirect('/account')
def delete_bad_gcm_token(cls, key): logging.info("removing bad GCM token: "+key) to_delete = MobileClient.query(MobileClient.messaging_id == key).fetch(keys_only=True) ndb.delete_multi(to_delete)
def fcm_messaging_ids(user_id): clients = MobileClient.query( MobileClient.client_type.IN(ClientType.FCM_CLIENTS), ancestor=ndb.Key( Account, user_id)).fetch(projection=[MobileClient.messaging_id]) return [c.messaging_id for c in clients]
def delete_bad_gcm_token(cls, key): logging.info("removing bad GCM token: {}".format(key)) to_delete = MobileClient.query(MobileClient.messaging_id == key).fetch( keys_only=True) ndb.delete_multi(to_delete)
def update_token(cls, old, new): to_update = MobileClient.query( MobileClient.messaging_id == old).fetch() for model in to_update: model.messaging_id = new model.put()
def update_token(cls, old, new): to_update = MobileClient.query(MobileClient.messaging_id == old).fetch() for model in to_update: model.messaging_id = new model.put()
def update_token(cls, old, new): logging.info("updating token"+old+"\n->"+new) to_update = MobileClient.query(MobileClient.messaging_id == old).fetch() for model in to_update: model.messaging_id = new model.put()
def mobile_clients(self): user_id = self.user.user_id() return MobileClient.query(ancestor=ndb.Key(Account, user_id)).fetch()