server_controller.py 12 KB

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