From 1a3b0ff660a9f7f6e5527c32eb779113f85f93c7 Mon Sep 17 00:00:00 2001 From: nex Date: Mon, 4 Mar 2024 12:39:45 +0000 Subject: [PATCH] Add UptimeKuma support to v2 --- setup.cfg | 3 +++ src/main.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..8547f5e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[flake8] +max-line-length=120 +ignore=W293,W291 \ No newline at end of file diff --git a/src/main.py b/src/main.py index 6053f19..82947c0 100644 --- a/src/main.py +++ b/src/main.py @@ -4,6 +4,10 @@ import logging import sys import traceback import typing +from threading import Thread, Event +import time +import random +import httpx import uvicorn from web import app @@ -14,6 +18,52 @@ from discord.ext import commands from rich.logging import RichHandler from conf import CONFIG + +class KillableThread(Thread): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.kill = Event() + + +class KumaThread(KillableThread): + def __init__(self, url: str, interval: float = 60.0): + super().__init__(target=self.run) + self.daemon = True + self.log = logging.getLogger("philip.status") + self.url = url + self.interval = interval + self.kill = Event() + self.retries = 0 + + def calculate_backoff(self) -> float: + rnd = random.uniform(0, 1) + retries = min(self.retries, 1000) + t = (2 * 2 ** retries) + rnd + self.log.debug("Backoff: 2 * (2 ** %d) + %f = %f", retries, rnd, t) + # T can never exceed self.interval + return max(0, min(self.interval, t)) + + def run(self) -> None: + with httpx.Client(http2=True) as client: + while not self.kill.is_set(): + start_time = time.time() + try: + self.retries += 1 + response = client.get(self.url) + response.raise_for_status() + except httpx.HTTPError as error: + self.log.error("Failed to connect to uptime-kuma: %r: %r", self.url, error, exc_info=error) + timeout = self.calculate_backoff() + self.log.warning("Waiting %d seconds before retrying ping.", timeout) + time.sleep(timeout) + continue + + self.retries = 0 + end_time = time.time() + timeout = self.interval - (end_time - start_time) + self.kill.wait(timeout) + + log = logging.getLogger("jimmy") CONFIG.setdefault("logging", {}) @@ -45,8 +95,15 @@ class Client(commands.Bot): def __init_(self, *args, **kwargs): super().__init__(*args, **kwargs) self.web: typing.Optional[asyncio.Task] = None + self.uptime_thread = None async def start(self, token: str, *, reconnect: bool = True) -> None: + if CONFIG["jimmy"].get("uptime_kuma_url"): + self.uptime_thread = KumaThread( + CONFIG["jimmy"]["uptime_kuma_url"], + CONFIG["jimmy"].get("uptime_kuma_interval", 60.0) + ) + self.uptime_thread.start() app.state.bot = self config = uvicorn.Config( app, @@ -63,6 +120,9 @@ class Client(commands.Bot): async def close(self) -> None: if self.web: self.web.cancel() + if self.thread: + self.thread.kill.set() + await asyncio.get_event_loop().run_in_executor(None, self.thread.join) await super().close()