From 2773d02ee689f259dce8e1613d413c2e1c9022c3 Mon Sep 17 00:00:00 2001 From: lucasheld Date: Thu, 30 Mar 2023 11:47:47 +0200 Subject: [PATCH 01/12] feat: add support for uptime kuma 1.21.1 --- README.md | 2 +- run_tests.sh | 2 +- uptime_kuma_api/docstrings.py | 14 ++++++++++++-- uptime_kuma_api/notification_providers.py | 21 +++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ad7f6ca..0cee7af 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package was developed to configure Uptime Kuma with Ansible. The Ansible co Python version 3.6+ is required. -Supported Uptime Kuma versions: 1.17.0 - 1.21.0 +Supported Uptime Kuma versions: 1.17.0 - 1.21.1 Installation --- diff --git a/run_tests.sh b/run_tests.sh index 66753ae..2a573bb 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -5,7 +5,7 @@ if [ $version ] then versions=("$version") else - versions=(1.21.0 1.20.2 1.19.6 1.18.5 1.17.1) + versions=(1.21.1 1.20.2 1.19.6 1.18.5 1.17.1) fi for version in ${versions[*]} diff --git a/uptime_kuma_api/docstrings.py b/uptime_kuma_api/docstrings.py index b885281..8d4d1bf 100644 --- a/uptime_kuma_api/docstrings.py +++ b/uptime_kuma_api/docstrings.py @@ -109,8 +109,8 @@ def notification_docstring(mode) -> str: :param str, optional lineChannelAccessToken: Notification option for ``type`` :attr:`~.NotificationType.LINE` :param str, optional lineUserID: Notification option for ``type`` :attr:`~.NotificationType.LINE` :param str, optional lunaseaTarget: Notification option for ``type`` :attr:`~.NotificationType.LUNASEA`. Allowed values: "device", "user". - :param str, optional lunaseaUserID: Notification option for ``type`` :attr:`~.NotificationType.LUNASEA` - :param str, optional lunaseaDevice: Notification option for ``type`` :attr:`~.NotificationType.LUNASEA` + :param str, optional lunaseaUserID: Notification option for ``type`` :attr:`~.NotificationType.LUNASEA`. User ID. + :param str, optional lunaseaDevice: Notification option for ``type`` :attr:`~.NotificationType.LUNASEA`. Device ID. :param str, optional internalRoomId: Notification option for ``type`` :attr:`~.NotificationType.MATRIX` :param str, optional accessToken: Notification option for ``type`` :attr:`~.NotificationType.MATRIX` :param str, optional homeserverUrl: Notification option for ``type`` :attr:`~.NotificationType.MATRIX` @@ -275,6 +275,16 @@ def notification_docstring(mode) -> str: - ``WARNING`` - ``CRITICAL`` :param str, optional splunkRestURL: Notification option for ``type`` :attr:`~.NotificationType.SPLUNK`. Splunk Rest URL. + :param int, optional opsgeniePriority: Notification option for ``type`` :attr:`~.NotificationType.OPSGENIE`. Priority. Available values are numbers between ``1`` and ``5``. + :param str, optional opsgenieRegion: Notification option for ``type`` :attr:`~.NotificationType.OPSGENIE`. Region. Available values are: + + - ``us``: US (Default) + - ``eu``: EU + :param str, optional opsgenieApiKey: Notification option for ``type`` :attr:`~.NotificationType.OPSGENIE`. API Key. + :param str, optional twilioAccountSID: Notification option for ``type`` :attr:`~.NotificationType.TWILIO`. Account SID. + :param str, optional twilioAuthToken: Notification option for ``type`` :attr:`~.NotificationType.TWILIO`. Auth Token. + :param str, optional twilioToNumber: Notification option for ``type`` :attr:`~.NotificationType.TWILIO`. To Number. + :param str, optional twilioFromNumber: Notification option for ``type`` :attr:`~.NotificationType.TWILIO`. From Number. """ diff --git a/uptime_kuma_api/notification_providers.py b/uptime_kuma_api/notification_providers.py index 5c7b3a6..ab23fb9 100644 --- a/uptime_kuma_api/notification_providers.py +++ b/uptime_kuma_api/notification_providers.py @@ -148,6 +148,12 @@ class NotificationType(str, Enum): SPLUNK = "Splunk" """Splunk""" + OPSGENIE = "Opsgenie" + """Opsgenie""" + + TWILIO = "twilio" + """twilio""" + notification_provider_options = { NotificationType.ALERTA: dict( @@ -411,6 +417,17 @@ notification_provider_options = { splunkAutoResolve=dict(type="str"), splunkSeverity=dict(type="str"), splunkRestURL=dict(type="str") + ), + NotificationType.OPSGENIE: dict( + opsgeniePriority=dict(type="int"), + opsgenieRegion=dict(type="str"), + opsgenieApiKey=dict(type="str") + ), + NotificationType.TWILIO: dict( + twilioAccountSID=dict(type="str"), + twilioAuthToken=dict(type="str"), + twilioToNumber=dict(type="str"), + twilioFromNumber=dict(type="str") ) } @@ -430,5 +447,9 @@ notification_provider_conditions = dict( smseaglePriority=dict( min=0, max=9 + ), + opsgeniePriority=dict( + min=1, + max=5 ) ) From cc3588cf4db6d0742c2ca61613997de6b1166db6 Mon Sep 17 00:00:00 2001 From: lucasheld Date: Thu, 30 Mar 2023 11:50:02 +0200 Subject: [PATCH 02/12] bump version to 0.12.0 --- CHANGELOG.md | 5 +++++ uptime_kuma_api/__version__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de7478f..30ae0e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Changelog +### Release 0.12.0 + +#### Feature +- add support for uptime kuma 1.21.1 + ### Release 0.11.0 #### Feature diff --git a/uptime_kuma_api/__version__.py b/uptime_kuma_api/__version__.py index 7ff5287..81636d9 100644 --- a/uptime_kuma_api/__version__.py +++ b/uptime_kuma_api/__version__.py @@ -1,5 +1,5 @@ __title__ = "uptime_kuma_api" -__version__ = "0.11.0" +__version__ = "0.12.0" __author__ = "Lucas Held" __license__ = "MIT" __copyright__ = "Copyright 2023 Lucas Held" From e1fd3b7f03a7e80e9bf234749604c35ec7218203 Mon Sep 17 00:00:00 2001 From: lucasheld Date: Thu, 30 Mar 2023 12:19:27 +0200 Subject: [PATCH 03/12] feat: implement custom socketio headers --- uptime_kuma_api/api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index e13ac01..0a5aadf 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -399,15 +399,18 @@ class UptimeKumaApi(object): :param str url: The url to the Uptime Kuma instance. For example ``http://127.0.0.1:3001`` :param float wait_timeout: How many seconds the client should wait for the connection., defaults to 1 + :param dict headers: Headers that are passed to the socketio connection, defaults to None :raises UptimeKumaException: When connection to server failed. """ def __init__( self, url: str, - wait_timeout: float = 1 + wait_timeout: float = 1, + headers: dict = None ) -> None: self.url = url self.wait_timeout = wait_timeout + self.headers = headers self.sio = socketio.Client() self._event_data: dict = { @@ -583,7 +586,7 @@ class UptimeKumaApi(object): """ url = self.url.rstrip("/") try: - self.sio.connect(f'{url}/socket.io/', wait_timeout=self.wait_timeout) + self.sio.connect(f'{url}/socket.io/', wait_timeout=self.wait_timeout, headers=self.headers) except: raise UptimeKumaException("unable to connect") From 307b25249c13188c10b120bda56f0687b0d9bf4f Mon Sep 17 00:00:00 2001 From: lucasheld Date: Fri, 7 Apr 2023 20:03:30 +0200 Subject: [PATCH 04/12] test: delete maintenances and api keys in setUp --- tests/uptime_kuma_test_case.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/uptime_kuma_test_case.py b/tests/uptime_kuma_test_case.py index a43e027..430ec05 100644 --- a/tests/uptime_kuma_test_case.py +++ b/tests/uptime_kuma_test_case.py @@ -80,6 +80,18 @@ class UptimeKumaTestCase(unittest.TestCase): for docker_host in docker_hosts: self.api.delete_docker_host(docker_host["id"]) + if parse_version(self.api.version) >= parse_version("1.19"): + # delete maintenances + maintenances = self.api.get_maintenances() + for maintenance in maintenances: + self.api.delete_maintenance(maintenance["id"]) + + if parse_version(self.api.version) >= parse_version("1.21"): + # delete api keys + api_keys = self.api.get_api_keys() + for api_key in api_keys: + self.api.delete_api_key(api_key["id"]) + # login again to receive initial messages self.api.disconnect() self.api = UptimeKumaApi(self.url) From be537a14d2190c790574cf0bf90f583a6c4fec2e Mon Sep 17 00:00:00 2001 From: lucasheld Date: Fri, 7 Apr 2023 20:07:52 +0200 Subject: [PATCH 05/12] fix: do not wait for events that have already arrived --- uptime_kuma_api/api.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index e13ac01..9d76d0d 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -451,8 +451,9 @@ class UptimeKumaApi(object): @contextmanager def wait_for_event(self, event: Event) -> None: + # 200 * 0.05 seconds = 10 seconds retries = 200 - event_data_before = deepcopy(self._event_data) + sleep = 0.05 try: yield @@ -460,11 +461,11 @@ class UptimeKumaApi(object): raise else: counter = 0 - while event_data_before[event] == self._event_data[event]: - time.sleep(0.01) + while self._event_data[event] is None: + time.sleep(sleep) counter += 1 if counter >= retries: - print("wait_for_event timeout") + print(f"wait_for_event {event} timeout") break def _get_event_data(self, event) -> Any: From d7f033030e066b7f28060e513ed12c0bd6fcb664 Mon Sep 17 00:00:00 2001 From: lucasheld Date: Fri, 7 Apr 2023 21:03:33 +0200 Subject: [PATCH 06/12] feat: add support for uptime kuma 1.21.2 --- README.md | 2 +- run_tests.sh | 2 +- tests/test_maintenance.py | 62 +++---- uptime_kuma_api/api.py | 217 +++++++++++++----------- uptime_kuma_api/docstrings.py | 5 +- uptime_kuma_api/maintenance_strategy.py | 3 + 6 files changed, 152 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 0cee7af..84f57c8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package was developed to configure Uptime Kuma with Ansible. The Ansible co Python version 3.6+ is required. -Supported Uptime Kuma versions: 1.17.0 - 1.21.1 +Supported Uptime Kuma versions: 1.17.0 - 1.21.2 Installation --- diff --git a/run_tests.sh b/run_tests.sh index 2a573bb..af7ff36 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -5,7 +5,7 @@ if [ $version ] then versions=("$version") else - versions=(1.21.1 1.20.2 1.19.6 1.18.5 1.17.1) + versions=(1.21.2 1.21.1 1.20.2 1.19.6 1.18.5 1.17.1) fi for version in ${versions[*]} diff --git a/tests/test_maintenance.py b/tests/test_maintenance.py index a298443..e5cf40e 100644 --- a/tests/test_maintenance.py +++ b/tests/test_maintenance.py @@ -24,20 +24,15 @@ class TestMaintenance(UptimeKumaTestCase): "2022-12-27 22:36:00", "2022-12-29 22:36:00" ], - "timeRange": [ - { - "hours": 2, - "minutes": 0 - }, - { - "hours": 3, - "minutes": 0 - } - ], "weekdays": [], "daysOfMonth": [] } + if parse_version(self.api.version) >= parse_version("1.21.2"): + expected_maintenance.update({ + "timezone": "Europe/Berlin" + }) + # add maintenance r = self.api.add_maintenance(**expected_maintenance) self.assertEqual(r["msg"], "Added Successfully.") @@ -119,18 +114,6 @@ class TestMaintenance(UptimeKumaTestCase): "dateRange": [ "2022-12-27 00:00:00" ], - "timeRange": [ - { - "hours": 2, - "minutes": 0, - "seconds": 0 - }, - { - "hours": 3, - "minutes": 0, - "seconds": 0 - } - ], "weekdays": [], "daysOfMonth": [] } @@ -147,16 +130,6 @@ class TestMaintenance(UptimeKumaTestCase): "2022-12-27 22:36:00", "2022-12-29 22:36:00" ], - "timeRange": [ - { - "hours": 2, - "minutes": 0 - }, - { - "hours": 3, - "minutes": 0 - } - ], "weekdays": [], "daysOfMonth": [] } @@ -246,12 +219,33 @@ class TestMaintenance(UptimeKumaTestCase): 10, 20, 30, - "lastDay4", - "lastDay2" + "lastDay1" ] } self.do_test_maintenance_strategy(expected_maintenance) + def test_maintenance_strategy_cron(self): + if parse_version(self.api.version) < parse_version("1.21.2"): + self.skipTest("Unsupported in this Uptime Kuma version") + + expected_maintenance = { + "title": "test", + "description": "test", + "strategy": MaintenanceStrategy.CRON, + "active": True, + "intervalDay": 1, + "dateRange": [ + "2022-12-27 22:37:00", + "2022-12-31 22:37:00" + ], + "weekdays": [], + "daysOfMonth": [], + "cron": "50 5 * * *", + "durationMinutes": 120, + "timezone": "Europe/Berlin" + } + self.do_test_maintenance_strategy(expected_maintenance) + def do_test_maintenance_strategy(self, expected_maintenance): # add maintenance r = self.api.add_maintenance(**expected_maintenance) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index 9d76d0d..93f2d5c 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -184,49 +184,6 @@ def _build_docker_host_data( return data -def _build_maintenance_data( - title: str, - strategy: MaintenanceStrategy, - active: bool = True, - description: str = "", - dateRange: list = None, - intervalDay: int = 1, - weekdays: list = None, - daysOfMonth: list = None, - timeRange: list = None -) -> dict: - if not dateRange: - dateRange = [ - datetime.date.today().strftime("%Y-%m-%d 00:00:00") - ] - if not timeRange: - timeRange = [ - { - "hours": 2, - "minutes": 0, - }, { - "hours": 3, - "minutes": 0, - } - ] - if not weekdays: - weekdays = [] - if not daysOfMonth: - daysOfMonth = [] - data = { - "title": title, - "active": active, - "intervalDay": intervalDay, - "dateRange": dateRange, - "description": description, - "strategy": strategy, - "weekdays": weekdays, - "daysOfMonth": daysOfMonth, - "timeRange": timeRange - } - return data - - def _build_tag_data( name: str, color: str @@ -611,7 +568,7 @@ class UptimeKumaApi(object): interval: int = 60, retryInterval: int = 60, resendInterval: int = 0, - maxretries: int = 0, + maxretries: int = 1, upsideDown: bool = False, notificationIDList: list = None, httpBodyEncoding: str = "json", @@ -686,6 +643,10 @@ class UptimeKumaApi(object): # GAMEDIG game: str = None ) -> dict: + # https://github.com/louislam/uptime-kuma/compare/1.21.1...1.21.2#diff-f672603317047f3e6f27b0d7a44f6f244b7dbb5d0d0a85f1059a6b0bc2cb9aa0L910 + if parse_version(self.version) < parse_version("1.21.2"): + maxretries = 0 + data = { "type": type, "name": name, @@ -828,6 +789,58 @@ class UptimeKumaApi(object): return data + def _build_maintenance_data( + self, + title: str, + strategy: MaintenanceStrategy, + active: bool = True, + description: str = "", + dateRange: list = None, + intervalDay: int = 1, + weekdays: list = None, + daysOfMonth: list = None, + timeRange: list = None, + cron: str = "30 3 * * *", + durationMinutes: int = 60, + timezone: str = None + ) -> dict: + if not dateRange: + dateRange = [ + datetime.date.today().strftime("%Y-%m-%d 00:00:00") + ] + if not timeRange: + timeRange = [ + { + "hours": 2, + "minutes": 0, + }, { + "hours": 3, + "minutes": 0, + } + ] + if not weekdays: + weekdays = [] + if not daysOfMonth: + daysOfMonth = [] + data = { + "title": title, + "active": active, + "intervalDay": intervalDay, + "dateRange": dateRange, + "description": description, + "strategy": strategy, + "weekdays": weekdays, + "daysOfMonth": daysOfMonth, + "timeRange": timeRange + } + if parse_version(self.version) >= parse_version("1.21.2"): + data.update({ + "cron": cron, + "durationMinutes": durationMinutes, + "timezone": timezone, + }) + return data + # monitor def get_monitors(self) -> list: @@ -875,7 +888,7 @@ class UptimeKumaApi(object): 'keyword': None, 'maintenance': False, 'maxredirects': 10, - 'maxretries': 0, + 'maxretries': 1, 'method': 'GET', 'mqttPassword': None, 'mqttSuccessMessage': None, @@ -957,7 +970,7 @@ class UptimeKumaApi(object): 'keyword': None, 'maintenance': False, 'maxredirects': 10, - 'maxretries': 0, + 'maxretries': 1, 'method': 'GET', 'mqttPassword': None, 'mqttSuccessMessage': None, @@ -2838,29 +2851,27 @@ class UptimeKumaApi(object): ], "timeRange": [ { - "hours": 2, - "minutes": 0, - "seconds": 0 + "hours": 0, + "minutes": 0 }, { - "hours": 3, - "minutes": 0, - "seconds": 0 + "hours": 0, + "minutes": 0 } ], "weekdays": [], "daysOfMonth": [], "timeslotList": [ { - "id": 1, - "startDate": "2022-12-27 14:39:00", - "endDate": "2022-12-30 14:39:00", - "startDateServerTimezone": "2022-12-27 15:39", - "endDateServerTimezone": "2022-12-30 15:39", - "serverTimezoneOffset": "+01:00" + "startDate": "2022-12-27 22:36:00", + "endDate": "2022-12-29 22:36:00" } ], - "status": "under-maintenance" + "cron": "", + "durationMinutes": null, + "timezone": "Europe/Berlin", + "timezoneOffset": "+02:00", + "status": "ended" } ] """ @@ -2891,29 +2902,28 @@ class UptimeKumaApi(object): ], "timeRange": [ { - "hours": 2, - "minutes": 0, - "seconds": 0 + "hours": 0, + "minutes": 0 }, { - "hours": 3, - "minutes": 0, - "seconds": 0 + "hours": 0, + "minutes": 0 } ], "weekdays": [], "daysOfMonth": [], "timeslotList": [ { - "id": 1, - "startDate": "2022-12-27 14:39:00", - "endDate": "2022-12-30 14:39:00", - "startDateServerTimezone": "2022-12-27 15:39", - "endDateServerTimezone": "2022-12-30 15:39", - "serverTimezoneOffset": "+01:00" + "startDate": "2022-12-27 22:36:00", + "endDate": "2022-12-29 22:36:00" } ], - "status": "under-maintenance" + "cron": null, + "duration": null, + "durationMinutes": 0, + "timezone": "Europe/Berlin", + "timezoneOffset": "+02:00", + "status": "ended" } """ return self._call('getMaintenance', id_)["maintenance"] @@ -2938,18 +2948,6 @@ class UptimeKumaApi(object): ... dateRange=[ ... "2022-12-27 00:00:00" ... ], - ... timeRange=[ - ... { - ... "hours": 2, - ... "minutes": 0, - ... "seconds": 0 - ... }, - ... { - ... "hours": 3, - ... "minutes": 0, - ... "seconds": 0 - ... } - ... ], ... weekdays=[], ... daysOfMonth=[] ... ) @@ -2970,20 +2968,9 @@ class UptimeKumaApi(object): ... "2022-12-27 22:36:00", ... "2022-12-29 22:36:00" ... ], - ... timeRange=[ - ... { - ... "hours": 2, - ... "minutes": 0, - ... "seconds": 0 - ... }, - ... { - ... "hours": 3, - ... "minutes": 0, - ... "seconds": 0 - ... } - ... ], ... weekdays=[], - ... daysOfMonth=[] + ... daysOfMonth=[], + ... timezone="Europe/Berlin" ... ) { "msg": "Added Successfully.", @@ -3015,7 +3002,8 @@ class UptimeKumaApi(object): ... } ... ], ... weekdays=[], - ... daysOfMonth=[] + ... daysOfMonth=[], + ... timezone="Europe/Berlin" ... ) { "msg": "Added Successfully.", @@ -3052,7 +3040,8 @@ class UptimeKumaApi(object): ... 5, ... 0 ... ], - ... daysOfMonth=[] + ... daysOfMonth=[], + ... timezone="Europe/Berlin" ... ) { "msg": "Added Successfully.", @@ -3089,15 +3078,39 @@ class UptimeKumaApi(object): ... 10, ... 20, ... 30, - ... "lastDay2" - ... ] + ... "lastDay1" + ... ], + ... timezone="Europe/Berlin" + ... ) + { + "msg": "Added Successfully.", + "maintenanceID": 1 + } + + Example (strategy: :attr:`~.MaintenanceStrategy.CRON`):: + + >>> api.add_maintenance( + ... title="test", + ... description="test", + ... strategy=MaintenanceStrategy.CRON, + ... active=True, + ... intervalDay=1, + ... dateRange=[ + ... "2022-12-27 22:39:00", + ... "2022-12-31 22:39:00" + ... ], + ... weekdays=[], + ... daysOfMonth=[], + ... cron="50 5 * * *", + ... durationMinutes=120, + ... timezone="Europe/Berlin" ... ) { "msg": "Added Successfully.", "maintenanceID": 1 } """ - data = _build_maintenance_data(**kwargs) + data = self._build_maintenance_data(**kwargs) _check_arguments_maintenance(data) return self._call('addMaintenance', data) diff --git a/uptime_kuma_api/docstrings.py b/uptime_kuma_api/docstrings.py index 8d4d1bf..f320735 100644 --- a/uptime_kuma_api/docstrings.py +++ b/uptime_kuma_api/docstrings.py @@ -319,8 +319,11 @@ def maintenance_docstring(mode) -> str: :param list, optional dateRange: DateTime Range, defaults to ``[""]`` :param int, optional intervalDay: Interval (Run once every day), defaults to ``1`` :param list, optional weekdays: List that contains the days of the week on which the maintenance is enabled (Sun = ``0``, Mon = ``1``, ..., Sat = ``6``). Required for ``strategy`` :attr:`~.MaintenanceStrategy.RECURRING_WEEKDAY`., defaults to ``[]``. - :param list, optional daysOfMonth: List that contains the days of the month on which the maintenance is enabled (Day 1 = ``1``, Day 2 = ``2``, ..., Day 31 = ``31``) and the last day of the month (Last Day of Month = ``"lastDay1"``, 2nd Last Day of Month = ``"lastDay2"``, 3rd Last Day of Month = ``"lastDay3"``, 4th Last Day of Month = ``"lastDay4"``). Required for ``strategy`` :attr:`~.MaintenanceStrategy.RECURRING_DAY_OF_MONTH`., defaults to ``[]``. + :param list, optional daysOfMonth: List that contains the days of the month on which the maintenance is enabled (Day 1 = ``1``, Day 2 = ``2``, ..., Day 31 = ``31``) and the last day of the month (``"lastDay1"``). Required for ``strategy`` :attr:`~.MaintenanceStrategy.RECURRING_DAY_OF_MONTH`., defaults to ``[]``. :param list, optional timeRange: Maintenance Time Window of a Day, defaults to ``[{{"hours": 2, "minutes": 0}}, {{"hours": 3, "minutes": 0}}]``. + :param str, optional cron: Cron Schedule. Required for ``strategy`` :attr:`~.MaintenanceStrategy.CRON`., defaults to ``"30 3 * * *"`` + :param int, optional durationMinutes: Duration (Minutes). Required for ``strategy`` :attr:`~.MaintenanceStrategy.CRON`., defaults to ``60`` + :param str, optional timezone: Timezone, defaults to ``None`` (Server Timezone) """ diff --git a/uptime_kuma_api/maintenance_strategy.py b/uptime_kuma_api/maintenance_strategy.py index 45af153..fe484c4 100644 --- a/uptime_kuma_api/maintenance_strategy.py +++ b/uptime_kuma_api/maintenance_strategy.py @@ -18,3 +18,6 @@ class MaintenanceStrategy(str, Enum): RECURRING_DAY_OF_MONTH = "recurring-day-of-month" """Recurring - Day of Month""" + + CRON = "cron" + """Cron Expression""" From e176b6b61303ea847fa2dab4453d2dc83af6ee43 Mon Sep 17 00:00:00 2001 From: lucasheld Date: Fri, 7 Apr 2023 21:14:13 +0200 Subject: [PATCH 07/12] bump version to 0.13.0 --- CHANGELOG.md | 9 +++++++++ uptime_kuma_api/__version__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30ae0e0..763c65c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Changelog +### Release 0.13.0 + +#### Feature +- add support for uptime kuma 1.21.2 +- implement custom socketio headers + +#### Bugfix +- do not wait for events that have already arrived + ### Release 0.12.0 #### Feature diff --git a/uptime_kuma_api/__version__.py b/uptime_kuma_api/__version__.py index 81636d9..7148fcc 100644 --- a/uptime_kuma_api/__version__.py +++ b/uptime_kuma_api/__version__.py @@ -1,5 +1,5 @@ __title__ = "uptime_kuma_api" -__version__ = "0.12.0" +__version__ = "0.13.0" __author__ = "Lucas Held" __license__ = "MIT" __copyright__ = "Copyright 2023 Lucas Held" From 341d3205499bf624ada591ccf22bd50fe5ef55b1 Mon Sep 17 00:00:00 2001 From: Vinalti Date: Fri, 28 Apr 2023 00:07:01 +0200 Subject: [PATCH 08/12] Add ssl_verify param --- uptime_kuma_api/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index ac7011a..8152da2 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -357,18 +357,22 @@ class UptimeKumaApi(object): :param str url: The url to the Uptime Kuma instance. For example ``http://127.0.0.1:3001`` :param float wait_timeout: How many seconds the client should wait for the connection., defaults to 1 :param dict headers: Headers that are passed to the socketio connection, defaults to None + :param bool ssl_verify: ``True`` to verify SSL certificates, or ``False`` to skip SSL certificate + verification, allowing connections to servers with self signed certificates. + Default is ``True``. :raises UptimeKumaException: When connection to server failed. """ def __init__( self, url: str, wait_timeout: float = 1, - headers: dict = None + headers: dict = None, + ssl_verify: bool = True ) -> None: self.url = url self.wait_timeout = wait_timeout self.headers = headers - self.sio = socketio.Client() + self.sio = socketio.Client(ssl_verify=ssl_verify) self._event_data: dict = { Event.MONITOR_LIST: None, From 391e5a3077e0db3071405fb0d8f08376378b9d0a Mon Sep 17 00:00:00 2001 From: lucasheld Date: Sun, 30 Apr 2023 16:48:33 +0200 Subject: [PATCH 09/12] feat: add param `wait_events` --- uptime_kuma_api/api.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index 8152da2..9a8b3f2 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -360,6 +360,10 @@ class UptimeKumaApi(object): :param bool ssl_verify: ``True`` to verify SSL certificates, or ``False`` to skip SSL certificate verification, allowing connections to servers with self signed certificates. Default is ``True``. + :param float wait_events: How many seconds the client should wait for the next event of the same type. + There is no way to determine when the last message of a certain type has arrived. + Therefore, a timeout is required. If no further message has arrived within this time, + it is assumed that it was the last message. Defaults is ``0.2``. :raises UptimeKumaException: When connection to server failed. """ def __init__( @@ -367,11 +371,13 @@ class UptimeKumaApi(object): url: str, wait_timeout: float = 1, headers: dict = None, - ssl_verify: bool = True + ssl_verify: bool = True, + wait_events: float = 0.2 ) -> None: self.url = url self.wait_timeout = wait_timeout self.headers = headers + self.wait_events = wait_events self.sio = socketio.Client(ssl_verify=ssl_verify) self._event_data: dict = { @@ -439,7 +445,7 @@ class UptimeKumaApi(object): if self._event_data[Event.MONITOR_LIST] == {} and event in monitor_events: return [] time.sleep(0.01) - time.sleep(0.05) # wait for multiple messages + time.sleep(self.wait_events) # wait for multiple messages return deepcopy(self._event_data[event]) def _call(self, event, data=None) -> Any: From e42f6461c09c30e6cdceff10cf197fccdd82a6db Mon Sep 17 00:00:00 2001 From: lucasheld Date: Mon, 1 May 2023 18:40:14 +0200 Subject: [PATCH 10/12] feat: implement context manager for UptimeKumaApi class --- uptime_kuma_api/api.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index 9a8b3f2..273fdf7 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -337,6 +337,9 @@ class UptimeKumaApi(object): >>> from uptime_kuma_api import UptimeKumaApi >>> api = UptimeKumaApi('INSERT_URL') >>> api.login('INSERT_USERNAME', 'INSERT_PASSWORD') + { + 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjgyOTU4OTU4fQ.Xb81nuKXeNyE1D_XoQowYgsgZHka-edONdwHmIznJdk' + } Now you can call one of the existing methods of the instance. For example create a new monitor: @@ -354,6 +357,20 @@ class UptimeKumaApi(object): >>> api.disconnect() + With a context manager, the disconnect method is called automatically: + + .. code-block:: python + + from uptime_kuma_api import UptimeKumaApi + + with UptimeKumaApi('INSERT_URL') as api: + api.login('INSERT_USERNAME', 'INSERT_PASSWORD') + api.add_monitor( + type=MonitorType.HTTP, + name="Google", + url="https://google.com" + ) + :param str url: The url to the Uptime Kuma instance. For example ``http://127.0.0.1:3001`` :param float wait_timeout: How many seconds the client should wait for the connection., defaults to 1 :param dict headers: Headers that are passed to the socketio connection, defaults to None @@ -419,6 +436,12 @@ class UptimeKumaApi(object): self.connect() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.disconnect() + @contextmanager def wait_for_event(self, event: Event) -> None: # 200 * 0.05 seconds = 10 seconds From 19bd8aecfaaafa9202ce6105afae0e725e0e9f1b Mon Sep 17 00:00:00 2001 From: Vinalti Date: Mon, 1 May 2023 18:57:55 +0200 Subject: [PATCH 11/12] Clean up code and implement best practices (#27) * clean up code and implement best practices: - `type(a) == list` replace with `isinstance(a, list)` - `adict['key']` replaced with `adict.get('key')` - annotation `-> list` replace by more accurate `-> list[dict]` * improve compatibility with previous python versions with Typing * little fix --- uptime_kuma_api/api.py | 83 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index 273fdf7..36fe904 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import datetime import json import random @@ -11,21 +13,28 @@ import requests import socketio from packaging.version import parse as parse_version -from . import AuthMethod -from . import DockerType -from . import Event -from . import IncidentStyle -from . import MaintenanceStrategy -from . import MonitorType -from . import NotificationType, notification_provider_options, notification_provider_conditions -from . import ProxyProtocol -from . import UptimeKumaException -from .docstrings import append_docstring, monitor_docstring, notification_docstring, proxy_docstring, \ - docker_host_docstring, maintenance_docstring, tag_docstring +from . import (AuthMethod, + DockerType, + Event, + IncidentStyle, + MaintenanceStrategy, + MonitorType, + NotificationType, + ProxyProtocol, + UptimeKumaException, + notification_provider_conditions, + notification_provider_options) +from .docstrings import (append_docstring, + docker_host_docstring, + maintenance_docstring, + monitor_docstring, + notification_docstring, + proxy_docstring, + tag_docstring) def int_to_bool(data, keys) -> None: - if type(data) == list: + if isinstance(data, list): for d in data: int_to_bool(d, keys) else: @@ -40,7 +49,7 @@ def gen_secret(length: int) -> str: def _convert_monitor_return(monitor) -> None: - if type(monitor["notificationIDList"]) == dict: + if isinstance(monitor["notificationIDList"], dict): monitor["notificationIDList"] = [int(i) for i in monitor["notificationIDList"].keys()] @@ -138,7 +147,7 @@ def _build_status_page_data( icon: str = "/icon.svg", publicGroupList: list = None -) -> (str, dict, str, list): +) -> tuple(str, dict, str, list): if theme not in ["light", "dark"]: raise ValueError if not domainNameList: @@ -473,9 +482,9 @@ class UptimeKumaApi(object): def _call(self, event, data=None) -> Any: r = self.sio.call(event, data) - if type(r) == dict and "ok" in r: + if isinstance(r, dict) and "ok" in r: if not r["ok"]: - raise UptimeKumaException(r["msg"]) + raise UptimeKumaException(r.get("msg")) r.pop("ok") return r @@ -594,7 +603,7 @@ class UptimeKumaApi(object): @property def version(self) -> str: info = self.info() - return info["version"] + return info.get("version") def _build_monitor_data( self, @@ -879,7 +888,7 @@ class UptimeKumaApi(object): # monitor - def get_monitors(self) -> list: + def get_monitors(self) -> list[dict]: """ Get all monitors. @@ -1092,7 +1101,7 @@ class UptimeKumaApi(object): with self.wait_for_event(Event.MONITOR_LIST): return self._call('deleteMonitor', id_) - def get_monitor_beats(self, id_: int, hours: int) -> list: + def get_monitor_beats(self, id_: int, hours: int) -> list[dict]: """ Get monitor beats for a specific monitor in a time range. @@ -1135,7 +1144,7 @@ class UptimeKumaApi(object): int_to_bool(r, ["important", "status"]) return r - def get_game_list(self) -> list: + def get_game_list(self) -> list[dict]: """ Get a list of games that are supported by the GameDig monitor type. @@ -1176,7 +1185,7 @@ class UptimeKumaApi(object): # Exists in 1.20.0 - 1.21.0 if not r: r = self._call('getGameList') - return r["gameList"] + return r.get("gameList") @append_docstring(monitor_docstring("add")) def add_monitor(self, **kwargs) -> dict: @@ -1294,7 +1303,7 @@ class UptimeKumaApi(object): # notification - def get_notifications(self) -> list: + def get_notifications(self) -> list[dict]: """ Get all notifications. @@ -1489,7 +1498,7 @@ class UptimeKumaApi(object): # proxy - def get_proxies(self) -> list: + def get_proxies(self) -> list[dict]: """ Get all proxies. @@ -1547,7 +1556,7 @@ class UptimeKumaApi(object): """ proxies = self.get_proxies() for proxy in proxies: - if proxy["id"] == id_: + if proxy.get("id") == id_: return proxy raise UptimeKumaException("proxy does not exist") @@ -1633,7 +1642,7 @@ class UptimeKumaApi(object): # status page - def get_status_pages(self) -> list: + def get_status_pages(self) -> list[dict]: """ Get all status pages. @@ -1914,7 +1923,7 @@ class UptimeKumaApi(object): # heartbeat - def get_heartbeats(self) -> list: + def get_heartbeats(self) -> list[dict]: """ Get heartbeats. @@ -1961,7 +1970,7 @@ class UptimeKumaApi(object): int_to_bool(i["data"], ["important", "status"]) return r - def get_important_heartbeats(self) -> list: + def get_important_heartbeats(self) -> list[dict]: """ Get important heartbeats. @@ -1994,7 +2003,7 @@ class UptimeKumaApi(object): int_to_bool(i["data"], ["important", "status"]) return r - def get_heartbeat(self) -> list: + def get_heartbeat(self) -> list[dict]: """ Get heartbeat. @@ -2021,7 +2030,7 @@ class UptimeKumaApi(object): # avg ping - def avg_ping(self) -> list: + def avg_ping(self) -> list[dict]: """ Get average ping. @@ -2042,7 +2051,7 @@ class UptimeKumaApi(object): # cert info - def cert_info(self) -> list: + def cert_info(self) -> list[dict]: """ Get certificate info. @@ -2063,7 +2072,7 @@ class UptimeKumaApi(object): # uptime - def uptime(self) -> list: + def uptime(self) -> list[dict]: """ Get monitor uptime. @@ -2162,7 +2171,7 @@ class UptimeKumaApi(object): # tags - def get_tags(self) -> list: + def get_tags(self) -> list[dict]: """ Get all tags. @@ -2717,7 +2726,7 @@ class UptimeKumaApi(object): # docker host - def get_docker_hosts(self) -> list: + def get_docker_hosts(self) -> list[dict]: """ Get all docker hosts. @@ -2862,7 +2871,7 @@ class UptimeKumaApi(object): # maintenance - def get_maintenances(self) -> list: + def get_maintenances(self) -> list[dict]: """ Get all maintenances. @@ -3251,7 +3260,7 @@ class UptimeKumaApi(object): """ return self._call('resumeMaintenance', id_) - def get_monitor_maintenance(self, id_: int) -> list: + def get_monitor_maintenance(self, id_: int) -> list[dict]: """ Gets all monitors of a maintenance. @@ -3309,7 +3318,7 @@ class UptimeKumaApi(object): """ return self._call('addMonitorMaintenance', (id_, monitors)) - def get_status_page_maintenance(self, id_: int) -> list: + def get_status_page_maintenance(self, id_: int) -> list[dict]: """ Gets all status pages of a maintenance. @@ -3365,7 +3374,7 @@ class UptimeKumaApi(object): # api key - def get_api_keys(self) -> list: + def get_api_keys(self) -> list[dict]: """ Get all api keys. From 50ff8f1219f155671c440f2182935d10d34ae04c Mon Sep 17 00:00:00 2001 From: lucasheld Date: Mon, 1 May 2023 19:06:28 +0200 Subject: [PATCH 12/12] feat: drop python 3.6 support BREAKING CHANGE: Python 3.7+ required --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84f57c8..44328ff 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ uptime-kuma-api is a Python wrapper for the [Uptime Kuma](https://github.com/lou This package was developed to configure Uptime Kuma with Ansible. The Ansible collection can be found at https://github.com/lucasheld/ansible-uptime-kuma. -Python version 3.6+ is required. +Python version 3.7+ is required. Supported Uptime Kuma versions: 1.17.0 - 1.21.2