1
0

healthcheck.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import requests
  2. import socket
  3. import threading
  4. import discord
  5. import math
  6. from http.server import BaseHTTPRequestHandler, HTTPServer
  7. from datetime import datetime
  8. from tcp_latency import measure_latency
  9. import myanimebot.globals as globals
  10. import myanimebot.utils as utils
  11. import myanimebot.anilist as anilist
  12. webtext = ""
  13. uptime = datetime.now().strftime("%H:%M:%S %d/%m/%Y")
  14. class MyServer(BaseHTTPRequestHandler):
  15. def do_GET(self):
  16. try:
  17. timestamp_request = datetime.now()
  18. webtext = "<html><head><title>MyAnimeBot Healthcheck status</title><link rel='icon' type='image/gif'' href='{}' /></head><body><h1>MyAnimeBot Healthcheck status</h1><table>".format(globals.iconBot)
  19. code = 200
  20. code, webtext = get_version(code, webtext)
  21. code, webtext = get_uptime(code, webtext)
  22. code, webtext = get_db_status(code, webtext)
  23. code, webtext = get_discord_websocket_status(code, webtext)
  24. code, webtext = get_anilist_status(code, webtext)
  25. code, webtext = get_myanimelist_status(code, webtext)
  26. generation_time = (datetime.now() - timestamp_request).total_seconds() * 1000
  27. webtext += "</table><p><em>Healthcheck generated in {}ms.</em></p></body></html>".format(round(generation_time))
  28. except:
  29. webtext = "<html><head><title>MyAnimeBot Healthcheck status</title></head><body><h1>MyAnimeBot Healthcheck status</h1><p>An unexpected error as occured when we tried to generate the healthcheck page, check the logs for more information.</p></body></html>"
  30. code = 503
  31. globals.logger.exception("Error on the healthcheck:\n")
  32. self.send_response(code)
  33. self.send_header("Content-type", "text/html")
  34. self.end_headers()
  35. self.wfile.write(bytes(webtext, "utf-8"))
  36. def line_formatter (desc : str, state : str, level : int):
  37. # Levels : 0 OK, 1 Error, 2 Warning, 3 Disabled
  38. if (level == 0):
  39. color = "7FFF00"
  40. elif (level == 1):
  41. color = "CD5C5C"
  42. elif (level == 2):
  43. color = "FFD700"
  44. else:
  45. color = "888888"
  46. result = "<tr><td>{}: </td><td bgcolor='{}' ><strong>{}</strong></td></tr>".format(desc, color, state)
  47. return result
  48. def ping(hostname : str):
  49. latencies = measure_latency(host=hostname, runs=1, wait=0)
  50. total = 0
  51. for value in latencies:
  52. total += value
  53. result = math.trunc(total/len(latencies))
  54. return result
  55. def get_anilist_status (code : int, webtext : str):
  56. if (globals.ANI_ENABLED):
  57. try:
  58. ani_status_code = requests.post(anilist.ANILIST_GRAPHQL_URL, timeout=3, allow_redirects=False).status_code
  59. if (ani_status_code == 400):
  60. ani_ping = ping("graphql.anilist.co")
  61. if (ani_ping < 300):
  62. webtext += line_formatter("AniList API status", "OK ({}ms)".format(ani_ping), 0)
  63. else:
  64. webtext += line_formatter("AniList API status", "SLOW ({}ms)".format(ani_ping), 2)
  65. else:
  66. webtext += line_formatter("AniList API status", "KO ({})".format(ani_status_code), 1)
  67. if (code == 200): code = 500
  68. except Exception as e:
  69. webtext += line_formatter("AniList API status", "KO ({})".format(e), 1)
  70. if (code == 200): code = 500
  71. else:
  72. webtext += line_formatter("AniList API status", "DISABLED", 3)
  73. return code, webtext
  74. def get_myanimelist_status (code : int, webtext : str):
  75. if (globals.MAL_ENABLED):
  76. try:
  77. mal_status_code = requests.head(globals.MAL_URL, timeout=3, allow_redirects=False).status_code
  78. if (mal_status_code == 200):
  79. mal_ping = ping("myanimelist.net")
  80. if (mal_ping < 300):
  81. webtext += line_formatter("MyAnimeList status", "OK ({}ms)".format(mal_ping), 0)
  82. else:
  83. webtext += line_formatter("MyAnimeList status", "SLOW ({}ms)".format(mal_ping), 2)
  84. else:
  85. webtext += line_formatter("MyAnimeList status", "KO ({})".format(mal_status_code), 1)
  86. if (code == 200): code = 500
  87. except Exception as e:
  88. webtext += line_formatter("MyAnimeList status", "KO ({})".format(e), 1)
  89. if (code == 200): code = 500
  90. else:
  91. webtext += line_formatter("MyAnimeList status", "DISABLED", 3)
  92. return code, webtext
  93. def get_uptime (code : int, webtext : str):
  94. webtext += line_formatter("Script uptime", uptime, 0)
  95. return code, webtext
  96. def get_version (code : int, webtext : str):
  97. webtext += line_formatter("Script version", globals.VERSION, 0)
  98. return code, webtext
  99. def get_discord_websocket_status (code : int, webtext : str):
  100. if (globals.client.is_closed()) or (not globals.client.is_ready()):
  101. webtext += line_formatter("Discord status", "KO", 1)
  102. if (code == 200): code = 500
  103. else:
  104. if (globals.client.is_ws_ratelimited()): webtext += line_formatter("Discord status", "NOT OK (Rate limited)", 2)
  105. else: webtext += line_formatter("Discord status", "OK (v{})".format(discord.__version__), 0)
  106. return code, webtext
  107. def get_db_status (code : int, webtext : str):
  108. try:
  109. cursor = globals.conn.cursor(buffered=False)
  110. cursor.execute("SELECT * FROM t_feeds LIMIT 1;")
  111. cursor.fetchone()
  112. cursor.close()
  113. cursor = globals.conn.cursor(buffered=True, dictionary=True)
  114. cursor.execute("SELECT @@VERSION AS ver;")
  115. data = cursor.fetchone()
  116. cursor.close()
  117. webtext += line_formatter("Database status", "OK ({})".format(data["ver"]), 0)
  118. except Exception as e:
  119. webtext += line_formatter("Database status", "KO", 1)
  120. globals.logger.error("The healthcheck cannot access to the database: {}".format(e))
  121. if (code == 200): code = 500
  122. return code, webtext
  123. def start_healthcheck(ip, port):
  124. webServer = HTTPServer((ip, port), MyServer)
  125. globals.logger.info("Healthcheck started on http://{}:{}".format(ip, port))
  126. try:
  127. webServer.serve_forever()
  128. except KeyboardInterrupt:
  129. pass
  130. except Exception as e:
  131. globals.logger.error("The healthcheck crashed: {}".format(e))
  132. async def main(asyncioloop):
  133. ''' Main function that starts the Healthcheck web page '''
  134. globals.logger.info("Starting up Healtcheck...")
  135. healthcheck_thread = threading.Thread(name='healthcheck', target=start_healthcheck, args=(globals.HEALTHCHECK_IP, globals.HEALTHCHECK_PORT))
  136. healthcheck_thread.setDaemon(True)
  137. healthcheck_thread.start()