python-ntfy 0.1.0__tar.gz → 0.2.0__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.1.0 → python_ntfy-0.2.0}/PKG-INFO +3 -1
- {python_ntfy-0.1.0 → python_ntfy-0.2.0}/pyproject.toml +1 -1
- {python_ntfy-0.1.0 → python_ntfy-0.2.0}/python_ntfy/__init__.py +3 -4
- python_ntfy-0.2.0/python_ntfy/_send_functions.py +188 -0
- python_ntfy-0.1.0/python_ntfy/_send_functions.py +0 -48
- {python_ntfy-0.1.0 → python_ntfy-0.2.0}/README.md +0 -0
- {python_ntfy-0.1.0 → python_ntfy-0.2.0}/python_ntfy/_get_functions.py +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-ntfy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: An ntfy library aiming for feature completeness
|
|
5
5
|
Author: Matthew Cane
|
|
6
6
|
Author-email: matthew.cane0@gmail.com
|
|
7
7
|
Requires-Python: >=3.11,<4.0
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
12
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
11
13
|
Description-Content-Type: text/markdown
|
|
12
14
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
class NtfyClient:
|
|
5
|
-
from ._send_functions import send, send_file
|
|
4
|
+
from ._send_functions import send, send_file, MessagePriority, ViewAction, BroadcastAction, HttpAction
|
|
6
5
|
from ._get_functions import get_cached_messages
|
|
7
6
|
|
|
8
7
|
def __init__(
|
|
@@ -15,9 +14,9 @@ class NtfyClient:
|
|
|
15
14
|
:param server: The server to connect to. Must include the protocol (http/https)
|
|
16
15
|
:return:
|
|
17
16
|
"""
|
|
18
|
-
self._server = server
|
|
17
|
+
self._server = os.environ.get("NTFY_SERVER") or server
|
|
19
18
|
self._topic = topic
|
|
20
|
-
self.__set_url(
|
|
19
|
+
self.__set_url(self._server, topic)
|
|
21
20
|
|
|
22
21
|
if (user := os.environ.get("NTFY_USER")) and (
|
|
23
22
|
password := os.environ.get("NTFY_PASSWORD")
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import json, requests
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
class MessagePriority(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Ntfy message priority levels.
|
|
8
|
+
"""
|
|
9
|
+
MIN = "1"
|
|
10
|
+
LOW = "2"
|
|
11
|
+
DEFAULT = "3"
|
|
12
|
+
HIGH = "4"
|
|
13
|
+
MAX = "5"
|
|
14
|
+
URGENT = MAX
|
|
15
|
+
|
|
16
|
+
class ActionType(Enum):
|
|
17
|
+
"""
|
|
18
|
+
Action button types
|
|
19
|
+
"""
|
|
20
|
+
VIEW = "view"
|
|
21
|
+
BROADCAST = "broadcast"
|
|
22
|
+
HTTP = "http"
|
|
23
|
+
|
|
24
|
+
class Action():
|
|
25
|
+
def __init__(self, label: str, url: str, clear: bool = False):
|
|
26
|
+
self.label = label
|
|
27
|
+
self.url = url
|
|
28
|
+
self.actions: list = []
|
|
29
|
+
self.clear = clear
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ViewAction(Action):
|
|
33
|
+
def __init__(self, label: str, url: str, clear: bool = False):
|
|
34
|
+
self.action = ActionType.VIEW
|
|
35
|
+
super().__init__(label=label, url=url, clear=clear)
|
|
36
|
+
|
|
37
|
+
def to_dict(self):
|
|
38
|
+
return {
|
|
39
|
+
"action": self.action.value,
|
|
40
|
+
"label": self.label,
|
|
41
|
+
"url": self.url,
|
|
42
|
+
"clear": self.clear
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
def to_header(self):
|
|
46
|
+
return f"action={self.action.value}, label={self.label}, url={self.url}, clear={self.clear}"
|
|
47
|
+
|
|
48
|
+
class BroadcastAction(Action):
|
|
49
|
+
def __init__(self, label: str, intent: str = "io.heckel.ntfy.USER_ACTION", extras: Optional[dict] = None, clear: bool = False):
|
|
50
|
+
self.action = ActionType.BROADCAST
|
|
51
|
+
self.intent = intent
|
|
52
|
+
self.extras = extras
|
|
53
|
+
super().__init__(label, ActionType.BROADCAST, clear)
|
|
54
|
+
|
|
55
|
+
def to_dict(self):
|
|
56
|
+
return {
|
|
57
|
+
"action": self.action.value,
|
|
58
|
+
"label": self.label,
|
|
59
|
+
"extras": self.extras,
|
|
60
|
+
"clear": self.clear
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def to_header(self):
|
|
64
|
+
extras = ""
|
|
65
|
+
if self.extras is not None:
|
|
66
|
+
for key, value in self.extras.items():
|
|
67
|
+
extras += f"{key}={value},"
|
|
68
|
+
return f"action={self.action.value}, label={self.label}, url={self.url}, clear={self.clear}"
|
|
69
|
+
|
|
70
|
+
class HttpAction(Action):
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
label: str,
|
|
74
|
+
url: str,
|
|
75
|
+
method: str = "POST",
|
|
76
|
+
headers: Optional[dict] = None,
|
|
77
|
+
body: Optional[str] = None,
|
|
78
|
+
clear: bool = False
|
|
79
|
+
):
|
|
80
|
+
self.action = ActionType.HTTP
|
|
81
|
+
self.method = method
|
|
82
|
+
self.headers = headers
|
|
83
|
+
self.body = body
|
|
84
|
+
super().__init__(label, url, clear)
|
|
85
|
+
|
|
86
|
+
def to_dict(self):
|
|
87
|
+
action_dict = {
|
|
88
|
+
"action": self.action.value,
|
|
89
|
+
"label": self.label,
|
|
90
|
+
"url": self.url,
|
|
91
|
+
"method": self.method,
|
|
92
|
+
"clear": self.clear
|
|
93
|
+
}
|
|
94
|
+
if self.headers:
|
|
95
|
+
action_dict["headers"] = self.headers
|
|
96
|
+
if self.body:
|
|
97
|
+
action_dict["body"] = self.body
|
|
98
|
+
return action_dict
|
|
99
|
+
|
|
100
|
+
def to_header(self):
|
|
101
|
+
header_str = f"action={self.action.value}, label={self.label}, url={self.url}, method={self.method}, clear={self.clear}"
|
|
102
|
+
if self.headers is not None:
|
|
103
|
+
headers = ""
|
|
104
|
+
for key, value in self.headers.items():
|
|
105
|
+
headers += f"headers.{key}={value}"
|
|
106
|
+
header_str += f", {headers}"
|
|
107
|
+
if self.body:
|
|
108
|
+
header_str += f", body={self.body}"
|
|
109
|
+
print(header_str)
|
|
110
|
+
return header_str
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def send(
|
|
114
|
+
self,
|
|
115
|
+
message: str,
|
|
116
|
+
title: str = None,
|
|
117
|
+
priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
|
|
118
|
+
tags: list = [],
|
|
119
|
+
actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = [],
|
|
120
|
+
format_as_markdown: bool = False
|
|
121
|
+
):
|
|
122
|
+
"""
|
|
123
|
+
Send a text based message to the server
|
|
124
|
+
|
|
125
|
+
:param message: The message to send
|
|
126
|
+
:param title: The title of the message. Optional
|
|
127
|
+
:param priority: The priority of the message. Optional, defaults to MessagePriority.DEFAULT
|
|
128
|
+
:param tags: A list of tags to attach to the message. Can be an emoji short code. Optional
|
|
129
|
+
:param format_as_markdown: If true, the message will be formatted as markdown. Optional
|
|
130
|
+
:param actions: A list of Actions objects to attach to the message. Optional
|
|
131
|
+
:return: The response from the server
|
|
132
|
+
|
|
133
|
+
:examples:
|
|
134
|
+
response = client.send(message="Example message")
|
|
135
|
+
response = client.send(message="Example message", title="Example title", priority=MessagePriority.HIGH, tags=["fire", "warning"])
|
|
136
|
+
response = client.send(message="*Example markdown*", format_as_markdown=True)
|
|
137
|
+
"""
|
|
138
|
+
headers = {
|
|
139
|
+
"Title": title,
|
|
140
|
+
"Priority": priority.value,
|
|
141
|
+
"Tags": ",".join(tags),
|
|
142
|
+
"Markdown": "true" if format_as_markdown else "false",
|
|
143
|
+
}
|
|
144
|
+
if len(actions) > 0:
|
|
145
|
+
headers['Actions'] = " ; ".join([action.to_header() for action in actions])
|
|
146
|
+
|
|
147
|
+
response = json.loads(
|
|
148
|
+
requests.post(url=self.url, data=message, headers=headers, auth=self._auth).text
|
|
149
|
+
)
|
|
150
|
+
return response
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def send_file(
|
|
154
|
+
self,
|
|
155
|
+
file: str,
|
|
156
|
+
title: str = None,
|
|
157
|
+
priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
|
|
158
|
+
tags: list = [],
|
|
159
|
+
actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = []
|
|
160
|
+
):
|
|
161
|
+
"""
|
|
162
|
+
Send a file to the server
|
|
163
|
+
|
|
164
|
+
:param file_path: The path to the file to send.
|
|
165
|
+
:param title: The title of the file. Optional
|
|
166
|
+
:param priority: The priority of the message. Optional, defaults to MessagePriority.DEFAULT
|
|
167
|
+
:param tags: A list of tags to attach to the message. Can be an emoji short code. Optional
|
|
168
|
+
:param actions: A list of ActionButton objects to attach to the message. Optional
|
|
169
|
+
:return: The response from the server
|
|
170
|
+
|
|
171
|
+
:examples:
|
|
172
|
+
response = client.send_file(file_path="example.txt")
|
|
173
|
+
"""
|
|
174
|
+
headers = {
|
|
175
|
+
"Title": title,
|
|
176
|
+
"Filename": file.split("/")[-1],
|
|
177
|
+
"Priority": priority.value,
|
|
178
|
+
"Tags": ",".join(tags),
|
|
179
|
+
"Actions": " ; ".join([action.to_header() for action in actions])
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
with open(file, "rb") as file:
|
|
183
|
+
response = json.loads(
|
|
184
|
+
requests.post(
|
|
185
|
+
url=self.url, data=file, headers=headers, auth=self._auth
|
|
186
|
+
).text
|
|
187
|
+
)
|
|
188
|
+
return response
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import json, requests
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def send(self, message: str, title: str = None, format_as_markdown: bool = False):
|
|
5
|
-
"""
|
|
6
|
-
Send a text based message to the server
|
|
7
|
-
|
|
8
|
-
:param message: The message to send
|
|
9
|
-
:param title: The title of the message. Optional
|
|
10
|
-
:param format_as_markdown: If true, the message will be formatted as markdown. Optional
|
|
11
|
-
:return: The response from the server
|
|
12
|
-
|
|
13
|
-
:examples:
|
|
14
|
-
response = client.send(message="Example message")
|
|
15
|
-
response = client.send(message="Example message", title="Example title")
|
|
16
|
-
response = client.send(message="*Example markdown*", format_as_markdown=True)
|
|
17
|
-
"""
|
|
18
|
-
headers = {
|
|
19
|
-
"Title": title,
|
|
20
|
-
"Markdown": "true" if format_as_markdown else "false",
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
response = json.loads(
|
|
24
|
-
requests.post(url=self.url, data=message, headers=headers, auth=self._auth).text
|
|
25
|
-
)
|
|
26
|
-
return response
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def send_file(self, file: str, title: str = None):
|
|
30
|
-
"""
|
|
31
|
-
Send a file to the server
|
|
32
|
-
|
|
33
|
-
:param file_path: The path to the file to send
|
|
34
|
-
:param title: The title of the file. Optional
|
|
35
|
-
:return: The response from the server
|
|
36
|
-
|
|
37
|
-
:examples:
|
|
38
|
-
response = client.send_file(file_path="example.txt")
|
|
39
|
-
"""
|
|
40
|
-
headers = {"Title": title, "Filename": file.split("/")[-1]}
|
|
41
|
-
|
|
42
|
-
with open(file, "rb") as file:
|
|
43
|
-
response = json.loads(
|
|
44
|
-
requests.post(
|
|
45
|
-
url=self.url, data=file, headers=headers, auth=self._auth
|
|
46
|
-
).text
|
|
47
|
-
)
|
|
48
|
-
return response
|
|
File without changes
|
|
File without changes
|