python-ntfy 0.2.2__tar.gz → 0.2.4__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Matthew Cane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,15 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-ntfy
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: An ntfy library aiming for feature completeness
5
+ Home-page: https://github.com/MatthewCane/python-ntfy
6
+ License: MIT
5
7
  Author: Matthew Cane
6
8
  Author-email: matthew.cane0@gmail.com
7
9
  Requires-Python: >=3.11,<4.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
8
13
  Classifier: Programming Language :: Python :: 3
9
14
  Classifier: Programming Language :: Python :: 3.11
10
15
  Classifier: Programming Language :: Python :: 3.12
11
16
  Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Dist: mypy (>=1.12.0,<2.0.0)
12
18
  Requires-Dist: requests (>=2.31.0,<3.0.0)
19
+ Project-URL: Documentation, https://matthewcane.github.io/python-ntfy/
20
+ Project-URL: Repository, https://github.com/MatthewCane/python-ntfy
13
21
  Description-Content-Type: text/markdown
14
22
 
15
23
  # A Python Library For ntfy.sh
@@ -20,13 +28,13 @@ An easy-to-use ntfy python library. Aiming for full feature support.
20
28
 
21
29
  1. Install using pip with `pip3 install python-ntfy`
22
30
  2. Configure the following environment variables:
23
- - `NTFY_USER`: The username for your server (if required)
24
- - `NTFY_PASSWORD`: The password for your server (if required)
25
- - `NTFY_SERVER`: The server URL (defaults to https://ntft.sh)
31
+ - `NTFY_USER`: The username for your server (if required)
32
+ - `NTFY_PASSWORD`: The password for your server (if required)
33
+ - `NTFY_SERVER`: The server URL (defaults to `https://ntft.sh`)
26
34
  3. Setup your application to use the library:
27
35
 
28
- ```python
29
- # Import the ntfy client
36
+ ```python
37
+ # Import the ntfy client
30
38
  from python_ntfy import NtfyClient
31
39
 
32
40
  # Create an `NtfyClient` instance with a topic
@@ -36,6 +44,8 @@ client = NtfyClient(topic="Your topic")
36
44
  client.send("Your message here")
37
45
  ```
38
46
 
47
+ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](https://matthewcane.github.io/python-ntfy/).
48
+
39
49
  ## Supported Features
40
50
 
41
51
  - Username + password auth
@@ -44,30 +54,34 @@ client.send("Your message here")
44
54
  - Sending Markdown formatted text messages
45
55
  - Retrieving cached messages
46
56
  - Scheduled delivery
57
+ - Tags
58
+ - Action buttons
47
59
 
48
60
  ## Future Features
49
61
 
50
62
  - Access token auth
51
63
  - Email notifications
52
- - Tags
53
- - Action buttons
54
64
  - Send to multiple topics at once
55
65
 
56
- ## Test and Development
66
+ ## Testing and Development
57
67
 
58
68
  This project uses:
59
69
 
60
70
  - Poetry as it's dependency manager
61
71
  - Ruff for linting and code formatting
72
+ - MyPy for static type checking
62
73
 
63
74
  To install dev dependencies, run `poetry install --with dev`.
64
75
 
65
- ### Linting and Formatting
76
+ ### Linting, Formatting and Type Checking
66
77
 
67
78
  These can be run with:
68
79
 
69
80
  - `poetry run ruff format`
70
81
  - `poetry run ruff check`
82
+ - `poetry run mypy .`
83
+
84
+ These tools are also run in the CI pipeline and must pass before merging.
71
85
 
72
86
  ### Tests
73
87
 
@@ -78,11 +92,11 @@ You can run the tests against a local instance of `ntfy` *or* `ntfy.sh`.
78
92
  #### Setup Steps
79
93
 
80
94
  1. To test against a *local* `ntfy` instance:
81
- i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
82
- ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
83
- iii. Add a dummy username and password to `.env` (see example.env)
84
- 2. To test against https://ntfy.sh:
85
- i. Add username and password for ntfy.sh to `.env` (see example.env)
95
+ i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
96
+ ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
97
+ iii. Add a dummy username and password to `.env` (see example.env)
98
+ 2. To test against `https://ntfy.sh`:
99
+ i. Add username and password for ntfy.sh to `.env` (see example.env)
86
100
  3. Run the tests with `poetry run pytest --cov`
87
101
 
88
102
  The tests will sent messages to the `python_ntfy_testing` topic so you will need to subcribe to that topic to see the test messages.
@@ -6,13 +6,13 @@ An easy-to-use ntfy python library. Aiming for full feature support.
6
6
 
7
7
  1. Install using pip with `pip3 install python-ntfy`
8
8
  2. Configure the following environment variables:
9
- - `NTFY_USER`: The username for your server (if required)
10
- - `NTFY_PASSWORD`: The password for your server (if required)
11
- - `NTFY_SERVER`: The server URL (defaults to https://ntft.sh)
9
+ - `NTFY_USER`: The username for your server (if required)
10
+ - `NTFY_PASSWORD`: The password for your server (if required)
11
+ - `NTFY_SERVER`: The server URL (defaults to `https://ntft.sh`)
12
12
  3. Setup your application to use the library:
13
13
 
14
- ```python
15
- # Import the ntfy client
14
+ ```python
15
+ # Import the ntfy client
16
16
  from python_ntfy import NtfyClient
17
17
 
18
18
  # Create an `NtfyClient` instance with a topic
@@ -22,6 +22,8 @@ client = NtfyClient(topic="Your topic")
22
22
  client.send("Your message here")
23
23
  ```
24
24
 
25
+ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](https://matthewcane.github.io/python-ntfy/).
26
+
25
27
  ## Supported Features
26
28
 
27
29
  - Username + password auth
@@ -30,30 +32,34 @@ client.send("Your message here")
30
32
  - Sending Markdown formatted text messages
31
33
  - Retrieving cached messages
32
34
  - Scheduled delivery
35
+ - Tags
36
+ - Action buttons
33
37
 
34
38
  ## Future Features
35
39
 
36
40
  - Access token auth
37
41
  - Email notifications
38
- - Tags
39
- - Action buttons
40
42
  - Send to multiple topics at once
41
43
 
42
- ## Test and Development
44
+ ## Testing and Development
43
45
 
44
46
  This project uses:
45
47
 
46
48
  - Poetry as it's dependency manager
47
49
  - Ruff for linting and code formatting
50
+ - MyPy for static type checking
48
51
 
49
52
  To install dev dependencies, run `poetry install --with dev`.
50
53
 
51
- ### Linting and Formatting
54
+ ### Linting, Formatting and Type Checking
52
55
 
53
56
  These can be run with:
54
57
 
55
58
  - `poetry run ruff format`
56
59
  - `poetry run ruff check`
60
+ - `poetry run mypy .`
61
+
62
+ These tools are also run in the CI pipeline and must pass before merging.
57
63
 
58
64
  ### Tests
59
65
 
@@ -64,11 +70,11 @@ You can run the tests against a local instance of `ntfy` *or* `ntfy.sh`.
64
70
  #### Setup Steps
65
71
 
66
72
  1. To test against a *local* `ntfy` instance:
67
- i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
68
- ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
69
- iii. Add a dummy username and password to `.env` (see example.env)
70
- 2. To test against https://ntfy.sh:
71
- i. Add username and password for ntfy.sh to `.env` (see example.env)
73
+ i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
74
+ ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
75
+ iii. Add a dummy username and password to `.env` (see example.env)
76
+ 2. To test against `https://ntfy.sh`:
77
+ i. Add username and password for ntfy.sh to `.env` (see example.env)
72
78
  3. Run the tests with `poetry run pytest --cov`
73
79
 
74
80
  The tests will sent messages to the `python_ntfy_testing` topic so you will need to subcribe to that topic to see the test messages.
@@ -1,13 +1,21 @@
1
1
  [tool.poetry]
2
2
  name = "python-ntfy"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "An ntfy library aiming for feature completeness"
5
5
  authors = ["Matthew Cane <matthew.cane0@gmail.com>"]
6
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
+ ]
7
14
 
8
15
  [tool.poetry.dependencies]
9
16
  python = "^3.11"
10
17
  requests = "^2.31.0"
18
+ mypy = "^1.12.0"
11
19
 
12
20
  [tool.poetry.group.dev.dependencies]
13
21
  pytest = ">=7.4.1,<9.0.0"
@@ -15,6 +23,12 @@ python-dotenv = "^1.0.0"
15
23
  pytest-asyncio = ">=0.21.1,<0.25.0"
16
24
  pytest-codecov = ">=0.5.1,<0.7.0"
17
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"
18
32
 
19
33
  [build-system]
20
34
  requires = ["poetry-core"]
@@ -1,27 +1,38 @@
1
1
  import os
2
+ from types import MethodType
3
+ from ._send_functions import (
4
+ send,
5
+ send_file,
6
+ MessagePriority,
7
+ ViewAction,
8
+ BroadcastAction,
9
+ HttpAction,
10
+ )
11
+ from ._get_functions import get_cached_messages
2
12
 
3
13
 
4
14
  class NtfyClient:
5
- from ._send_functions import (
6
- send,
7
- send_file,
8
- MessagePriority,
9
- ViewAction,
10
- BroadcastAction,
11
- HttpAction,
12
- )
13
- from ._get_functions import get_cached_messages
14
-
15
15
  def __init__(
16
16
  self,
17
17
  topic: str,
18
18
  server: str = "https://ntfy.sh",
19
- ):
19
+ ) -> None:
20
20
  """
21
21
  :param topic: The topic to use for this client
22
22
  :param server: The server to connect to. Must include the protocol (http/https)
23
- :return:
23
+ :return None:
24
24
  """
