server_controller.py 12 KB

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