watchdiff-core 0.2.2__tar.gz → 0.2.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.
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/PKG-INFO +27 -1
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/README.md +26 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/pyproject.toml +1 -1
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/fetcher/fetcher.py +8 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/models.py +1 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/.github/workflows/ci.yml +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/.github/workflows/release-testpypi.yml +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/.github/workflows/release.yml +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/.gitignore +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/.python-version +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/LICENSE +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/main.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/tests/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/tests/test_db.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/tests/test_watchdiff.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/uv.lock +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/ai_summarizer/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cert_fetcher/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cert_models.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cert_scheduler/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cleaner/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cleaner/cleaner.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cli/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cli/main.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/core.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/cron_parser/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_diff/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_fetcher/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_fetcher/mysql.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_fetcher/postgres.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_fetcher/sqlite.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_models.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/db_scheduler/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/diff/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/diff/engine.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/exporter/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/exporter/exporter.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/fetcher/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/fetcher/browser.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/file_fetcher/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/json_path/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/mailer/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/notifier/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/notifier/notifier.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/parser/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/parser/parser.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/scheduler/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/scheduler/scheduler.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/sitemap/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/status_server/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/status_server/server.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/store/__init__.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/store/sqlite_store.py +0 -0
- {watchdiff_core-0.2.2 → watchdiff_core-0.2.3}/watchdiff/store/store.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: watchdiff-core
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Lightweight web change monitoring library - clean diffs, AI summaries, file/API/SSL/DB monitoring, structured alerts.
|
|
5
5
|
Author: WatchDiff Contributors
|
|
6
6
|
License: BSD-2-Clause
|
|
@@ -91,6 +91,7 @@ WatchDiff watches web pages, files, APIs, databases, and SSL certificates - then
|
|
|
91
91
|
| Confirm change before alerting | `confirm_after=30` (flapping detection) |
|
|
92
92
|
| Monitor a sitemap for URL changes | `.watch_sitemap("https://example.com/sitemap.xml")` |
|
|
93
93
|
| Watch one JSON field only | `json_path="$.data.price"` |
|
|
94
|
+
| Monitor authenticated pages | `cookies={"session": "abc123", "csrftoken": "xyz"}` |
|
|
94
95
|
| Send email on change | `alert=AlertConfig(email=EmailConfig(to="...", smtp=SmtpConfig(...)))` |
|
|
95
96
|
| AI summary of changes | `ai_summary=True, ai_provider=AiProvider(type="gemini", api_key="...")` |
|
|
96
97
|
| Customize the AI prompt | `ai_prompt="Summarize in French."` or `ai_prompt=lambda r: ...` |
|
|
@@ -146,6 +147,7 @@ WatchDiff watches web pages, files, APIs, databases, and SSL certificates - then
|
|
|
146
147
|
- [Flapping detection](#flapping-detection)
|
|
147
148
|
- [Sitemap monitoring](#sitemap-monitoring)
|
|
148
149
|
- [JSON path targeting](#json-path-targeting)
|
|
150
|
+
- [Cookie-based authentication](#cookie-based-authentication)
|
|
149
151
|
- [Email alerts](#email-alerts)
|
|
150
152
|
- [API reference](#api-reference)
|
|
151
153
|
- [`.watch()`](#watchurl--)
|
|
@@ -1395,6 +1397,29 @@ value = extract_json_path('{"data": {"price": 42.5}}', "$.data.price")
|
|
|
1395
1397
|
# "42.5"
|
|
1396
1398
|
```
|
|
1397
1399
|
|
|
1400
|
+
## Cookie-based authentication
|
|
1401
|
+
|
|
1402
|
+
Pass cookies with every request to monitor pages that require a login session. Cookies are merged into the outgoing `Cookie` header on top of any `headers` you supply.
|
|
1403
|
+
|
|
1404
|
+
```python
|
|
1405
|
+
wd.watch("https://app.example.com/dashboard",
|
|
1406
|
+
cookies={"session": "your-session-token", "csrftoken": "your-csrf-token"},
|
|
1407
|
+
interval=300,
|
|
1408
|
+
on_change=lambda r: print(r.changes))
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
Combine `cookies` with `headers` — the cookie string is appended to any existing `Cookie` header:
|
|
1412
|
+
|
|
1413
|
+
```python
|
|
1414
|
+
wd.watch_api("https://api.example.com/account",
|
|
1415
|
+
headers={"Authorization": "Bearer token123"},
|
|
1416
|
+
cookies={"_ga": "GA1.1.0000000000.0000000000"},
|
|
1417
|
+
json_path="$.balance",
|
|
1418
|
+
interval=60)
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
> Tip: copy cookie values from your browser's DevTools → Network tab → Request Headers.
|
|
1422
|
+
|
|
1398
1423
|
## Email alerts
|
|
1399
1424
|
|
|
1400
1425
|
Send email notifications via SMTP when a change is detected. Uses Python stdlib `smtplib` — no extra dependency required.
|
|
@@ -1480,6 +1505,7 @@ Register a URL to monitor. All keyword arguments are optional. Returns `self` (c
|
|
|
1480
1505
|
| `on_spike` | `Callable \| None` | `None` | Called with `SpikeInfo` when spike is detected |
|
|
1481
1506
|
| `alert_on_status_change` | `bool` | `False` | Alert when HTTP status code changes (200→503, etc.) |
|
|
1482
1507
|
| `on_status_change` | `Callable \| None` | `None` | Called with `StatusChangeInfo` on status code change |
|
|
1508
|
+
| `cookies` | `dict[str, str]` | `{}` | Cookies sent with every request — merged into the `Cookie` header (e.g. `{"session": "abc"}`) |
|
|
1483
1509
|
| `alert` | `AlertConfig \| None` | `None` | Full alert config — use instead of `on_change`/`webhooks` to include email or fine-tune retries |
|
|
1484
1510
|
| `alert_if` | `Callable[[DiffReport], bool] \| None` | `None` | Custom gate - only alert when this function returns `True` |
|
|
1485
1511
|
| `expected_status` | `int \| None` | `None` | Fire `on_error` when actual HTTP status differs from this value |
|
|
@@ -43,6 +43,7 @@ WatchDiff watches web pages, files, APIs, databases, and SSL certificates - then
|
|
|
43
43
|
| Confirm change before alerting | `confirm_after=30` (flapping detection) |
|
|
44
44
|
| Monitor a sitemap for URL changes | `.watch_sitemap("https://example.com/sitemap.xml")` |
|
|
45
45
|
| Watch one JSON field only | `json_path="$.data.price"` |
|
|
46
|
+
| Monitor authenticated pages | `cookies={"session": "abc123", "csrftoken": "xyz"}` |
|
|
46
47
|
| Send email on change | `alert=AlertConfig(email=EmailConfig(to="...", smtp=SmtpConfig(...)))` |
|
|
47
48
|
| AI summary of changes | `ai_summary=True, ai_provider=AiProvider(type="gemini", api_key="...")` |
|
|
48
49
|
| Customize the AI prompt | `ai_prompt="Summarize in French."` or `ai_prompt=lambda r: ...` |
|
|
@@ -98,6 +99,7 @@ WatchDiff watches web pages, files, APIs, databases, and SSL certificates - then
|
|
|
98
99
|
- [Flapping detection](#flapping-detection)
|
|
99
100
|
- [Sitemap monitoring](#sitemap-monitoring)
|
|
100
101
|
- [JSON path targeting](#json-path-targeting)
|
|
102
|
+
- [Cookie-based authentication](#cookie-based-authentication)
|
|
101
103
|
- [Email alerts](#email-alerts)
|
|
102
104
|
- [API reference](#api-reference)
|
|
103
105
|
- [`.watch()`](#watchurl--)
|
|
@@ -1347,6 +1349,29 @@ value = extract_json_path('{"data": {"price": 42.5}}', "$.data.price")
|
|
|
1347
1349
|
# "42.5"
|
|
1348
1350
|
```
|
|
1349
1351
|
|
|
1352
|
+
## Cookie-based authentication
|
|
1353
|
+
|
|
1354
|
+
Pass cookies with every request to monitor pages that require a login session. Cookies are merged into the outgoing `Cookie` header on top of any `headers` you supply.
|
|
1355
|
+
|
|
1356
|
+
```python
|
|
1357
|
+
wd.watch("https://app.example.com/dashboard",
|
|
1358
|
+
cookies={"session": "your-session-token", "csrftoken": "your-csrf-token"},
|
|
1359
|
+
interval=300,
|
|
1360
|
+
on_change=lambda r: print(r.changes))
|
|
1361
|
+
```
|
|
1362
|
+
|
|
1363
|
+
Combine `cookies` with `headers` — the cookie string is appended to any existing `Cookie` header:
|
|
1364
|
+
|
|
1365
|
+
```python
|
|
1366
|
+
wd.watch_api("https://api.example.com/account",
|
|
1367
|
+
headers={"Authorization": "Bearer token123"},
|
|
1368
|
+
cookies={"_ga": "GA1.1.0000000000.0000000000"},
|
|
1369
|
+
json_path="$.balance",
|
|
1370
|
+
interval=60)
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
> Tip: copy cookie values from your browser's DevTools → Network tab → Request Headers.
|
|
1374
|
+
|
|
1350
1375
|
## Email alerts
|
|
1351
1376
|
|
|
1352
1377
|
Send email notifications via SMTP when a change is detected. Uses Python stdlib `smtplib` — no extra dependency required.
|
|
@@ -1432,6 +1457,7 @@ Register a URL to monitor. All keyword arguments are optional. Returns `self` (c
|
|
|
1432
1457
|
| `on_spike` | `Callable \| None` | `None` | Called with `SpikeInfo` when spike is detected |
|
|
1433
1458
|
| `alert_on_status_change` | `bool` | `False` | Alert when HTTP status code changes (200→503, etc.) |
|
|
1434
1459
|
| `on_status_change` | `Callable \| None` | `None` | Called with `StatusChangeInfo` on status code change |
|
|
1460
|
+
| `cookies` | `dict[str, str]` | `{}` | Cookies sent with every request — merged into the `Cookie` header (e.g. `{"session": "abc"}`) |
|
|
1435
1461
|
| `alert` | `AlertConfig \| None` | `None` | Full alert config — use instead of `on_change`/`webhooks` to include email or fine-tune retries |
|
|
1436
1462
|
| `alert_if` | `Callable[[DiffReport], bool] \| None` | `None` | Custom gate - only alert when this function returns `True` |
|
|
1437
1463
|
| `expected_status` | `int \| None` | `None` | Fire `on_error` when actual HTTP status differs from this value |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "watchdiff-core"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Lightweight web change monitoring library - clean diffs, AI summaries, file/API/SSL/DB monitoring, structured alerts."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -85,6 +85,10 @@ class Fetcher:
|
|
|
85
85
|
FetchError: after all retries are exhausted.
|
|
86
86
|
"""
|
|
87
87
|
headers = {**_DEFAULT_ACCEPT_HEADERS, "User-Agent": _pick_ua(config), **config.headers}
|
|
88
|
+
if config.cookies:
|
|
89
|
+
existing = headers.get("Cookie", "")
|
|
90
|
+
cookie_str = "; ".join(f"{k}={v}" for k, v in config.cookies.items())
|
|
91
|
+
headers["Cookie"] = f"{existing}; {cookie_str}" if existing else cookie_str
|
|
88
92
|
proxy = _pick_proxy(config)
|
|
89
93
|
|
|
90
94
|
client_kwargs: dict = {"timeout": config.timeout, "follow_redirects": True}
|
|
@@ -133,6 +137,10 @@ class AsyncFetcher:
|
|
|
133
137
|
FetchError: after all retries are exhausted.
|
|
134
138
|
"""
|
|
135
139
|
headers = {**_DEFAULT_ACCEPT_HEADERS, "User-Agent": _pick_ua(config), **config.headers}
|
|
140
|
+
if config.cookies:
|
|
141
|
+
existing = headers.get("Cookie", "")
|
|
142
|
+
cookie_str = "; ".join(f"{k}={v}" for k, v in config.cookies.items())
|
|
143
|
+
headers["Cookie"] = f"{existing}; {cookie_str}" if existing else cookie_str
|
|
136
144
|
proxy = _pick_proxy(config)
|
|
137
145
|
|
|
138
146
|
client_kwargs: dict = {"timeout": config.timeout, "follow_redirects": True}
|
|
@@ -53,6 +53,7 @@ class WatchConfig:
|
|
|
53
53
|
interval: int = 300 # seconds between checks
|
|
54
54
|
label: str | None = None # human-readable name for this watch
|
|
55
55
|
headers: dict[str, str] = field(default_factory=dict)
|
|
56
|
+
cookies: dict[str, str] = field(default_factory=dict)
|
|
56
57
|
timeout: int = 15 # HTTP timeout in seconds
|
|
57
58
|
ignore_selectors: list[str] = field(default_factory=list)
|
|
58
59
|
ignore_patterns: list[str] = field(default_factory=list)
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|