25
+ # Bind the imported functions to the class
26
+ self.send = MethodType(send, self)
27
+ self.send_file = MethodType(send_file, self)
28
+ self.MessagePriority = MethodType(MessagePriority, self)
29
+ self.get_cached_messages = MethodType(get_cached_messages, self)
30
+
31
+ # These are Enums that don't need to be bound
32
+ self.ViewAction = ViewAction
33
+ self.BroadcastAction = BroadcastAction
34
+ self.HttpAction = HttpAction
35
+
25
36
  self._server = os.environ.get("NTFY_SERVER") or server
26
37
  self._topic = topic
27
38
  self.__set_url(self._server, topic)
@@ -45,3 +56,11 @@ class NtfyClient:
45
56
  """
46
57
  self._topic = topic
47
58
  self.__set_url(self._server, self._topic)
59
+
60
+ def get_topic(self):
61
+ """
62
+ Get the current topic
63
+
64
+ :return: str
65
+ """
66
+ return self._topic
@@ -2,7 +2,9 @@ import json
2
2
  import requests
3
3
 
4
4
 
5
- def get_cached_messages(self, since: str = "all", scheduled: bool = False):
5
+ def get_cached_messages(
6
+ self, since: str = "all", scheduled: bool = False
7
+ ) -> list[dict]:
6
8
  """
