multi_notifier 0.6.2__tar.gz → 0.6.3__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.
- multi_notifier-0.6.3/CLAUDE.md +83 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/PKG-INFO +1 -1
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/changelog.md +4 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/__init__.py +1 -1
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/connector_mail.py +1 -1
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/tests/connectors/test_connector_mail.py +4 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/.github/workflows/publish_pypi.yml +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/.github/workflows/run_tests.yml +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/.yamlfmt.yaml +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/LICENSE +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/__init__.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/connector_telegram.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/exceptions.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/interface.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/exceptions.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/py.typed +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/prek.toml +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/pyproject.toml +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/scripts/__init__.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/scripts/version_check.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/tests/__init__.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/tests/connectors/__init__.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/tests/connectors/conftest.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/tests/connectors/test_connector_telegram.py +0 -0
- {multi_notifier-0.6.2 → multi_notifier-0.6.3}/uv.lock +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
All commands use `uv` as the package manager.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install dependencies
|
|
11
|
+
uv sync --frozen --all-groups
|
|
12
|
+
|
|
13
|
+
# Lint (with autofix)
|
|
14
|
+
uv run ruff check --fix multi_notifier
|
|
15
|
+
|
|
16
|
+
# Format
|
|
17
|
+
uv run ruff format multi_notifier
|
|
18
|
+
|
|
19
|
+
# Type checking
|
|
20
|
+
uv run mypy multi_notifier
|
|
21
|
+
|
|
22
|
+
# Run tests with coverage
|
|
23
|
+
uv run pytest --cov=multi_notifier --cov-report=html --cov-report=term
|
|
24
|
+
|
|
25
|
+
# Run a single test file
|
|
26
|
+
uv run pytest tests/connectors/test_connector_mail.py
|
|
27
|
+
|
|
28
|
+
# Run all pre-commit hooks
|
|
29
|
+
uv run prek run --all-files
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
`multi_notifier` is a Python library for sending notifications over multiple protocols. The two implemented connectors are **Mail** (SMTP) and **Telegram** (via `aiogram`).
|
|
35
|
+
|
|
36
|
+
### Key abstractions
|
|
37
|
+
|
|
38
|
+
- **`multi_notifier/connectors/interface.py`** — Abstract base class all connectors must implement:
|
|
39
|
+
|
|
40
|
+
- `send_message(recipient, message, subject=None, images=None)` — accepts a single recipient or a list
|
|
41
|
+
- `is_valid_recipient(recipient)` — static method for format validation
|
|
42
|
+
|
|
43
|
+
- **Config models** — each connector defines a Pydantic v2 `<Protocol>Config` model that is validated at init time. The Telegram connector also calls `_test_config()` (an async call wrapped in `asyncio.run()`) during `__init__` to verify the bot token against the API.
|
|
44
|
+
|
|
45
|
+
- **Exceptions** — `multi_notifier/connectors/exceptions.py` contains `ConnectorError`, `ConnectorConfigurationError`, and `ConnectorTimeoutError`; the top-level `multi_notifier/exceptions.py` holds `NotificationError` and `ConfigurationError`.
|
|
46
|
+
|
|
47
|
+
### Email MIME details
|
|
48
|
+
|
|
49
|
+
The Mail connector builds MIME structure dynamically based on content:
|
|
50
|
+
|
|
51
|
+
| Content | MIME type |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Plain text only | `multipart/alternative` |
|
|
54
|
+
| Plain text + images | `multipart/mixed` |
|
|
55
|
+
| HTML only | `multipart/alternative` |
|
|
56
|
+
| HTML + images | `multipart/related` |
|
|
57
|
+
|
|
58
|
+
Images are embedded via Content-ID for HTML and sent as attachments for plain text.
|
|
59
|
+
|
|
60
|
+
### Telegram HTML
|
|
61
|
+
|
|
62
|
+
The Telegram connector uses `sulguk.transform_html()` to convert HTML into Telegram message entities. Images are sent as a media group (photos).
|
|
63
|
+
|
|
64
|
+
### Adding a new connector
|
|
65
|
+
|
|
66
|
+
1. Create `multi_notifier/connectors/connector_<protocol>.py`
|
|
67
|
+
1. Define a `<Protocol>Config` Pydantic model
|
|
68
|
+
1. Subclass `Interface` and implement `send_message` and `is_valid_recipient`
|
|
69
|
+
1. Raise exceptions from `multi_notifier.connectors.exceptions`
|
|
70
|
+
1. Add `tests/connectors/test_connector_<protocol>.py` with async-compatible tests
|
|
71
|
+
|
|
72
|
+
## Changelog
|
|
73
|
+
|
|
74
|
+
Every change must be documented in `changelog.md` under a new version heading using the format `# Version X.Y.Z - DD.MM.YYYY`, and the version in `multi_notifier/__init__.py` must be bumped to match.
|
|
75
|
+
|
|
76
|
+
## Code quality standards
|
|
77
|
+
|
|
78
|
+
- **Python** ≥ 3.11
|
|
79
|
+
- **Line length** 150 characters (ruff)
|
|
80
|
+
- **Docstrings** Google style
|
|
81
|
+
- **Coverage** 100% required (`fail_under = 100`)
|
|
82
|
+
- **Ruff** runs with `select = ["ALL"]` — check `pyproject.toml` for the explicit ignores list before adding new code
|
|
83
|
+
- Async tests use `pytest-asyncio`; mocking via `pytest-mock`
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Version 0.6.3 - 17.05.2026
|
|
2
|
+
|
|
3
|
+
- fixed missing file suffix on image attachments in HTML mails on Android (added `Content-Disposition: inline; filename=...` to embedded images)
|
|
4
|
+
|
|
1
5
|
# Version 0.6.2 - 12.05.2026
|
|
2
6
|
|
|
3
7
|
- bumped requests (and other packages) to avoid security vulnerabilities
|
|
@@ -148,8 +148,8 @@ class Mail(multi_notifier.connectors.interface.Interface):
|
|
|
148
148
|
try:
|
|
149
149
|
img_data = image_path.read_bytes()
|
|
150
150
|
img = MIMEImage(img_data)
|
|
151
|
-
# Set Content-ID header: allows HTML to reference this image via src="cid:..."
|
|
152
151
|
img.add_header("Content-ID", f"<{cid}>")
|
|
152
|
+
img.add_header("Content-Disposition", f'inline; filename="{image_path.name}"')
|
|
153
153
|
msg.attach(img)
|
|
154
154
|
except FileNotFoundError:
|
|
155
155
|
LOGGER.warning(f"Image file not found: {image_path}")
|
|
@@ -100,6 +100,10 @@ def test_create_msg_structure(
|
|
|
100
100
|
# HTML messages should have Content-ID for embedding
|
|
101
101
|
assert "Content-ID" in image_part
|
|
102
102
|
assert "<img1>" in image_part.get("Content-ID")
|
|
103
|
+
# Must also carry a filename so Android clients know the extension
|
|
104
|
+
assert "Content-Disposition" in image_part
|
|
105
|
+
assert "inline" in image_part.get("Content-Disposition")
|
|
106
|
+
assert "filename=" in image_part.get("Content-Disposition")
|
|
103
107
|
else:
|
|
104
108
|
# Plain text messages should have Content-Disposition for attachment
|
|
105
109
|
assert "Content-Disposition" in image_part
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_notifier-0.6.2 → multi_notifier-0.6.3}/multi_notifier/connectors/connector_telegram.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|