python-ntfy 0.2.5__tar.gz → 0.3.6__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.
- {python_ntfy-0.2.5 → python_ntfy-0.3.6}/PKG-INFO +9 -5
- {python_ntfy-0.2.5 → python_ntfy-0.3.6}/README.md +8 -4
- python_ntfy-0.3.6/pyproject.toml +74 -0
- python_ntfy-0.3.6/python_ntfy/__init__.py +90 -0
- python_ntfy-0.3.6/python_ntfy/_get_functions.py +46 -0
- {python_ntfy-0.2.5 → python_ntfy-0.3.6}/python_ntfy/_send_functions.py +83 -47
- python_ntfy-0.2.5/pyproject.toml +0 -35
- python_ntfy-0.2.5/python_ntfy/__init__.py +0 -61
- python_ntfy-0.2.5/python_ntfy/_get_functions.py +0 -35
- {python_ntfy-0.2.5 → python_ntfy-0.3.6}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-ntfy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: An ntfy library aiming for feature completeness
|
|
5
5
|
Home-page: https://github.com/MatthewCane/python-ntfy
|
|
6
6
|
License: MIT
|
|
@@ -20,9 +20,13 @@ Project-URL: Documentation, https://matthewcane.github.io/python-ntfy/
|
|
|
20
20
|
Project-URL: Repository, https://github.com/MatthewCane/python-ntfy
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
|
|
23
|
-
# A Python Library For ntfy
|
|
23
|
+
# A Python Library For ntfy
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+

|
|
26
|
+

