123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import datetime
- import json
- from typing import Dict, List, Any
- import requests
- from geventwebsocket import WebSocketError
- from geventwebsocket.websocket import WebSocket
- import model
- from debug import debug
- from my_types import MessageType, Message, UserIdentification, UserId, MessageQueue
- from routes import push_message_types
- PORT = 58317
- if debug:
- host = 'http://localhost:' + str(PORT)
- else:
- host = 'http://koljastrohm-games.com:' + str(PORT)
- websockets_for_user: Dict[UserIdentification, List[WebSocket]] = {}
- users_for_websocket: Dict[WebSocket, List[UserIdentification]] = {}
- push_message_queue: MessageQueue = []
- session_id = None
- class HttpError(Exception):
- def __init__(self, code: int, message: Any = None, prepend_code=True):
- Exception.__init__(self)
- self.body = dict()
- self.code = code
- if message is None:
- message = str(code)
- if prepend_code and isinstance(message, str) and not message.startswith(str(code)):
- message = str(code) + ': ' + message
- if code // 100 == 2:
- self.body['message'] = message
- else:
- self.body['error'] = message
- elif isinstance(message, str):
- if code // 100 == 2:
- self.body['message'] = message
- else:
- self.body['error'] = message
- elif isinstance(message, dict):
- self.body = message.copy()
- else: # for example a list or number
- self.body['data'] = message
- class NotFound(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 404, message, prepend_code)
- class Success(HttpError):
- def __init__(self, message: Any = None, prepend_code=False):
- HttpError.__init__(self, 200, message, prepend_code)
- class UnavailableForLegalReasons(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 451, message, prepend_code)
- class Forbidden(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 403, message, prepend_code)
- class Unauthorized(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 401, message, prepend_code)
- class BadRequest(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 400, message, prepend_code)
- class InternalServerError(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 500, message, prepend_code)
- class Locked(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 423, message, prepend_code)
- class PreconditionFailed(HttpError):
- def __init__(self, message: Any = None, prepend_code=True):
- HttpError.__init__(self, 412, message, prepend_code)
- def check_missing_attributes(request_json: Dict, attributes: List[str]):
- for attr in attributes:
- if attr not in request_json:
- if str(attr) == 'session_id':
- raise Unauthorized('You are not signed in.')
- raise BadRequest('Missing value for attribute ' + str(attr))
- if str(attr) == 'session_id':
- if not model.valid_session_id(request_json['session_id']):
- raise Unauthorized('You are not signed in.')
- def client_request(route, data=None):
- if data is None:
- data = {}
- return json_request(host + '/json/' + route, data)
- def json_request(url, data):
- if debug:
- print('Sending to ' + url + ': ' + str(json.dumps(data)))
- r = requests.post(url,
- data=json.dumps(data),
- headers={'Content-type': 'application/json; charset=latin-1'})
- if debug:
- print('Request returned: ' + str(r.content))
- return r.json()
- def push_message(recipient_ids: List[UserIdentification], contents: Message, message_type: MessageType):
- if message_type not in push_message_types:
- raise AssertionError('Invalid message type.')
- sockets = {socket for user_id in recipient_ids for socket in websockets_for_user.get(user_id, [])}
- if len(sockets) > 0:
- message = {'message_type': message_type, 'contents': contents}
- for ws in sockets:
- if ws.closed:
- ws_cleanup(ws)
- continue
- message = json.dumps({'message_type': message_type, 'contents': contents})
- ws.send(message)
- print(message_type,
- 'to',
- len(sockets),
- 'sockets',
- datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- len(message))
- def enqueue_push_message(recipient_ids: List[UserId], contents: Dict, message_type: str):
- assert message_type in push_message_types
- recipient_ids = [user_id for user_id in recipient_ids]
- if len(recipient_ids) == 0:
- return
- recipient_ids = [(model.current_db_name, user_id) for user_id in recipient_ids]
- push_message_queue.append((recipient_ids, contents, message_type))
- def ws_cleanup(ws):
- if ws in users_for_websocket:
- users_affected = users_for_websocket[ws]
- for user_id in users_affected:
- # remove the websocket from all lists
- websockets_for_user[user_id][:] = filter(lambda s: s != ws,
- websockets_for_user[user_id])
- del users_for_websocket[ws]
- if not ws.closed:
- ws.close()
- print('websocket connection ended',
- *ws.handler.client_address,
- datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), )
- def preprocess_push_message_queue(queue: MessageQueue) -> MessageQueue:
- return queue
- def push_messages_in_queue():
- global push_message_queue
- push_message_queue = preprocess_push_message_queue(push_message_queue)
- for message in push_message_queue:
- try:
- push_message(*message)
- except WebSocketError:
- continue
- except ConnectionResetError:
- continue
- del push_message_queue[:]
|