mojang_status.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import requests
  2. from queue import Queue
  3. from datetime import datetime, timedelta
  4. from typing import TypedDict, Literal, TypeAlias
  5. from homepagebuilder.interfaces import script
  6. API_URL = "https://www.mcstate.net/api/mojang-status"
  7. QUEUE_MAX_SIZE = 3
  8. GREEN_COLOR = "#41D041"
  9. YELLOW_COLOR = "#D1A041"
  10. RED_COLOR = "#D14241"
  11. GRAY_COLOR = "#888888"
  12. USER_AGENT = (
  13. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
  14. "AppleWebKit/537.36 (KHTML, like Gecko) "
  15. "Chrome/139.0 Safari/537.36"
  16. )
  17. HEADERS = {
  18. "User-Agent": USER_AGENT,
  19. "Accept": (
  20. "application/json"
  21. ),
  22. }
  23. StatusState: TypeAlias = Literal['up', 'down', 'unknown']
  24. class StatusDetials(TypedDict):
  25. session: StatusState = "unknown"
  26. accounts: StatusState = "unknown"
  27. microsoft: StatusState = "unknown"
  28. api: StatusState = "unknown"
  29. textures: StatusState = "unknown"
  30. website: StatusState = "unknown"
  31. class StatusRecord(TypedDict):
  32. status: StatusDetials
  33. check_time: datetime = datetime.min
  34. CHECK_TIME: datetime = datetime.now()
  35. UPDATE_INTERVAL = 150
  36. UPDATE_TIME = datetime.min
  37. STATUS_RECORDS: Queue[StatusRecord | None] = Queue(maxsize=QUEUE_MAX_SIZE)
  38. CALCULATED_STATUS_CACHE: StatusRecord = None
  39. def get_status():
  40. try:
  41. response = requests.get(API_URL, headers=HEADERS, timeout=10)
  42. response.raise_for_status() # Raise an error for bad responses
  43. return response.json() # Return the JSON response as a dictionary
  44. except requests.RequestException as e:
  45. print(f"Error fetching Mojang status: {e}")
  46. return None
  47. def get_service_status(status_dict, service_id):
  48. return next((service['status'] for service in status_dict['services'] if service['id'] == service_id), 'unknown')
  49. def update_status():
  50. global UPDATE_TIME, CALCULATED_STATUS_CACHE
  51. result = get_status()
  52. status: StatusRecord = {'status': StatusDetials()}
  53. if result is not None:
  54. status['status']['session'] = get_service_status(result, 'session')
  55. status['status']['accounts'] = get_service_status(result, 'accounts')
  56. status['status']['microsoft'] = get_service_status(result, 'microsoft')
  57. status['status']['api'] = get_service_status(result, 'api')
  58. status['status']['textures'] = get_service_status(result, 'textures')
  59. status['status']['website'] = get_service_status(result, 'website')
  60. status['check_time'] = datetime.strptime(result.get('checkedAt'), "%Y-%m-%dT%H:%M:%S.%fZ") + timedelta(hours=8)
  61. else:
  62. status['check_time'] = datetime.now()
  63. if STATUS_RECORDS.full():
  64. STATUS_RECORDS.get()
  65. STATUS_RECORDS.put(status)
  66. UPDATE_TIME = datetime.now()
  67. CALCULATED_STATUS_CACHE = None
  68. def should_update_status():
  69. global UPDATE_TIME
  70. return (datetime.now() - UPDATE_TIME).total_seconds() >= UPDATE_INTERVAL
  71. def get_calculated_status():
  72. global CALCULATED_STATUS_CACHE, STATUS_RECORDS
  73. if CALCULATED_STATUS_CACHE is not None:
  74. return CALCULATED_STATUS_CACHE
  75. status: StatusRecord = StatusRecord()
  76. status['status'] = StatusDetials()
  77. for item in STATUS_RECORDS.queue:
  78. if item is None:
  79. continue
  80. for key, value in item['status'].items():
  81. if key == 'check_time':
  82. continue
  83. if value == 'down' and status.get(key) != 'up':
  84. status['status'][key] = 'down'
  85. elif value == 'up':
  86. status['status'][key] = 'up'
  87. status['check_time'] = max((item.get('check_time', datetime.min) for item in STATUS_RECORDS.queue if item is not None), default=datetime.min)
  88. return status
  89. def get_mojang_major_status():
  90. status = get_calculated_status()['status']
  91. if all(value == 'unknown' for value in status.values()):
  92. return "未知"
  93. if status['api'] == 'down' or status['session'] == 'down' or status['microsoft'] == 'down':
  94. return "异常"
  95. elif status['accounts'] == 'down' or status['textures'] == 'down' or status['website'] == 'down':
  96. return "问题"
  97. else:
  98. return "正常"
  99. @script('MojangStatusText')
  100. def mojang_status_text(name, **_):
  101. if should_update_status():
  102. update_status()
  103. status = get_calculated_status()['status'].get(name, 'unknown')
  104. if status == 'up':
  105. return "正常"
  106. elif status == 'down':
  107. return "异常"
  108. else:
  109. return "未知"
  110. @script('MojangMajorStatusText')
  111. def mojang_major_status_text(**_):
  112. if should_update_status():
  113. update_status()
  114. return get_mojang_major_status()
  115. @script('MojangStatusColor')
  116. def mojang_status_color(name, **_):
  117. if should_update_status():
  118. update_status()
  119. status = get_calculated_status()['status'].get(name, 'unknown')
  120. if status == 'up':
  121. return GREEN_COLOR
  122. elif status == 'down':
  123. return RED_COLOR
  124. else:
  125. return GRAY_COLOR
  126. @script('MojangMajorStatusColor')
  127. def mojang_major_status_color(**_):
  128. if should_update_status():
  129. update_status()
  130. major_status = get_mojang_major_status()
  131. if major_status == "正常":
  132. return GREEN_COLOR
  133. elif major_status == "异常":
  134. return RED_COLOR
  135. elif major_status == "问题":
  136. return YELLOW_COLOR
  137. else:
  138. return GRAY_COLOR
  139. @script('MojangStatusTime')
  140. def mojang_status_time(**_):
  141. return get_calculated_status()['check_time'].strftime("%Y年%m月%d日 %H:%M")
  142. @script('MojangStatusTimeCompressed')
  143. def mojang_status_time(**_):
  144. return get_calculated_status()['check_time'].strftime("%H:%M")
  145. @script('MojangStatusCardReference')
  146. def mojang_status(**_):
  147. status = get_mojang_major_status()
  148. if status == "异常":
  149. return "MojangStatusDetail"
  150. else:
  151. return "MojangStatusCompress"