server_controller.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import json
  2. from datetime import timedelta, datetime
  3. from math import ceil, floor
  4. from bottle import request, response
  5. from passlib.hash import sha256_crypt
  6. import model
  7. from debug import debug
  8. from util import salt
  9. def missing_attributes(attributes):
  10. for attr in attributes:
  11. if attr not in request.json or request.json[attr] == '' or request.json[attr] is None:
  12. if str(attr) == 'session_id':
  13. return 'You are not signed in.'
  14. return 'Missing value for attribute ' + str(attr)
  15. if str(attr) == 'session_id':
  16. if not model.valid_session_id(request.json['session_id']):
  17. return 'You are not signed in.'
  18. return False
  19. def login():
  20. if debug:
  21. missing = missing_attributes(['username'])
  22. else:
  23. missing = missing_attributes(['username', 'password'])
  24. if missing:
  25. return bad_request(missing)
  26. username = request.json['username']
  27. password = request.json['password']
  28. session_id = model.login(username, password)
  29. if session_id:
  30. return {'session_id': session_id}
  31. else:
  32. return forbidden('Invalid login data')
  33. def depot():
  34. missing = missing_attributes(['session_id'])
  35. if missing:
  36. return bad_request(missing)
  37. user_id = model.get_user_id_by_session_id(request.json['session_id'])
  38. return {'data': model.get_user_ownership(user_id),
  39. 'own_wealth': model.user_wealth(user_id)}
  40. def register():
  41. missing = missing_attributes(['username', 'password'])
  42. if missing:
  43. return bad_request(missing)
  44. username = request.json['username'].strip()
  45. if username == '':
  46. return bad_request('Username can not be empty.')
  47. hashed_password = sha256_crypt.encrypt(request.json['password'] + salt)
  48. if model.user_exists(username):
  49. return bad_request('User already exists.')
  50. game_key = ''
  51. if 'game_key' in request.json:
  52. game_key = request.json['game_key'].strip().upper()
  53. if game_key != '' and not model.valid_key(game_key):
  54. return bad_request('Game key is not valid.')
  55. if model.register(username, hashed_password, game_key):
  56. return {'message': "successfully registered user"}
  57. else:
  58. return bad_request('Registration not successful')
  59. def activate_key():
  60. missing = missing_attributes(['key', 'session_id'])
  61. if missing:
  62. return bad_request(missing)
  63. if model.valid_key(request.json['key']):
  64. user_id = model.get_user_id_by_session_id(request.json['session_id'])
  65. model.activate_key(request.json['key'], user_id)
  66. return {'message': "successfully activated key"}
  67. else:
  68. return bad_request('Invalid key.')
  69. def order():
  70. missing = missing_attributes(['buy', 'session_id', 'amount', 'ownable', 'time_until_expiration'])
  71. if missing:
  72. return bad_request(missing)
  73. if not model.ownable_name_exists(request.json['ownable']):
  74. return bad_request('This kind of object can not be ordered.')
  75. buy = request.json['buy']
  76. sell = not buy
  77. if not isinstance(buy, bool):
  78. return bad_request('`buy` must be a boolean')
  79. session_id = request.json['session_id']
  80. amount = request.json['amount']
  81. try:
  82. amount = int(amount)
  83. except ValueError:
  84. return bad_request('Invalid amount.')
  85. if amount < 0:
  86. return bad_request('You can not order a negative amount.')
  87. if amount < 1:
  88. return bad_request('The minimum order size is 1.')
  89. ownable_name = request.json['ownable']
  90. time_until_expiration = float(request.json['time_until_expiration'])
  91. if time_until_expiration < 0:
  92. return bad_request('Invalid expiration time.')
  93. ownable_id = model.ownable_id_by_name(ownable_name)
  94. user_id = model.get_user_id_by_session_id(session_id)
  95. model.own(user_id, ownable_name)
  96. ownership_id = model.get_ownership_id(ownable_id, user_id)
  97. try:
  98. if request.json['limit'] == '':
  99. limit = None
  100. elif request.json['limit'] is None:
  101. limit = None
  102. else:
  103. if buy:
  104. limit = floor(float(request.json['limit']) * 10000) / 10000
  105. else:
  106. limit = ceil(float(request.json['limit']) * 10000) / 10000
  107. except ValueError: # for example when float fails
  108. return bad_request('Invalid limit.')
  109. except KeyError: # for example when limit was not specified
  110. limit = None
  111. if limit < 0:
  112. return bad_request('Limit must not be negative.')
  113. try:
  114. if request.json['stop_loss'] == '':
  115. stop_loss = None
  116. elif request.json['stop_loss'] is None:
  117. stop_loss = None
  118. else:
  119. stop_loss = 'stop_loss' in request.json and request.json['stop_loss']
  120. if stop_loss is not None and limit is None:
  121. return bad_request('Can only set stop-loss for limit orders')
  122. except KeyError: # for example when stop_loss was not specified
  123. stop_loss = None
  124. if sell:
  125. if not model.user_has_at_least_available(amount, user_id, ownable_id):
  126. return bad_request('You can not sell more than you own.')
  127. try:
  128. expiry = datetime.strptime(model.current_db_time(), '%Y-%m-%d %H:%M:%S') + \
  129. timedelta(minutes=time_until_expiration)
  130. except OverflowError:
  131. return bad_request('The expiration time is too far in the future.')
  132. model.place_order(buy, ownership_id, limit, stop_loss, amount, expiry)
  133. return {'message': "Order placed."}
  134. def gift():
  135. missing = missing_attributes(['session_id', 'amount', 'object_name', 'username'])
  136. if missing:
  137. return bad_request(missing)
  138. if not model.ownable_name_exists(request.json['object_name']):
  139. return bad_request('This kind of object can not be given away.')
  140. if request.json['username'] == 'bank' or not model.user_exists(request.json['username']):
  141. return bad_request('There is no user with this name.')
  142. try:
  143. amount = float(request.json['amount'])
  144. except ValueError:
  145. return bad_request('Invalid amount.')
  146. ownable_id = model.ownable_id_by_name(request.json['object_name'])
  147. sender_id = model.get_user_id_by_session_id(request.json['session_id'])
  148. if model.available_amount(sender_id, ownable_id) == 0:
  149. return bad_request('You do not own any of these.')
  150. if not model.user_has_at_least_available(amount, sender_id, ownable_id):
  151. # for example if you have a 1.23532143213 Kollar and want to give them all away
  152. amount = model.available_amount(sender_id, ownable_id)
  153. recipient_id = model.get_user_id_by_name(request.json['username'])
  154. model.send_ownable(sender_id,
  155. recipient_id,
  156. ownable_id,
  157. amount)
  158. return {'message': "Gift sent."}
  159. def orders():
  160. missing = missing_attributes(['session_id'])
  161. if missing:
  162. return bad_request(missing)
  163. data = model.get_user_orders(model.get_user_id_by_session_id(request.json['session_id']))
  164. return {'data': data}
  165. def orders_on():
  166. missing = missing_attributes(['session_id', 'ownable'])
  167. if missing:
  168. return bad_request(missing)
  169. if not model.ownable_name_exists(request.json['ownable']):
  170. return bad_request('This kind of object can not be ordered.')
  171. user_id = model.get_user_id_by_session_id(request.json['session_id'])
  172. ownable_id = model.ownable_id_by_name(request.json['ownable'])
  173. data = model.get_ownable_orders(user_id, ownable_id)
  174. return {'data': data}
  175. def old_orders():
  176. missing = missing_attributes(['session_id', 'include_canceled', 'include_executed', 'limit'])
  177. if missing:
  178. return bad_request(missing)
  179. include_executed = request.json['include_executed']
  180. include_canceled = request.json['include_canceled']
  181. user_id = model.get_user_id_by_session_id(request.json['session_id'])
  182. limit = request.json['limit']
  183. data = model.get_old_orders(user_id, include_executed, include_canceled, limit)
  184. return {'data': data}
  185. def cancel_order():
  186. missing = missing_attributes(['session_id', 'order_id'])
  187. if missing:
  188. return bad_request(missing)
  189. if not model.user_has_order_with_id(request.json['session_id'], request.json['order_id']):
  190. return bad_request('You do not have an order with that number.')
  191. model.delete_order(request.json['order_id'], 'Canceled')
  192. return {'message': "Successfully deleted order"}
  193. def change_password():
  194. missing = missing_attributes(['session_id', 'password'])
  195. if missing:
  196. return bad_request(missing)
  197. hashed_password = sha256_crypt.encrypt(request.json['password'] + salt)
  198. model.change_password(request.json['session_id'], hashed_password)
  199. model.sign_out_user(request.json['session_id'])
  200. return {'message': "Successfully changed password"}
  201. def news():
  202. return {'data': model.news()}
  203. def tradables():
  204. return {'data': model.ownables()}
  205. def trades():
  206. missing = missing_attributes(['session_id', 'limit'])
  207. if missing:
  208. return bad_request(missing)
  209. return {'data': model.trades(model.get_user_id_by_session_id(request.json['session_id']), request.json['limit'])}
  210. def trades_on():
  211. missing = missing_attributes(['session_id', 'ownable', 'limit'])
  212. if missing:
  213. return bad_request(missing)
  214. if not model.ownable_name_exists(request.json['ownable']):
  215. return bad_request('This kind of object can not have transactions.')
  216. return {'data': model.trades_on(model.ownable_id_by_name(request.json['ownable']), request.json['limit'])}
  217. def leaderboard():
  218. return {'data': model.leaderboard()}
  219. def not_found(msg=''):
  220. response.status = 404
  221. if debug:
  222. msg = str(response.status) + ': ' + msg
  223. response.content_type = 'application/json'
  224. return json.dumps({"error_message": msg})
  225. def forbidden(msg=''):
  226. response.status = 403
  227. if debug:
  228. msg = str(response.status) + ': ' + msg
  229. response.content_type = 'application/json'
  230. return json.dumps({"error_message": msg})
  231. def bad_request(msg=''):
  232. response.status = 400
  233. if debug:
  234. msg = str(response.status) + ': ' + msg
  235. response.content_type = 'application/json'
  236. return json.dumps({"error_message": msg})
  237. def internal_server_error(msg=''):
  238. response.status = 500
  239. if debug:
  240. msg = str(response.status) + ': ' + msg
  241. response.content_type = 'application/json'
  242. return json.dumps({"error_message": msg})