7
9
  Get cached messages from the server
8
10
 
@@ -19,7 +21,7 @@ def get_cached_messages(self, since: str = "all", scheduled: bool = False):
19
21
 
20
22
  params = {"poll": "1"}
21
23
  if scheduled:
22
- params.update({"scheduled": scheduled})
24
+ params.update({"scheduled": str(scheduled)})
23
25
  if since:
24
26
  params.update({"since": since})
25
27
 
@@ -40,7 +40,7 @@ class ViewAction(Action):
40
40
  self.action = ActionType.VIEW
41
41
  super().__init__(label=label, url=url, clear=clear)
42
42
 
43
- def to_dict(self):
43
+ def to_dict(self) -> dict:
44
44
  return {
45
45
  "action": self.action.value,
46
46
  "label": self.label,
@@ -48,7 +48,7 @@ class ViewAction(Action):
48
48
  "clear": self.clear,
49
49
  }
50
50
 
51
- def to_header(self):
51
+ def to_header(self) -> str:
52
52
  return f"action={self.action.value}, label={self.label}, url={self.url}, clear={self.clear}"
53
53
 
54
54
 
@@ -63,9 +63,9 @@ class BroadcastAction(Action):
63
63
  self.action = ActionType.BROADCAST
64
64
  self.intent = intent
