__init__.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import datetime
  2. import json
  3. import os
  4. import ssl
  5. import urllib.parse
  6. import urllib.request
  7. """
  8. official api documentation:
  9. https://github.com/ToontownRewritten/api-doc/blob/master/login.md
  10. https://github.com/ToontownRewritten/api-doc/blob/master/invasions.md
  11. """
  12. INVASIONS_API_URL = 'https://www.toontownrewritten.com/api/invasions?format=json'
  13. LOGIN_API_URL = 'https://www.toontownrewritten.com/api/login?format=json'
  14. def _utc_from_timestamp(timestamp):
  15. dt = datetime.datetime.utcfromtimestamp(timestamp)
  16. return dt.replace(tzinfo = datetime.timezone.utc)
  17. def api_request(url, params=None, validate_ssl_cert=True):
  18. resp = urllib.request.urlopen(
  19. url=url,
  20. data=urllib.parse.urlencode(params).encode('ascii')
  21. if params else None,
  22. context=None if validate_ssl_cert
  23. else ssl._create_unverified_context(),
  24. )
  25. return json.loads(resp.read().decode('ascii'))
  26. class LoginSuccessful:
  27. def __init__(self, playcookie, gameserver):
  28. self.playcookie = playcookie
  29. self.gameserver = gameserver
  30. class LoginDelayed:
  31. def __init__(self, queue_token):
  32. self.queue_token = queue_token
  33. def login(username=None, password=None,
  34. queue_token=None, validate_ssl_cert=True):
  35. if username is not None and queue_token is None:
  36. assert password is not None
  37. req_params = {
  38. 'username': username,
  39. 'password': password,
  40. }
  41. elif username is None and queue_token is not None:
  42. req_params = {
  43. 'queueToken': queue_token,
  44. }
  45. else:
  46. raise Exception('either specify username or queue token')
  47. resp_data = api_request(
  48. url=LOGIN_API_URL,
  49. params=req_params,
  50. validate_ssl_cert=validate_ssl_cert,
  51. )
  52. if resp_data['success'] == 'true':
  53. return LoginSuccessful(
  54. playcookie=resp_data['cookie'],
  55. gameserver=resp_data['gameserver'],
  56. )
  57. elif resp_data['success'] == 'delayed':
  58. return LoginDelayed(
  59. queue_token=resp_data['queueToken'],
  60. )
  61. else:
  62. raise Exception(repr(resp_data))
  63. class InvasionProgress:
  64. def __init__(self, district, date, cog_type,
  65. despawned_number, total_number):
  66. self.district = district
  67. self.date = date
  68. self.cog_type = cog_type
  69. self.despawned_number = despawned_number
  70. self.total_number = total_number
  71. @property
  72. def remaining_number(self):
  73. return self.total_number - self.despawned_number
  74. class InvasionsResponse:
  75. def __init__(self, update_date, invasions):
  76. self.update_date = update_date
  77. self.invasions = invasions
  78. def request_active_invasions(validate_ssl_certs=True):
  79. resp_data = api_request(INVASIONS_API_URL)
  80. if resp_data['error'] is not None:
  81. raise Exception(resp_data['error'])
  82. else:
  83. invs = {}
  84. for district, inv_data in resp_data['invasions'].items():
  85. despawned_number, total_number = inv_data['progress'].split('/')
  86. invs[district] = InvasionProgress(
  87. district=district,
  88. date=_utc_from_timestamp(inv_data['asOf']),
  89. cog_type=inv_data['type'],
  90. despawned_number=int(despawned_number),
  91. total_number=int(total_number),
  92. )
  93. return InvasionsResponse(
  94. update_date=_utc_from_timestamp(resp_data['lastUpdated']),
  95. invasions=invs,
  96. )