import json from datetime import timedelta, datetime from math import ceil, floor from bottle import request, response from passlib.hash import sha256_crypt import model from debug import debug from util import salt def missing_attributes(attributes): for attr in attributes: if attr not in request.json or request.json[attr] == '' or request.json[attr] is None: if str(attr) == 'session_id': return 'You are not signed in.' return 'Missing value for attribute ' + str(attr) if str(attr) == 'session_id': if not model.valid_session_id(request.json['session_id']): return 'You are not signed in.' return False def login(): if debug: missing = missing_attributes(['username']) else: missing = missing_attributes(['username', 'password']) if missing: return bad_request(missing) username = request.json['username'] password = request.json['password'] session_id = model.login(username, password) if session_id: return {'session_id': session_id} else: return forbidden('Invalid login data') def depot(): missing = missing_attributes(['session_id']) if missing: return bad_request(missing) user_id = model.get_user_id_by_session_id(request.json['session_id']) return {'data': model.get_user_ownership(user_id), 'own_wealth': model.user_wealth(user_id)} def register(): missing = missing_attributes(['username', 'password']) if missing: return bad_request(missing) username = request.json['username'].strip() if username == '': return bad_request('Username can not be empty.') hashed_password = sha256_crypt.encrypt(request.json['password'] + salt) if model.user_exists(username): return bad_request('User already exists.') game_key = '' if 'game_key' in request.json: game_key = request.json['game_key'].strip().upper() if game_key != '' and not model.valid_key(game_key): return bad_request('Game key is not valid.') if model.register(username, hashed_password, game_key): return {'message': "successfully registered user"} else: return bad_request('Registration not successful') def activate_key(): missing = missing_attributes(['key', 'session_id']) if missing: return bad_request(missing) if model.valid_key(request.json['key']): user_id = model.get_user_id_by_session_id(request.json['session_id']) model.activate_key(request.json['key'], user_id) return {'message': "successfully activated key"} else: return bad_request('Invalid key.') def order(): missing = missing_attributes(['buy', 'session_id', 'amount', 'ownable', 'time_until_expiration']) if missing: return bad_request(missing) if not model.ownable_name_exists(request.json['ownable']): return bad_request('This kind of object can not be ordered.') buy = request.json['buy'] sell = not buy if not isinstance(buy, bool): return bad_request('`buy` must be a boolean') session_id = request.json['session_id'] amount = request.json['amount'] try: amount = int(amount) except ValueError: return bad_request('Invalid amount.') if amount < 0: return bad_request('You can not order a negative amount.') if amount < 1: return bad_request('The minimum order size is 1.') ownable_name = request.json['ownable'] time_until_expiration = float(request.json['time_until_expiration']) if time_until_expiration < 0: return bad_request('Invalid expiration time.') ownable_id = model.ownable_id_by_name(ownable_name) user_id = model.get_user_id_by_session_id(session_id) model.own(user_id, ownable_name) ownership_id = model.get_ownership_id(ownable_id, user_id) try: if request.json['limit'] == '': limit = None elif request.json['limit'] is None: limit = None else: if buy: limit = floor(float(request.json['limit']) * 10000) / 10000 else: limit = ceil(float(request.json['limit']) * 10000) / 10000 except ValueError: # for example when float fails return bad_request('Invalid limit.') except KeyError: # for example when limit was not specified limit = None if limit < 0: return bad_request('Limit must not be negative.') try: if request.json['stop_loss'] == '': stop_loss = None elif request.json['stop_loss'] is None: stop_loss = None else: stop_loss = 'stop_loss' in request.json and request.json['stop_loss'] if stop_loss is not None and limit is None: return bad_request('Can only set stop-loss for limit orders') except KeyError: # for example when stop_loss was not specified stop_loss = None if sell: if not model.user_has_at_least_available(amount, user_id, ownable_id): return bad_request('You can not sell more than you own.') try: expiry = datetime.strptime(model.current_db_time(), '%Y-%m-%d %H:%M:%S') + \ timedelta(minutes=time_until_expiration) except OverflowError: return bad_request('The expiration time is too far in the future.') model.place_order(buy, ownership_id, limit, stop_loss, amount, expiry) return {'message': "Order placed."} def gift(): missing = missing_attributes(['session_id', 'amount', 'object_name', 'username']) if missing: return bad_request(missing) if not model.ownable_name_exists(request.json['object_name']): return bad_request('This kind of object can not be given away.') if request.json['username'] == 'bank' or not model.user_exists(request.json['username']): return bad_request('There is no user with this name.') try: amount = float(request.json['amount']) except ValueError: return bad_request('Invalid amount.') ownable_id = model.ownable_id_by_name(request.json['object_name']) sender_id = model.get_user_id_by_session_id(request.json['session_id']) if model.available_amount(sender_id, ownable_id) == 0: return bad_request('You do not own any of these.') if not model.user_has_at_least_available(amount, sender_id, ownable_id): # for example if you have a 1.23532143213 Kollar and want to give them all away amount = model.available_amount(sender_id, ownable_id) recipient_id = model.get_user_id_by_name(request.json['username']) model.send_ownable(sender_id, recipient_id, ownable_id, amount) return {'message': "Gift sent."} def orders(): missing = missing_attributes(['session_id']) if missing: return bad_request(missing) data = model.get_user_orders(model.get_user_id_by_session_id(request.json['session_id'])) return {'data': data} def orders_on(): missing = missing_attributes(['session_id', 'ownable']) if missing: return bad_request(missing) if not model.ownable_name_exists(request.json['ownable']): return bad_request('This kind of object can not be ordered.') user_id = model.get_user_id_by_session_id(request.json['session_id']) ownable_id = model.ownable_id_by_name(request.json['ownable']) data = model.get_ownable_orders(user_id, ownable_id) return {'data': data} def old_orders(): missing = missing_attributes(['session_id', 'include_canceled', 'include_executed', 'limit']) if missing: return bad_request(missing) include_executed = request.json['include_executed'] include_canceled = request.json['include_canceled'] user_id = model.get_user_id_by_session_id(request.json['session_id']) limit = request.json['limit'] data = model.get_old_orders(user_id, include_executed, include_canceled, limit) return {'data': data} def cancel_order(): missing = missing_attributes(['session_id', 'order_id']) if missing: return bad_request(missing) if not model.user_has_order_with_id(request.json['session_id'], request.json['order_id']): return bad_request('You do not have an order with that number.') model.delete_order(request.json['order_id'], 'Canceled') return {'message': "Successfully deleted order"} def change_password(): missing = missing_attributes(['session_id', 'password']) if missing: return bad_request(missing) hashed_password = sha256_crypt.encrypt(request.json['password'] + salt) model.change_password(request.json['session_id'], hashed_password) model.sign_out_user(request.json['session_id']) return {'message': "Successfully changed password"} def news(): return {'data': model.news()} def tradables(): return {'data': model.ownables()} def trades(): missing = missing_attributes(['session_id', 'limit']) if missing: return bad_request(missing) return {'data': model.trades(model.get_user_id_by_session_id(request.json['session_id']), request.json['limit'])} def trades_on(): missing = missing_attributes(['session_id', 'ownable', 'limit']) if missing: return bad_request(missing) if not model.ownable_name_exists(request.json['ownable']): return bad_request('This kind of object can not have transactions.') return {'data': model.trades_on(model.ownable_id_by_name(request.json['ownable']), request.json['limit'])} def leaderboard(): return {'data': model.leaderboard()} def not_found(msg=''): response.status = 404 if debug: msg = str(response.status) + ': ' + msg response.content_type = 'application/json' return json.dumps({"error_message": msg}) def forbidden(msg=''): response.status = 403 if debug: msg = str(response.status) + ': ' + msg response.content_type = 'application/json' return json.dumps({"error_message": msg}) def bad_request(msg=''): response.status = 400 if debug: msg = str(response.status) + ': ' + msg response.content_type = 'application/json' return json.dumps({"error_message": msg}) def internal_server_error(msg=''): response.status = 500 if debug: msg = str(response.status) + ': ' + msg response.content_type = 'application/json' return json.dumps({"error_message": msg})