pythonkuma 0.0.0rc1__tar.gz → 0.0.0rc3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonkuma
3
- Version: 0.0.0rc1
3
+ Version: 0.0.0rc3
4
4
  Summary: Simple Python wrapper for Uptime Kuma
5
5
  Project-URL: Source, https://github.com/tr4nt0r/pythonkuma
6
6
  Author-email: Manfred Dennerlein Rodelo <manfred@dennerlein.name>, Jayakorn Karikan <jayakornk@gmail.com>
@@ -37,17 +37,16 @@ import aiohttp
37
37
 
38
38
  from pythonkuma import UptimeKuma
39
39
 
40
- URL = ""
41
- USERNAME = ""
42
- PASSWORD = ""
40
+ URL = "https://uptime.exampe.com"
41
+ API_KEY = "api_key"
43
42
 
44
43
 
45
44
  async def main():
46
45
 
47
46
  async with aiohttp.ClientSession() as session:
48
- uptime_kuma = UptimeKuma(session, URL, USERNAME, PASSWORD)
49
- response = await uptime_kuma.async_get_monitors()
50
- print(response.data)
47
+ uptime_kuma = UptimeKuma(session, URL, API_KEY)
48
+ response = await uptime_kuma.metrics()
49
+ print(response)
51
50
 
52
51
 
53
52
  asyncio.run(main())
@@ -16,17 +16,16 @@ import aiohttp
16
16
 
17
17
  from pythonkuma import UptimeKuma
18
18
 
19
- URL = ""
20
- USERNAME = ""
21
- PASSWORD = ""
19
+ URL = "https://uptime.exampe.com"
20
+ API_KEY = "api_key"
22
21
 
23
22
 
24
23
  async def main():
25
24
 
26
25
  async with aiohttp.ClientSession() as session:
27
- uptime_kuma = UptimeKuma(session, URL, USERNAME, PASSWORD)
28
- response = await uptime_kuma.async_get_monitors()
29
- print(response.data)
26
+ uptime_kuma = UptimeKuma(session, URL, API_KEY)
27
+ response = await uptime_kuma.metrics()
28
+ print(response)
30
29
 
31
30
 
32
31
  asyncio.run(main())
@@ -5,15 +5,15 @@ from .exceptions import (
5
5
  UptimeKumaConnectionException,
6
6
  UptimeKumaException,
7
7
  )
8
- from .models import MonitorType, UptimeKumaApiResponse, UptimeKumaMonitor
8
+ from .models import MonitorStatus, MonitorType, UptimeKumaMonitor
9
9
  from .uptimekuma import UptimeKuma
10
10
 
11
- __version__ = "0.0.0rc1"
11
+ __version__ = "0.0.0rc3"
12
12
 