65
65
  self.extras = extras
66
- super().__init__(label, ActionType.BROADCAST, clear)
66
+ super().__init__(label, ActionType.BROADCAST.value, clear)
67
67
 
68
- def to_dict(self):
68
+ def to_dict(self) -> dict:
69
69
  return {
70
70
  "action": self.action.value,
71
71
  "label": self.label,
@@ -73,7 +73,7 @@ class BroadcastAction(Action):
73
73
  "clear": self.clear,
74
74
  }
75
75
 
76
- def to_header(self):
76
+ def to_header(self) -> str:
77
77
  extras = ""
78
78
  if self.extras is not None:
79
79
  for key, value in self.extras.items():
@@ -87,7 +87,7 @@ class HttpAction(Action):
87
87
  label: str,
88
88
  url: str,
89
89
  method: str = "POST",
90
- headers: Optional[dict] = None,
90
+ headers: Optional[dict[str, str]] = None,
91
91
  body: Optional[str] = None,
92
92
  clear: bool = False,
93
93
  ):
@@ -97,8 +97,8 @@ class HttpAction(Action):
97
97
  self.body = body
98
98
  super().__init__(label, url, clear)
99
99
 
100
- def to_dict(self):
101
- action_dict = {
100
+ def to_dict(self) -> dict[str, Union[str, bool, dict[str, str]]]:
101
+ action_dict: dict[str, Union[str, bool, dict[str, str]]] = {
102
102
  "action": self.action.value,
103
103
  "label": self.label,
104
104
  "url": self.url,
@@ -111,7 +111,7 @@ class HttpAction(Action):
111
111
  action_dict["body"] = self.body
112
112
  return action_dict
113
113
 
114
- def to_header(self):
114
+ def to_header(self) -> str:
115
115
  header_str = f"action={self.action.value}, label={self.label}, url={self.url}, method={self.method}, clear={self.clear}"
116
116
  if self.headers is not None:
117
117
  headers = ""
@@ -127,12 +127,12 @@ class HttpAction(Action):
127
127
  def send(
128
128
  self,
129
129
  message: str,
130
- title: str = None,
131
- priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
130
+ title: Optional[str] = None,
131
+ priority: MessagePriority = MessagePriority.DEFAULT,
132
132
  tags: list = [],
133
- actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = [],
133
+ actions: list[Union[ViewAction, BroadcastAction, HttpAction]] = [],
134
134
  format_as_markdown: bool = False,
135
- ):
135
+ ) -> dict:
136
136
  """
137
137
  Send a text based message to the server
138
138
 
@@ -167,11 +167,11 @@ def send(
167
167
  def send_file(
168
168
  self,
169
169
  file: str,
170
- title: str = None,
171
- priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
170
+ title: Optional[str] = None,
171
+ priority: MessagePriority = MessagePriority.DEFAULT,
172
172
  tags: list = [],
173
- actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = [],
174
- ):
173
+ actions: list[Union[ViewAction, BroadcastAction, HttpAction]] = [],
174
+ ) -> dict:
175
175
  """
176
176
  Send a file to the server
177
177
 
@@ -186,17 +186,15 @@ def send_file(
186
186
  response = client.send_file(file_path="example.txt")
187
187
  """
188
188
  headers = {
189
- "Title": title,
189
+ "Title": str(title),
190
190
  "Filename": file.split("/")[-1],
191
191
  "Priority": priority.value,
192
192
  "Tags": ",".join(tags),
193
193
  "Actions": " ; ".join([action.to_header() for action in actions]),
194
194
  }
195
195
 
196
- with open(file, "rb") as file:
196
+ with open(file, "rb") as f:
197
197
  response = json.loads(
198
- requests.post(
199
- url=self.url, data=file, headers=headers, auth=self._auth
200
- ).text
198
+ requests.post(url=self.url, data=f, headers=headers, auth=self._auth).text
201
199
  )
202
200
  return response