From 88470055c8071a35c00fe2de646d065593128078 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Wed, 24 Jun 2020 11:39:49 +0200 Subject: [PATCH] Removed expo notifications --- expo_notifications/dao.php | 120 ------- expo_notifications/en.json | 10 - .../exponent_server_sdk/__init__.py | 319 ------------------ expo_notifications/fr.json | 10 - expo_notifications/handler.py | 163 --------- expo_notifications/save_token.php | 52 --- 6 files changed, 674 deletions(-) delete mode 100644 expo_notifications/dao.php delete mode 100644 expo_notifications/en.json delete mode 100644 expo_notifications/exponent_server_sdk/__init__.py delete mode 100644 expo_notifications/fr.json delete mode 100644 expo_notifications/handler.py delete mode 100644 expo_notifications/save_token.php diff --git a/expo_notifications/dao.php b/expo_notifications/dao.php deleted file mode 100644 index 106988a..0000000 --- a/expo_notifications/dao.php +++ /dev/null @@ -1,120 +0,0 @@ -debug = file_exists(__DIR__ . DIRECTORY_SEPARATOR . "DEBUG"); - } - - - public function __construct() - { - $this->get_debug_mode(); - if ($this->debug) { - $username = 'test'; - $password = $this->read_password();; - $dsn = 'mysql:dbname=test;host=127.0.0.1'; - } else { - $username = 'amicale_app'; - $password = $this->read_password(); - $dsn = 'mysql:dbname=amicale_app;host=127.0.0.1'; - } - try { - $this->conn = new PDO($dsn, $username, $password, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); - } catch (PDOException $e) { - echo $e; - } - } - - private function read_password() - { - if ($this->debug) - $real_path = __DIR__ . DIRECTORY_SEPARATOR . ".htpassdb_debug"; - else - $real_path = __DIR__ . DIRECTORY_SEPARATOR . ".htpassdb"; - $file = fopen($real_path, "r") or die("Unable to open DB password file!");; - $password = fgets($file); - fclose($file); - return trim($password); - } - - /** - * Return the list of machines watched by the user associated by the given token - * - * @param $token - * @return array - */ - public function get_machine_watchlist($token) { - $this->register_user($token); - $sql = "SELECT machine_id FROM machine_watchlist WHERE user_token=:token"; - $cursor = $this->conn->prepare($sql); // Protect against SQL injections - $cursor->bindParam(':token', $token); - $cursor->execute(); - $result = $cursor->fetchAll(); - $finalArray = []; - foreach ($result as $row) { - array_push($finalArray, $row["machine_id"]); - } - return $finalArray; - } - - - public function set_machine_reminder($token, $time) { - $this->register_user($token); - $sql = "UPDATE users SET machine_reminder_time=:time WHERE token=:token"; - $cursor = $this->conn->prepare($sql); // Protect against SQL injections - $cursor->bindParam(':token', $token); - $cursor->bindParam(':time', $time); - var_dump($cursor->execute()); - } - - - /** - * Add/Remove a machine from the database for the specified token. - * - * @param $token - * @param $machine_id - * @param $should_add - */ - public function update_machine_end_token($token, $machine_id, $should_add, $locale) - { - $this->register_user($token); - $this->update_user_locale($token, $locale); - if ($should_add) - $sql = "INSERT INTO machine_watchlist (machine_id, user_token) VALUES (:id, :token)"; - else - $sql = "DELETE FROM machine_watchlist WHERE machine_id=:id AND user_token=:token"; - $cursor = $this->conn->prepare($sql); // Protect against SQL injections - $cursor->bindParam(':id', $machine_id); - $cursor->bindParam(':token', $token); - $cursor->execute(); - } - - - /** - * Register user in the database if not already in it - * @param $userToken - * @param $locale - */ - private function register_user($userToken) { - $sql = "INSERT INTO users (token) VALUES (:token)"; - $cursor = $this->conn->prepare($sql); // Protect against SQL injections - $cursor->bindParam(':token', $userToken); - $cursor->execute(); - } - - private function update_user_locale($token, $locale) { - $sql = "UPDATE users SET locale=:locale WHERE token=:token"; - $cursor = $this->conn->prepare($sql); // Protect against SQL injections - $cursor->bindParam(':token', $token); - $cursor->bindParam(':locale', $locale); - $cursor->execute(); - } - -} - - diff --git a/expo_notifications/en.json b/expo_notifications/en.json deleted file mode 100644 index 6bce783..0000000 --- a/expo_notifications/en.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "endNotification": { - "title": "Machine n°%s finished", - "body": "Come get your laundry in machine n°%s" - }, - "reminderNotification": { - "title": "Machine n°%s running...", - "body": "Don't forget your laundry in machine n°%s" - } -} \ No newline at end of file diff --git a/expo_notifications/exponent_server_sdk/__init__.py b/expo_notifications/exponent_server_sdk/__init__.py deleted file mode 100644 index a28a0c0..0000000 --- a/expo_notifications/exponent_server_sdk/__init__.py +++ /dev/null @@ -1,319 +0,0 @@ -from collections import namedtuple -import json - - -class PushResponseError(Exception): - """Base class for all push reponse errors""" - - def __init__(self, push_response): - if push_response.message: - self.message = push_response.message - else: - self.message = 'Unknown push response error' - super(PushResponseError, self).__init__(self.message) - - self.push_response = push_response - - -class DeviceNotRegisteredError(PushResponseError): - """Raised when the push token is invalid - - To handle this error, you should stop sending messages to this token. - """ - pass - - -class MessageTooBigError(PushResponseError): - """Raised when the notification was too large. - - On Android and iOS, the total payload must be at most 4096 bytes. - """ - pass - - -class MessageRateExceededError(PushResponseError): - """Raised when you are sending messages too frequently to a device - - You should implement exponential backoff and slowly retry sending messages. - """ - pass - - -class PushServerError(Exception): - """Raised when the push token server is not behaving as expected - - For example, invalid push notification arguments result in a different - style of error. Instead of a "data" array containing errors per - notification, an "error" array is returned. - - {"errors": [ - {"code": "API_ERROR", - "message": "child \"to\" fails because [\"to\" must be a string]. \"value\" must be an array." - } - ]} - """ - - def __init__(self, message, response, response_data=None, errors=None): - self.message = message - self.response = response - self.response_data = response_data - self.errors = errors - super(PushServerError, self).__init__(self.message) - - -class PushMessage(namedtuple('PushMessage', [ - 'to', 'data', 'title', 'body', 'sound', 'ttl', 'expiration', - 'priority', 'badge', 'channel_id'])): - """An object that describes a push notification request. - - You can override this class to provide your own custom validation before - sending these to the Exponent push servers. You can also override the - get_payload function itself to take advantage of any hidden or new - arguments before this library updates upstream. - - Args: - to: A token of the form ExponentPushToken[xxxxxxx] - data: A dict of extra data to pass inside of the push notification. - The total notification payload must be at most 4096 bytes. - title: The title to display in the notification. On iOS, this is - displayed only on Apple Watch. - body: The message to display in the notification. - sound: A sound to play when the recipient receives this - notification. Specify "default" to play the device's default - notification sound, or omit this field to play no sound. - ttl: The number of seconds for which the message may be kept around - for redelivery if it hasn't been delivered yet. Defaults to 0. - expiration: UNIX timestamp for when this message expires. It has - the same effect as ttl, and is just an absolute timestamp - instead of a relative one. - priority: Delivery priority of the message. 'default', 'normal', - and 'high' are the only valid values. - badge: An integer representing the unread notification count. This - currently only affects iOS. Specify 0 to clear the badge count. - channel_id: ID of the Notification Channel through which to display - this notification on Android devices. - - """ - def get_payload(self): - # Sanity check for invalid push token format. - if not PushClient.is_exponent_push_token(self.to): - raise ValueError('Invalid push token') - - # There is only one required field. - payload = { - 'to': self.to, - } - - # All of these fields are optional. - if self.data is not None: - payload['data'] = self.data - if self.title is not None: - payload['title'] = self.title - if self.body is not None: - payload['body'] = self.body - if self.sound is not None: - payload['sound'] = self.sound - if self.ttl is not None: - payload['ttl'] = self.ttl - if self.expiration is not None: - payload['expiration'] = self.expiration - if self.priority is not None: - payload['priority'] = self.priority - if self.badge is not None: - payload['badge'] = self.badge - if self.channel_id is not None: - payload['channelId'] = self.channel_id - return payload - - -# Allow optional arguments for PushMessages since everything but the `to` field -# is optional. Unfortunately namedtuples don't allow for an easy way to create -# a required argument at the contructor level right now. -PushMessage.__new__.__defaults__ = (None,) * len(PushMessage._fields) - - -class PushResponse(namedtuple('PushResponse', [ - 'push_message', 'status', 'message', 'details'])): - """Wrapper class for a push notification response. - - A successful single push notification: - {'status': 'ok'} - - An invalid push token - {'status': 'error', - 'message': '"adsf" is not a registered push notification recipient'} - """ - # Known status codes - ERROR_STATUS = 'error' - SUCCESS_STATUS = 'ok' - - # Known error strings - ERROR_DEVICE_NOT_REGISTERED = 'DeviceNotRegistered' - ERROR_MESSAGE_TOO_BIG = 'MessageTooBig' - ERROR_MESSAGE_RATE_EXCEEDED = 'MessageRateExceeded' - - def is_success(self): - """Returns True if this push notification successfully sent.""" - return self.status == PushResponse.SUCCESS_STATUS - - def validate_response(self): - """Raises an exception if there was an error. Otherwise, do nothing. - - Clients should handle these errors, since these require custom handling - to properly resolve. - """ - if self.is_success(): - return - - # Handle the error if we have any information - if self.details: - error = self.details.get('error', None) - - if error == PushResponse.ERROR_DEVICE_NOT_REGISTERED: - raise DeviceNotRegisteredError(self) - elif error == PushResponse.ERROR_MESSAGE_TOO_BIG: - raise MessageTooBigError(self) - elif error == PushResponse.ERROR_MESSAGE_RATE_EXCEEDED: - raise MessageRateExceededError(self) - - # No known error information, so let's raise a generic error. - raise PushResponseError(self) - - -class PushClient(object): - """Exponent push client - - See full API docs at https://docs.expo.io/versions/latest/guides/push-notifications.html#http2-api - """ - DEFAULT_HOST = "https://exp.host" - DEFAULT_BASE_API_URL = "/--/api/v2" - - def __init__(self, host=None, api_url=None): - """Construct a new PushClient object. - - Args: - host: The server protocol, hostname, and port. - api_url: The api url at the host. - """ - self.host = host - if not self.host: - self.host = PushClient.DEFAULT_HOST - - self.api_url = api_url - if not self.api_url: - self.api_url = PushClient.DEFAULT_BASE_API_URL - - @classmethod - def is_exponent_push_token(cls, token): - """Returns `True` if the token is an Exponent push token""" - import six - - return ( - isinstance(token, six.string_types) and - token.startswith('ExponentPushToken')) - - def _publish_internal(self, push_messages): - """Send push notifications - - The server will validate any type of syntax errors and the client will - raise the proper exceptions for the user to handle. - - Each notification is of the form: - { - 'to': 'ExponentPushToken[xxx]', - 'body': 'This text gets display in the notification', - 'badge': 1, - 'data': {'any': 'json object'}, - } - - Args: - push_messages: An array of PushMessage objects. - """ - # Delayed import because this file is immediately read on install, and - # the requests library may not be installed yet. - import requests - - response = requests.post( - self.host + self.api_url + '/push/send', - data=json.dumps([pm.get_payload() for pm in push_messages]), - headers={ - 'accept': 'application/json', - 'accept-encoding': 'gzip, deflate', - 'content-type': 'application/json', - } - ) - - # Let's validate the response format first. - try: - response_data = response.json() - except ValueError: - # The response isn't json. First, let's attempt to raise a normal - # http error. If it's a 200, then we'll raise our own error. - response.raise_for_status() - - raise PushServerError('Invalid server response', response) - - # If there are errors with the entire request, raise an error now. - if 'errors' in response_data: - raise PushServerError( - 'Request failed', - response, - response_data=response_data, - errors=response_data['errors']) - - # We expect the response to have a 'data' field with the responses. - if 'data' not in response_data: - raise PushServerError( - 'Invalid server response', - response, - response_data=response_data) - - # Use the requests library's built-in exceptions for any remaining 4xx - # and 5xx errors. - response.raise_for_status() - - # Sanity check the response - if len(push_messages) != len(response_data['data']): - raise PushServerError( - ('Mismatched response length. Expected %d %s but only ' - 'received %d' % ( - len(push_messages), - 'receipt' if len(push_messages) == 1 else 'receipts', - len(response_data['data']))), - response, - response_data=response_data) - - # At this point, we know it's a 200 and the response format is correct. - # Now let's parse the responses per push notification. - receipts = [] - for i, receipt in enumerate(response_data['data']): - receipts.append(PushResponse( - push_message=push_messages[i], - # If there is no status, assume error. - status=receipt.get('status', PushResponse.ERROR_STATUS), - message=receipt.get('message', ''), - details=receipt.get('details', None))) - - return receipts - - def publish(self, push_message): - """Sends a single push notification - - Args: - push_message: A single PushMessage object. - - Returns: - A PushResponse object which contains the results. - """ - return self.publish_multiple([push_message])[0] - - def publish_multiple(self, push_messages): - """Sends multiple push notifications at once - - Args: - push_messages: An array of PushMessage objects. - - Returns: - An array of PushResponse objects which contains the results. - """ - return self._publish_internal(push_messages) diff --git a/expo_notifications/fr.json b/expo_notifications/fr.json deleted file mode 100644 index 4475113..0000000 --- a/expo_notifications/fr.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "endNotification": { - "title": "Machine n°%s terminée", - "body": "Vous pouvez venir chercher votre linge dans la machine n°%s" - }, - "reminderNotification": { - "title": "Machine n°%s en cours...", - "body": "N'oubliez pas votre linge dans la machine n°%s" - } -} \ No newline at end of file diff --git a/expo_notifications/handler.py b/expo_notifications/handler.py deleted file mode 100644 index 9cec8a9..0000000 --- a/expo_notifications/handler.py +++ /dev/null @@ -1,163 +0,0 @@ -from exponent_server_sdk import PushClient -from exponent_server_sdk import PushMessage -import mysql.connector # using lib from https://github.com/expo/expo-server-sdk-python -import json -from enum import Enum - -isDebug = False - - -class Priority(Enum): - DEFAULT = 'default' - NORMAL = 'normal' - HIGH = 'high' - - -class ChannelIDs(Enum): - REMINDERS = 'reminders' - - -class MachineStates(Enum): - FINISHED = 'TERMINE' - READY = 'DISPONIBLE' - RUNNING = 'EN COURS' - BROKEN = 'HS' - ERROR = 'ERROR' - - -if isDebug: - washinsaFile = 'data.json' - db = mysql.connector.connect( - host="127.0.0.1", - user="test", - passwd="coucou", - database="test" - ) -else: - washinsaFile = '../washinsa/washinsa.json' - db = mysql.connector.connect( - host="127.0.0.1", - user="amicale_app", - passwd="EYiDCalfNj", - database="amicale_app" - ) - - -def send_push_message(token, title, body, channel_id, extra=None): - prio = Priority.HIGH.value if channel_id == ChannelIDs.REMINDERS.value else Priority.NORMAL.value - print(prio) - response = PushClient().publish( - PushMessage(to=token, - title=title, - body=body, - data=extra, - sound='default', - priority=prio)) - - -def get_machines_of_state(state): - machines = [] - with open(washinsaFile) as f: - data = json.load(f) - for d in data['dryers']: - if d['state'] == state: - machines.append(d['number']) - for d in data['washers']: - if d['state'] == state: - machines.append(d['number']) - return machines - - -def get_machine_remaining_time(machine_id): - with open(washinsaFile) as f: - data = json.load(f) - for d in data['dryers']: - if d['number'] == machine_id: - return int(d['remainingTime']) - for d in data['washers']: - if d['number'] == machine_id: - return int(d['remainingTime']) - return 0 - - -def send_end_notifications(): - cursor = db.cursor() - machines = get_machines_of_state(MachineStates.FINISHED.value) - for machine_id in machines: - cursor.execute('SELECT * FROM machine_watchlist WHERE machine_id=%s', (machine_id,)) - result = cursor.fetchall() - for r in result: - token = r[2] - translation = get_notification_translation(token, False) - body = translation["body"].replace("%s", machine_id, 1) - title = translation["title"].replace("%s", machine_id, 1) - # Remove from db - cursor.execute('DELETE FROM machine_watchlist WHERE machine_id=%s AND user_token=%s', (machine_id, token,)) - db.commit() - send_push_message(token, title, body, ChannelIDs.REMINDERS.value) - - -def send_reminder_notifications(): - cursor = db.cursor() - machines = get_machines_of_state(MachineStates.RUNNING.value) - print(machines) - for machine_id in machines: - remaining_time = get_machine_remaining_time(machine_id) - print(remaining_time) - cursor.execute('SELECT * FROM machine_watchlist WHERE machine_id=%s', (machine_id,)) - result = cursor.fetchall() - print(result) - for r in result: - if r[3] == 0: # We did not send a reminder notification yet - token = r[2] - user_reminder_time = get_user_reminder_time(token) - if user_reminder_time >= remaining_time: - translation = get_notification_translation(token, True) - body = translation["body"].replace("%s", machine_id, 1) - title = translation["title"].replace("%s", machine_id, 1) - cursor.execute( - 'UPDATE machine_watchlist SET reminder_sent=%s WHERE machine_id=%s AND user_token=%s', - (1, machine_id, token,)) - db.commit() - send_push_message(token, title, body, ChannelIDs.REMINDERS.value) - - -def get_user_reminder_time(token): - cursor = db.cursor() - cursor.execute('SELECT machine_reminder_time FROM users WHERE token=%s', (token,)) - result = cursor.fetchall() - print(result[0][0]) - return result[0][0] - - -def get_user_locale(token): - cursor = db.cursor() - cursor.execute('SELECT locale FROM users WHERE token=%s', (token,)) - result = cursor.fetchall() - print(result[0][0]) - locale = 'en' - if "fr" in result[0][0]: - locale = 'fr' - return locale - - -def get_notification_translation(token, is_reminder): - locale = get_user_locale(token) - file_name = locale + '.json' - print(file_name) - with open(file_name) as f: - data = json.load(f) - if is_reminder: - translation = data["reminderNotification"] - else: - translation = data["endNotification"] - print(translation) - return translation - - -def main(): - send_reminder_notifications() - send_end_notifications() - - -main() diff --git a/expo_notifications/save_token.php b/expo_notifications/save_token.php deleted file mode 100644 index c054fa8..0000000 --- a/expo_notifications/save_token.php +++ /dev/null @@ -1,52 +0,0 @@ -update_machine_end_token($token, $machineId, $enabled, $locale); -} - -function get_machine_watchlist() { - $token = $_POST['token']; - - $dao = new Dao(); - echo json_encode($dao->get_machine_watchlist($token)); -} - -function set_machine_reminder() { - $token = $_POST['token']; - $time = intval($_POST['time']); - - $dao = new Dao(); - $dao->set_machine_reminder($token, $time); -} - - -function show_error() { - echo "Échec :\n"; - var_dump($_POST); -}