|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
An easy-to-use python library for the [ntfy notification service](https://ntfy.sh/). Aiming for full feature support and a super easy to use interface.
|
|
26
30
|
|
|
27
31
|
## Quickstart
|
|
28
32
|
|
|
@@ -49,6 +53,7 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
|
|
|
49
53
|
## Supported Features
|
|
50
54
|
|
|
51
55
|
- Username + password auth
|
|
56
|
+
- Access token auth
|
|
52
57
|
- Custom servers
|
|
53
58
|
- Sending plaintext messages
|
|
54
59
|
- Sending Markdown formatted text messages
|
|
@@ -59,8 +64,7 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
|
|
|
59
64
|
|
|
60
65
|
## Future Features
|
|
61
66
|
|
|
62
|
-
-
|
|
63
|
-
- Email notifications
|
|
67
|
+
- [Email notifications](https://docs.ntfy.sh/publish/#e-mail-notifications)
|
|
64
68
|
- Send to multiple topics at once
|
|
65
69
|
|
|
66
70
|
## Testing and Development
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
# A Python Library For ntfy
|
|
1
|
+
# A Python Library For ntfy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
An easy-to-use python library for the [ntfy notification service](https://ntfy.sh/). Aiming for full feature support and a super easy to use interface.
|
|
4
8
|
|
|
5
9
|
## Quickstart
|
|
6
10
|
|
|
@@ -27,6 +31,7 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
|
|
|
27
31
|
## Supported Features
|
|
28
32
|
|
|
29
33
|
- Username + password auth
|
|
34
|
+
- Access token auth
|
|
30
35
|
- Custom servers
|
|
31
36
|
- Sending plaintext messages
|
|
32
37
|
- Sending Markdown formatted text messages
|
|
@@ -37,8 +42,7 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
|
|
|
37
42
|
|
|
38
43
|
## Future Features
|
|
39
44
|
|
|
40
|
-
-
|
|
41
|
-
- Email notifications
|
|
45
|
+
- [Email notifications](https://docs.ntfy.sh/publish/#e-mail-notifications)
|
|
42
46
|
- Send to multiple topics at once
|
|
43
47
|
|
|
44
48
|
## Testing and Development
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "python-ntfy"
|
|
3
|
+
version = "0.3.6"
|
|
4
|
+
description = "An ntfy library aiming for feature completeness"
|
|
5
|
+
authors = ["Matthew Cane <matthew.cane0@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
repository = "https://github.com/MatthewCane/python-ntfy"
|
|
9
|
+
documentation = "https://matthewcane.github.io/python-ntfy/"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[tool.poetry.dependencies]
|
|
16
|
+
python = "^3.11"
|
|
17
|
+
requests = "^2.31.0"
|
|
18
|
+
mypy = "^1.12.0"
|
|
19
|
+
|
|
20
|
+
[tool.poetry.group.dev.dependencies]
|
|
21
|
+
pytest = ">=7.4.1,<9.0.0"
|
|
22
|
+
python-dotenv = "^1.0.0"
|
|
23
|
+
pytest-asyncio = ">=0.21.1,<0.25.0"
|
|
24
|
+
pytest-codecov = ">=0.5.1,<0.7.0"
|
|
25
|
+
ruff = "^0.7.0"
|
|
26
|
+
mkdocs-material = "^9.5.41"
|
|
27
|
+
mkdocstrings-python = "^1.12.1"
|
|
28
|
+
types-pygments = "^2.18.0.20240506"
|
|
29
|
+
types-colorama = "^0.4.15.20240311"
|
|
30
|
+
types-requests = "^2.32.0.20241016"
|
|
31
|
+
types-setuptools = "^75.2.0.20241018"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["poetry-core"]
|
|
35
|
+
build-backend = "poetry.core.masonry.api"
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = [
|
|
39
|
+
"E", # PycodeStyle errors
|
|
40
|
+
"W", # PycodeStyle warnings
|
|
41
|
+
"F", # PyFlakes
|
|
42
|
+
"I", # Isort
|
|
43
|
+
"N", # pep8-naming
|
|
44
|
+
"D", # Pydocstyle
|
|
45
|
+
"UP", # PyUpgrade
|
|
46
|
+
"ANN", # Flake8-annotations
|
|
47
|
+
"S", # Flake8-bandit
|
|
48
|
+
"BLE", # Flake8-blind-except
|
|
49
|
+
"B", # Flake8-bugbear
|
|
50
|
+
"A", # Flake8-builtins
|
|
51
|
+
"C4", # Flake8-comprehensions
|
|
52
|
+
"T10", # Flake8-debugger
|
|
53
|
+
"EM", # Flake8-errmessages
|
|
54
|
+
"Q", # Flake8-quotes
|
|
55
|
+
"RET", # Flake8-return
|
|
56
|
+
"TRY", # Triceratops
|
|
57
|
+
"FURB", # Refurb
|
|
58
|
+
"RUF", # Ruff
|
|
59
|
+
"PERF", # Perflint
|
|
60
|
+
]
|
|
61
|
+
ignore = [
|
|
62
|
+
"E501", # Line too long
|
|
63
|
+
"D103", # Missing docstring in public function
|
|
64
|
+
"D417", # Undocumented parameters
|
|
65
|
+
"D104", # First line of docstring should be in imperative moof
|
|
66
|
+
"D100", # Missing docstring in public module
|
|
67
|
+
"ANN001", # Missing type annotation for function argument
|
|
68
|
+
"ANN101", # Missing type annotation for self in method
|
|
69
|
+
"S101", # Use of assert detected
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.pydocstyle]
|
|
74
|
+
convention = "google"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""This module provides the NtfyClient class for interacting with the ntfy notification service.
|
|
2
|
+
|
|
3
|
+
The NtfyClient class allows users to send notifications, files, and perform various actions
|
|
4
|
+
through the ntfy.sh service. It also supports retrieving cached messages.
|
|
5
|
+
|
|
6
|
+
Typical usage example:
|
|
7
|
+
|
|
8
|
+
client = NtfyClient(topic="my_topic")
|
|
9
|
+
client.send("Hello, World!")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NtfyClient:
|
|
16
|
+
"""A class for interacting with the ntfy notification service."""
|
|
17
|
+
|
|
18
|
+
# The functions need to be imported here to:
|
|
19
|
+
# 1. Keep the functions in a separate file
|
|
20
|
+
# 2. Keep the docstrings working in the IDE
|
|
21
|
+
# 3. Allow the functions to be called with self
|
|
22
|
+
# MyPy does not like this, but it works
|
|
23
|
+
from ._get_functions import get_cached_messages # type: ignore
|
|
24
|
+
from ._send_functions import ( # type: ignore
|
|
25
|
+
BroadcastAction,
|
|
26
|
+
HttpAction,
|
|
27
|
+
MessagePriority,
|
|
28
|
+
ViewAction,
|
|
29
|
+
send,
|
|
30
|
+
send_file,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
topic: str,
|
|
36
|
+
server: str = "https://ntfy.sh",
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Itinialize the NtfyClient.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
topic: The topic to use for this client
|
|
42
|
+
server: The server to connect to. Must include the protocol (http/https)
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
None
|
|
46
|
+
|
|
47
|
+
Exceptions:
|
|
48
|
+
ToDo
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
client = NtfyClient(topic="my_topic")
|
|
52
|
+
"""
|
|
53
|
+
self._server = os.environ.get("NTFY_SERVER") or server
|
|
54
|
+
self._topic = topic
|
|
55
|
+
self.__set_url(self._server, topic)
|
|
56
|
+
|
|
57
|
+
# If the user has set the user and password, use that
|
|
58
|
+
# If the user has set the token, use that
|
|
59
|
+
# Otherwise, use an empty user and token
|
|
60
|
+
if (user := os.environ.get("NTFY_USER")) and (
|
|
61
|
+
password := os.environ.get("NTFY_PASSWORD")
|
|
62
|
+
):
|
|
63
|
+
self._auth = (user, password)
|
|
64
|
+
elif token := os.environ.get("NTFY_TOKEN"):
|
|
65
|
+
self._auth = ("", token)
|
|
66
|
+
else:
|
|
67
|
+
self._auth = ("", "")
|
|
68
|
+
|
|
69
|
+
def __set_url(self, server, topic) -> None:
|
|
70
|
+
self.url = server.strip("/") + "/" + topic
|
|
71
|
+
|
|
72
|
+
def set_topic(self, topic: str) -> None:
|
|
73
|
+
"""Set a new topic for the client.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
topic: The topic to set for this client.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
None
|
|
80
|
+
"""
|
|
81
|
+
self._topic = topic
|
|
82
|
+
self.__set_url(self._server, self._topic)
|
|
83
|
+
|
|
84
|
+
def get_topic(self) -> str:
|
|
85
|
+
"""Get the current topic.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
str: The current topic.
|
|
89
|
+
"""
|
|
90
|
+
return self._topic
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_cached_messages(
|
|
7
|
+
self,
|
|
8
|
+
since: str = "all",
|
|
9
|
+
scheduled: bool = False,
|
|
10
|
+
timeout_seconds: int = 10,
|
|
11
|
+
) -> list[dict]:
|
|
12
|
+
"""Get cached messages from the server.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
since: The timestamp to start from. If set to "all", will return all messages.
|
|
16
|
+
scheduled: If true, will return scheduled messages.
|
|
17
|
+
timeout_seconds: The number of seconds to wait for the response.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A list of messages.
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
response = client.get(since="all")
|
|
24
|
+
response = client.get(since="all", scheduled=True)
|
|
25
|
+
response = client.get(since="2019-01-01")
|
|
26
|
+
response = client.get(since="2019-01-01", scheduled=True)
|
|
27
|
+
"""
|
|
28
|
+
params = {"poll": "1"}
|
|
29
|
+
if scheduled:
|
|
30
|
+
params.update({"scheduled": str(scheduled)})
|
|
31
|
+
if since:
|
|
32
|
+
params.update({"since": since})
|
|
33
|
+
|
|
34
|
+
response = [
|
|
35
|
+
json.loads(line)
|
|
36
|
+
for line in requests.get(
|
|
37
|
+
url=self.url + "/json",
|
|
38
|
+
params=params,
|
|
39
|
+
auth=self._auth,
|
|
40
|
+
timeout=timeout_seconds,
|
|
41
|
+
)
|
|
42
|
+
.text.strip()
|
|
43
|
+
.splitlines()
|
|
44
|
+
]
|
|
45
|
+
# Reverse the list so that the most recent notification is first
|
|
46
|
+
return sorted(response, key=lambda x: x["time"], reverse=True)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import requests
|
|
3
2
|
from enum import Enum
|
|
3
|
+
from pathlib import Path
|
|
4
4
|
from typing import Optional, Union
|
|
5
5
|
|
|
6
|
+
import requests
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class MessagePriority(Enum):
|
|
8
|
-
"""
|
|
9
|
-
Ntfy message priority levels.
|
|
10
|
-
"""
|
|
10
|
+
"""Ntfy message priority levels."""
|
|
11
11
|
|
|
12
12
|
MIN = "1"
|
|
13
13
|
LOW = "2"
|
|
@@ -18,9 +18,7 @@ class MessagePriority(Enum):
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ActionType(Enum):
|
|
21
|
-
"""
|
|
22
|
-
Action button types
|
|
23
|
-
"""
|
|
21
|
+
"""Action button types."""
|
|
24
22
|
|
|
25
23
|
VIEW = "view"
|
|
26
24
|
BROADCAST = "broadcast"
|
|
@@ -28,7 +26,7 @@ class ActionType(Enum):
|
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
class Action:
|
|
31
|
-
def __init__(self, label: str, url: str, clear: bool = False):
|
|
29
|
+
def __init__(self, label: str, url: str, clear: bool = False) -> None:
|
|
32
30
|
self.label = label
|
|
33
31
|
self.url = url
|
|
34
32
|
self.actions: list = []
|
|
@@ -36,7 +34,7 @@ class Action:
|
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
class ViewAction(Action):
|
|
39
|
-
def __init__(self, label: str, url: str, clear: bool = False):
|
|
37
|
+
def __init__(self, label: str, url: str, clear: bool = False) -> None:
|
|
40
38
|
self.action = ActionType.VIEW
|
|
41
39
|
super().__init__(label=label, url=url, clear=clear)
|
|
42
40
|
|
|
@@ -59,7 +57,7 @@ class BroadcastAction(Action):
|
|
|
59
57
|
intent: str = "io.heckel.ntfy.USER_ACTION",
|
|
60
58
|
extras: Optional[dict[str, str]] = None,
|
|
61
59
|
clear: bool = False,
|
|
62
|
-
):
|
|
60
|
+
) -> None:
|
|
63
61
|
self.action = ActionType.BROADCAST
|
|
64
62
|
self.intent = intent
|
|
65
63
|
self.extras = extras
|
|
@@ -94,7 +92,7 @@ class HttpAction(Action):
|
|
|
94
92
|
headers: Optional[dict[str, str]] = None,
|
|
95
93
|
body: Optional[str] = None,
|
|
96
94
|
clear: bool = False,
|
|
97
|
-
):
|
|
95
|
+
) -> None:
|
|
98
96
|
self.action = ActionType.HTTP
|
|
99
97
|
self.method = method
|
|
100
98
|
self.headers = headers
|
|
@@ -133,26 +131,42 @@ def send(
|
|
|
133
131
|
message: str,
|
|
134
132
|
title: Optional[str] = None,
|
|
135
133
|
priority: MessagePriority = MessagePriority.DEFAULT,
|
|
136
|
-
tags: list =
|
|
137
|
-
actions: list[Union[ViewAction, BroadcastAction, HttpAction]] =
|
|
134
|
+
tags: Optional[list] = None,
|
|
135
|
+
actions: Optional[list[Union[ViewAction, BroadcastAction, HttpAction]]] = None,
|
|
138
136
|
format_as_markdown: bool = False,
|
|
137
|
+
timeout_seconds: int = 5,
|
|
139
138
|
) -> dict:
|
|
139
|
+
"""Send a text-based message to the server.
|
|
140
|
+
|
|
141
|
+
Call this function to send a message to the server. The message will be sent
|
|
142
|
+
to the server and then broadcast to all clients subscribed to the topic.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
message: The message to send.
|
|
146
|
+
title: The title of the message.
|
|
147
|
+
priority: The priority of the message.
|
|
148
|
+
tags: A list of tags to attach to the message. Can be an emoji short code.
|
|
149
|
+
actions: A list of Actions objects to attach to the message.
|
|
150
|
+
format_as_markdown: If true, the message will be formatted as markdown.
|
|
151
|
+
additional_topics: A list of additional topics to send the message to.
|
|
152
|
+
timeout_seconds: The number of seconds to wait before timing out.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
dict: The response from the server.
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
ToDo
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
response = client.send(message="Example message")
|
|
162
|
+
response = client.send(message="Example message", title="Example title", priority=MessagePriority.HIGH, tags=["fire", "warning"])
|
|
163
|
+
response = client.send(message="*Example markdown*", format_as_markdown=True)
|
|
140
164
|
"""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
:param tags: A list of tags to attach to the message. Can be an emoji short code. Optional
|
|
147
|
-
:param format_as_markdown: If true, the message will be formatted as markdown. Optional
|
|
148
|
-
:param actions: A list of Actions objects to attach to the message. Optional
|
|
149
|
-
:return: The response from the server
|
|
150
|
-
|
|
151
|
-
:examples:
|
|
152
|
-
response = client.send(message="Example message")
|
|
153
|
-
response = client.send(message="Example message", title="Example title", priority=MessagePriority.HIGH, tags=["fire", "warning"])
|
|
154
|
-
response = client.send(message="*Example markdown*", format_as_markdown=True)
|
|
155
|
-
"""
|
|
165
|
+
if tags is None:
|
|
166
|
+
tags = []
|
|
167
|
+
if actions is None:
|
|
168
|
+
actions = []
|
|
169
|
+
|
|
156
170
|
headers = {
|
|
157
171
|
"Title": title,
|
|
158
172
|
"Priority": priority.value,
|
|
@@ -162,10 +176,15 @@ def send(
|
|
|
162
176
|
if len(actions) > 0:
|
|
163
177
|
headers["Actions"] = " ; ".join([action.to_header() for action in actions])
|
|
164
178
|
|
|
165
|
-
|
|
166
|
-
requests.post(
|
|
179
|
+
return json.loads(
|
|
180
|
+
requests.post(
|
|
181
|
+
url=self.url,
|
|
182
|
+
data=message,
|
|
183
|
+
headers=headers,
|
|
184
|
+
auth=self._auth,
|
|
185
|
+
timeout=timeout_seconds,
|
|
186
|
+
).text,
|
|
167
187
|
)
|
|
168
|
-
return response
|
|
169
188
|
|
|
170
189
|
|
|
171
190
|
def send_file(
|
|
@@ -173,22 +192,34 @@ def send_file(
|
|
|
173
192
|
file: str,
|
|
174
193
|
title: Optional[str] = None,
|
|
175
194
|
priority: MessagePriority = MessagePriority.DEFAULT,
|
|
176
|
-
tags: list =
|
|
177
|
-
actions: list[Union[ViewAction, BroadcastAction, HttpAction]] =
|
|
195
|
+
tags: Optional[list] = None,
|
|
196
|
+
actions: Optional[list[Union[ViewAction, BroadcastAction, HttpAction]]] = None,
|
|
197
|
+
timeout_seconds: int = 30,
|
|
178
198
|
) -> dict:
|
|
179
|
-
"""
|
|
180
|
-
Send a file to the server
|
|
199
|
+
"""Sends a file to the server.
|
|
181
200
|
|
|
182
|
-
:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
Args:
|
|
202
|
+
file: The path to the file to send.
|
|
203
|
+
title: The title of the file.
|
|
204
|
+
priority: The priority of the message. Optional, defaults to MessagePriority.
|
|
205
|
+
tags: A list of tags to attach to the message. Can be an emoji short code.
|
|
206
|
+
actions: A list of ActionButton objects to attach to the message.
|
|
207
|
+
timeout_seconds: The number of seconds to wait before timing out.
|
|
188
208
|
|
|
189
|
-
:
|
|
190
|
-
|
|
209
|
+
Returns:
|
|
210
|
+
dict: The response from the server.
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
ToDo
|
|
214
|
+
|
|
215
|
+
Examples:
|
|
216
|
+
response = client.send_file(file="example.txt")
|
|
191
217
|
"""
|
|
218
|
+
if actions is None:
|
|
219
|
+
actions = []
|
|
220
|
+
if tags is None:
|
|
221
|
+
tags = []
|
|
222
|
+
|
|
192
223
|
headers = {
|
|
193
224
|
"Title": str(title),
|
|
194
225
|
"Filename": file.split("/")[-1],
|
|
@@ -197,8 +228,13 @@ def send_file(
|
|
|
197
228
|
"Actions": " ; ".join([action.to_header() for action in actions]),
|
|
198
229
|
}
|
|
199
230
|
|
|
200
|
-
with open(
|
|
201
|
-
|
|
202
|
-
requests.post(
|
|
231
|
+
with Path(file).open("rb") as f:
|
|
232
|
+
return json.loads(
|
|
233
|
+
requests.post(
|
|
234
|
+
url=self.url,
|
|
235
|
+
data=f,
|
|
236
|
+
headers=headers,
|
|
237
|
+
auth=self._auth,
|
|
238
|
+
timeout=timeout_seconds,
|
|
239
|
+
).text,
|
|
203
240
|
)
|
|
204
|
-
return response
|
python_ntfy-0.2.5/pyproject.toml
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "python-ntfy"
|
|
3
|
-
version = "0.2.5"
|
|
4
|
-
description = "An ntfy library aiming for feature completeness"
|
|
5
|
-
authors = ["Matthew Cane <matthew.cane0@gmail.com>"]
|
|
6
|
-
readme = "README.md"
|
|
7
|
-
license = "MIT"
|
|
8
|
-
repository = "https://github.com/MatthewCane/python-ntfy"
|
|
9
|
-
documentation = "https://matthewcane.github.io/python-ntfy/"
|
|
10
|
-
classifiers = [
|
|
11
|
-
"Development Status :: 4 - Beta",
|
|
12
|
-
"Intended Audience :: Developers"
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
[tool.poetry.dependencies]
|
|
16
|
-
python = "^3.11"
|
|
17
|
-
requests = "^2.31.0"
|
|
18
|
-
mypy = "^1.12.0"
|
|
19
|
-
|
|
20
|
-
[tool.poetry.group.dev.dependencies]
|
|
21
|
-
pytest = ">=7.4.1,<9.0.0"
|
|
22
|
-
python-dotenv = "^1.0.0"
|
|
23
|
-
pytest-asyncio = ">=0.21.1,<0.25.0"
|
|
24
|
-
pytest-codecov = ">=0.5.1,<0.7.0"
|
|
25
|
-
ruff = "^0.7.0"
|
|
26
|
-
mkdocs-material = "^9.5.41"
|
|
27
|
-
mkdocstrings-python = "^1.12.1"
|
|
28
|
-
types-pygments = "^2.18.0.20240506"
|
|
29
|
-
types-colorama = "^0.4.15.20240311"
|
|
30
|
-
types-requests = "^2.32.0.20241016"
|
|
31
|
-
types-setuptools = "^75.2.0.20241018"
|
|
32
|
-
|
|
33
|
-
[build-system]
|
|
34
|
-
requires = ["poetry-core"]
|
|
35
|
-
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class NtfyClient:
|
|
5
|
-
# The functions need to be imported here to:
|
|
6
|
-
# 1. Keep the functions in a separate file
|
|
7
|
-
# 2. Keep the docstrings working in the IDE
|
|
8
|
-
# 3. Allow the functions to be called with self
|
|
9
|
-
# MyPy does not like this, but it works
|
|
10
|
-
from ._send_functions import ( # type: ignore
|
|
11
|
-
send,
|
|
12
|
-
send_file,
|
|
13
|
-
MessagePriority,
|
|
14
|
-
ViewAction,
|
|
15
|
-
BroadcastAction,
|
|
16
|
-
HttpAction,
|
|
17
|
-
)
|
|
18
|
-
from ._get_functions import get_cached_messages # type: ignore
|
|
19
|
-
|
|
20
|
-
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
topic: str,
|
|
23
|
-
server: str = "https://ntfy.sh",
|
|
24
|
-
) -> None:
|
|
25
|
-
"""
|
|
26
|
-
:param topic: The topic to use for this client
|
|
27
|
-
:param server: The server to connect to. Must include the protocol (http/https)
|
|
28
|
-
:return None:
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
self._server = os.environ.get("NTFY_SERVER") or server
|
|
32
|
-
self._topic = topic
|
|
33
|
-
self.__set_url(self._server, topic)
|
|
34
|
-
|
|
35
|
-
if (user := os.environ.get("NTFY_USER")) and (
|
|
36
|
-
password := os.environ.get("NTFY_PASSWORD")
|
|
37
|
-
):
|
|
38
|
-
self._auth = (user, password)
|
|
39
|
-
else:
|
|
40
|
-
self._auth = ("", "")
|
|
41
|
-
|
|
42
|
-
def __set_url(self, server, topic):
|
|
43
|
-
self.url = server.strip("/") + "/" + topic
|
|
44
|
-
|
|
45
|
-
def set_topic(self, topic: str):
|
|
46
|
-
"""
|
|
47
|
-
Set a new topic for the client
|
|
48
|
-
|
|
49
|
-
:param topic: The topic to use for this client
|
|
50
|
-
:return: None
|
|
51
|
-
"""
|
|
52
|
-
self._topic = topic
|
|
53
|
-
self.__set_url(self._server, self._topic)
|
|
54
|
-
|
|
55
|
-
def get_topic(self):
|
|
56
|
-
"""
|
|
57
|
-
Get the current topic
|
|
58
|
-
|
|
59
|
-
:return: str
|
|
60
|
-
"""
|
|
61
|
-
return self._topic
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import requests
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def get_cached_messages(
|
|
6
|
-
self, since: str = "all", scheduled: bool = False
|
|
7
|
-
) -> list[dict]:
|
|
8
|
-
"""
|
|
9
|
-
Get cached messages from the server
|
|
10
|
-
|
|
11
|
-
:param since: The timestamp to start from. If set to "all", will return all messages. Optional
|
|
12
|
-
:param scheduled: If true, will return scheduled messages. Optional
|
|
13
|
-
:return: A list of messages
|
|
14
|
-
|
|
15
|
-
:examples:
|
|
16
|
-
response = client.get(since="all")
|
|
17
|
-
response = client.get(since="all", scheduled=True)
|
|
18
|
-
response = client.get(since="2019-01-01")
|
|
19
|
-
response = client.get(since="2019-01-01", scheduled=True)
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
params = {"poll": "1"}
|
|
23
|
-
if scheduled:
|
|
24
|
-
params.update({"scheduled": str(scheduled)})
|
|
25
|
-
if since:
|
|
26
|
-
params.update({"since": since})
|
|
27
|
-
|
|
28
|
-
response = [
|
|
29
|
-
json.loads(line)
|
|
30
|
-
for line in requests.get(url=self.url + "/json", params=params, auth=self._auth)
|
|
31
|
-
.text.strip()
|
|
32
|
-
.splitlines()
|
|
33
|
-
]
|
|
34
|
-
# Reverse the list so that the most recent notification is first
|
|
35
|
-
return sorted(response, key=lambda x: x["time"], reverse=True)
|
|
File without changes
|