13
13
  __all__ = [
14
+ "MonitorStatus",
14
15
  "MonitorType",
15
16
  "UptimeKuma",
16
- "UptimeKumaApiResponse",
17
17
  "UptimeKumaAuthenticationException",
18
18
  "UptimeKumaConnectionException",
19
19
  "UptimeKumaException",
@@ -0,0 +1,77 @@
1
+ """Uptime Kuma models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import IntEnum, StrEnum
7
+ from typing import Self
8
+
9
+ from mashumaro import DataClassDictMixin
10
+
11
+
12
+ class MonitorStatus(IntEnum):
13
+ """Monitor states."""
14
+
15
+ DOWN = 0
16
+ UP = 1
17
+ PENDING = 2
18
+ MAINTENANCE = 3
19
+
20
+
21
+ class MonitorType(StrEnum):
22
+ """Monitors type."""
23
+
24
+ HTTP = "http"
25
+ PORT = "port"
26
+ PING = "ping"
27
+ KEYWORD = "keyword"
28
+ DNS = "dns"
29
+ PUSH = "push"
30
+ STEAM = "steam"
31
+ MQTT = "mqtt"
32
+ SQL = "sqlserver"
33
+ JSON_QUERY = "json-query"
34
+ GROUP = "group"
35
+ DOCKER = "docker"
36
+ GRPC_KEYWORD = "grpc-keyword"
37
+ REAL_BROWSER = "real-browser"
38
+ GAMEDIG = "gamedig"
39
+ KAFKA_PRODUCER = "kafka-producer"
40
+ POSTGRES = "postgres"
41
+ MYSQL = "mysql"
42
+ MONGODB = "mongodb"
43
+ RADIUS = "radius"
44
+ REDIS = "redis"
45
+ TAILSCALE_PING = "tailscale-ping"
46
+ UNKNOWN = "unknown"
47
+
48
+ @classmethod
49
+ def _missing_(cls, _: object) -> Self:
50
+ """Handle new and unknown monitor types."""
51
+ return cls.UNKNOWN
52
+
53
+
54
+ @dataclass
55
+ class UptimeKumaBaseModel(DataClassDictMixin):
56
+ """UptimeKumaBaseModel."""
57
+
58
+
59
+ @dataclass(kw_only=True)
60
+ class UptimeKumaMonitor(UptimeKumaBaseModel):
61
+ """Monitor model for Uptime Kuma."""
62
+
63
+ monitor_cert_days_remaining: int
64
+ monitor_cert_is_valid: bool
65
+ monitor_hostname: str | None = field(
66
+ metadata={"deserialize": lambda v: None if v == "null" else v}
67
+ )
68
+ monitor_name: str
69
+ monitor_port: str | None = field(
70
+ metadata={"deserialize": lambda v: None if v == "null" else v}
71
+ )
72
+ monitor_response_time: int = 0
73
+ monitor_status: MonitorStatus
74
+ monitor_type: MonitorType = MonitorType.HTTP
75
+ monitor_url: str | None = field(
76
+ metadata={"deserialize": lambda v: None if v == "null" else v}
77
+ )
@@ -0,0 +1,76 @@
1
+ """Uptime Kuma client."""
2
+
3
+ from http import HTTPStatus
4
+ from typing import Any
5
+
6
+ from aiohttp import (
7
+ BasicAuth,
8
+ ClientError,
9
+ ClientResponseError,
10
+ ClientSession,
11
+ ClientTimeout,
12
+ )
13
+ from prometheus_client.parser import text_string_to_metric_families
14
+ from yarl import URL
15
+
16
+ from .exceptions import UptimeKumaAuthenticationException, UptimeKumaConnectionException
17
+ from .models import UptimeKumaMonitor
18
+
19
+
20
+ class UptimeKuma:
21
+ """Uptime Kuma client."""
22
+
23
+ def __init__(
24
+ self,
25
+ session: ClientSession,
26
+ base_url: URL | str,
27
+ api_key: str | None = None,
28
+ timeout: float | None = None,
29
+ ) -> None:
30
+ """Initialize the Uptime Kuma client."""
31
+ self._base_url = base_url if isinstance(base_url, URL) else URL(base_url)
32
+
33
+ self._auth = BasicAuth("", api_key) if api_key else None
34
+
35
+ self._timeout = ClientTimeout(total=timeout or 10)
36
+ self._session = session
37
+
38
+ async def metrics(self) -> list[UptimeKumaMonitor]:
39
+ """Retrieve metrics from Uptime Kuma."""
40
+ url = self._base_url / "metrics"
41
+
42
+ try:
43
+ request = await self._session.get(
44
+ url, auth=self._auth, timeout=self._timeout
45
+ )
46
+ request.raise_for_status()
47
+ except ClientResponseError as e:
48
+ if e.status is HTTPStatus.UNAUTHORIZED:
49
+ msg = "Authentication failed for %s"
50
+ raise UptimeKumaAuthenticationException(msg, str(url)) from e
51
+ msg = "Request for %s failed with status code %s"
52
+ raise UptimeKumaConnectionException(msg, str(url), e.status) from e
53
+ except TimeoutError as e:
54
+ msg = "Request timeout for %s"
55
+ raise UptimeKumaConnectionException(msg, str(url)) from e
56
+ except ClientError as e:
57
+ raise UptimeKumaConnectionException from e
58
+ else:
59
+ parsed = text_string_to_metric_families(await request.text())
60
+
61
+ monitors: dict[str, dict[str, Any]] = {}
62
+ for metric in parsed:
63
+ if not metric.name.startswith("monitor"):
64
+ continue
65
+ for sample in metric.samples:
66
+ if not (monitor_name := sample.labels.get("monitor_name")):
67
+ continue
68
+
69
+ monitors.setdefault(monitor_name, sample.labels).update(
70
+ {sample.name: sample.value}
71
+ )
72
+
73
+ return {
74
+ key: UptimeKumaMonitor.from_dict(value)
75
+ for key, value in monitors.items()
76
+ }
@@ -1,5 +0,0 @@
1
- """Uptime Kuma constants."""
2
-
3
- from logging import Logger, getLogger
4
-
5
- LOGGER: Logger = getLogger(__package__)
@@ -1,71 +0,0 @@
1
- """Decorator for Uptime Kuma"""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import TYPE_CHECKING
6
-
7
- import aiohttp
8
-
9
- from . import exceptions
10
- from .const import LOGGER
11
- from .models import UptimeKumaApiResponse
12
-
13
- if TYPE_CHECKING:
14
- from .uptimekuma import UptimeKuma
15
-
16
-
17
- def api_request(api_path: str, method: str = "GET"):
18
- """Decorator for Uptime Kuma API request"""
19
-
20
- def decorator(func):
21
- """Decorator"""
22
-
23
- async def wrapper(*args, **kwargs):
24
- """Wrapper"""
25
- client: UptimeKuma = args[0]
26
- url = client._base_url / api_path
27
- LOGGER.debug("Requesting %s", url.human_repr())
28
- try:
29
- request = await client._session.request(
30
- method=method,
31
- url=url,
32
- timeout=aiohttp.ClientTimeout(total=10),
33
- auth=aiohttp.BasicAuth(client._username, client._password),
34
- )
35
-
36
- if request.status != 200:
37
- raise exceptions.UptimeKumaConnectionException(
38
- f"Request for '{url}' failed with status code '{request.status}'"
39
- )
40
-
41
- result = await request.text()
42
- except aiohttp.ClientError as exception:
43
- raise exceptions.UptimeKumaConnectionException(
44
- f"Request exception for '{url}' with - {exception}"
45
- ) from exception
46
-
47
- except TimeoutError:
48
- raise exceptions.UptimeKumaConnectionException(f"Request timeout for '{url}'") from None
49
-
50
- except exceptions.UptimeKumaConnectionException as exception:
51
- raise exceptions.UptimeKumaConnectionException(exception) from exception
52
-
53
- except exceptions.UptimeKumaException as exception:
54
- raise exceptions.UptimeKumaException(exception) from exception
55
-
56
- except (Exception, BaseException) as exception:
57
- raise exceptions.UptimeKumaException(
58
- f"Unexpected exception for '{url}' with - {exception}"
59
- ) from exception
60
-
61
- LOGGER.debug("Requesting %s returned %s", url, result)
62
-
63
- response = UptimeKumaApiResponse.from_prometheus(
64
- {"monitors": result, "_api_path": api_path, "_method": method}
65
- )
66
-
67
- return response
68
-
69
- return wrapper
70
-
71
- return decorator
@@ -1,101 +0,0 @@
1
- """Uptime Kuma models"""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
- from enum import StrEnum
7
- from typing import Any
8
-
9
- from prometheus_client.parser import text_string_to_metric_families as parser
10
-
11
-
12
- class MonitorType(StrEnum):
13
- """Monitors type."""
14
-
15
- HTTP = "http"
16
- PORT = "port"
17
- PING = "ping"
18
- KEYWORD = "keyword"
19
- DNS = "dns"
20
- PUSH = "push"
21
- STEAM = "steam"
22
- MQTT = "mqtt"
23
- SQL = "sqlserver"
24
- JSON_QUERY = "json-query"
25
- GROUP = "group"
26
- DOCKER = "docker"
27
- GRPC_KEYWORD = "grpc-keyword"
28
- REAL_BROWSER = "real-browser"
29
- GAMEDIG = "gamedig"
30
- KAFKA_PRODUCER = "kafka-producer"
31
- POSTGRES = "postgres"
32
- MYSQL = "mysql"
33
- MONGODB = "mongodb"
34
- RADIUS = "radius"
35
- REDIS = "redis"
36
- TAILSCALE_PING = "tailscale-ping"
37
-
38
-
39
- class UptimeKumaBaseModel:
40
- """UptimeKumaBaseModel."""
41
-
42
-
43
- @dataclass
44
- class UptimeKumaMonitor(UptimeKumaBaseModel):
45
- """Monitor model for Uptime Kuma."""
46
-
47
- monitor_cert_days_remaining: float = 0
48
- monitor_cert_is_valid: float = 0
49
- monitor_hostname: str = ""
50
- monitor_name: str = ""
51
- monitor_port: str = ""
52
- monitor_response_time: float = 0
53
- monitor_status: float = 0
54
- monitor_type: MonitorType = MonitorType.HTTP
55
- monitor_url: str = ""
56
-
57
- @staticmethod
58
- def from_dict(data: dict[str, Any]) -> UptimeKumaMonitor:
59
- """Generate object from json."""
60
- obj: dict[str, Any] = {}
61
- for key, value in data.items():
62
- if hasattr(UptimeKumaMonitor, key):
63
- obj[key] = MonitorType(value) if key == "monitor_type" else value
64
-
65
- return UptimeKumaMonitor(**obj)
66
-
67
-
68
- @dataclass
69
- class UptimeKumaApiResponse(UptimeKumaBaseModel):
70
- """API response model for Uptime Kuma."""
71
-
72
- _method: str | None = None
73
- _api_path: str | None = None
74
- data: list[UptimeKumaMonitor] | None = None
75
-
76
- @staticmethod
77
- def from_prometheus(data: dict[str, Any]) -> UptimeKumaApiResponse:
78
- """Generate object from json."""
79
- obj: dict[str, Any] = {}
80
- monitors = []
81
-
82
- for key, value in data.items():
83
- if hasattr(UptimeKumaApiResponse, key):
84
- obj[key] = value
85
-
86
- parsed = parser(data["monitors"])
87
- for family in parsed:
88
- for sample in family.samples:
89
- if sample.name.startswith("monitor"):
90
- existed = next(
91
- (i for i, x in enumerate(monitors) if x["monitor_name"] == sample.labels["monitor_name"]),
92
- None,
93
- )
94
- if existed is None:
95
- temp = {**sample.labels, sample.name: sample.value}
96
- monitors.append(temp)
97
- else:
98
- monitors[existed][sample.name] = sample.value
99
- obj["data"] = [UptimeKumaMonitor.from_dict(monitor) for monitor in monitors]
100
-
101
- return UptimeKumaApiResponse(**obj)
@@ -1,23 +0,0 @@
1
- """Uptime Kuma client."""
2
-
3
- from aiohttp import ClientSession
4
- from yarl import URL
5
-
6
- from .decorator import api_request
7
- from .models import UptimeKumaApiResponse
8
-
9
-
10
- class UptimeKuma:
11
- """This class is used to get information from Uptime Kuma."""
12
-
13
- def __init__(self, session: ClientSession, base_url: URL | str, username: str, password: str) -> None:
14
- """Initialize"""
15
- self.monitors = []
16
- self._base_url = base_url if isinstance(base_url, URL) else URL(base_url)
17
- self._username = username
18
- self._password = password
19
- self._session: ClientSession = session
20
-
21
- @api_request("metrics")
22
- async def async_get_monitors(self, **kwargs) -> UptimeKumaApiResponse:
23
- """Get monitors from API."""
File without changes
File without changes