Metadata-Version: 2.1
Name: signalwatch
Version: 0.1.0
Summary: Configurable monitoring and alerting engine for domain-specific signals.
Author: Ernest Borysenko
License: MIT License
        
        Copyright (c) 2026 Ernest Borysenko
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/Mephistos-ML/signalwatch
Project-URL: Repository, https://github.com/Mephistos-ML/signalwatch
Project-URL: Issues, https://github.com/Mephistos-ML/signalwatch/issues
Keywords: monitoring,telegram,scraping,watcher,tkmaxx
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: beautifulsoup4>=4.12.0
Requires-Dist: playwright>=1.45.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: requests>=2.32.0
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: python-semantic-release>=9.0.0; extra == "dev"
Requires-Dist: ruff>=0.5.0; extra == "dev"

# SignalWatch

SignalWatch is a configurable product monitoring bot. It fetches product listings
from external sources, normalises them into domain objects, stores seen items in
SQLite, detects newly observed items, applies matching rules, and sends
notifications.

The first production source is TK Maxx Gold Label.

## Features

- Fetches TK Maxx Gold Label product listings with Playwright-rendered HTML.
- Normalises source-specific product cards into a shared `WatchItem` domain model.
- Persists seen items in SQLite with `first_seen_at` and `last_seen_at` timestamps.
- Detects new items across runs.
- Filters notifications by brand with case-insensitive matching.
- Supports log and Telegram notification adapters.
- Provides one-shot checks and long-running watch mode.
- Adds random polling jitter to avoid fixed, machine-like request intervals.

## Architecture

SignalWatch keeps the monitoring pipeline layered:

```text
CLI -> app workflow -> source -> WatchItem -> storage/dedup -> matching -> notification
```

Key modules:

- `signalwatch.cli`: command-line entry point.
- `signalwatch.app.run_bot`: one monitoring cycle.
- `signalwatch.app.watch`: repeated monitoring loop with polling jitter.
- `signalwatch.sources`: external source adapters.
- `signalwatch.storage`: persistence adapters.
- `signalwatch.matching`: relevance filters such as brand matching.
- `signalwatch.notify`: notification interfaces and adapters.
- `signalwatch.models`: domain models shared across the pipeline.

The source layer only fetches and normalises products. Storage, deduplication,
matching, scheduling, and notifications are kept outside source adapters.

## Installation

SignalWatch requires Python 3.12 or newer.

```bash
python3 -m pip install -e ".[dev]"
python3 -m playwright install webkit
```

## Configuration

Example Telegram configuration:

```yaml
source:
  type: tkmaxx
  url: "https://www.tkmaxx.com/uk/en/mens-gold-label/c/02090000?sort=published_date+desc"

storage:
  sqlite_path: "../data/tkmaxx.sqlite3"

notification:
  type: telegram
  bot_token_env: "SIGNALWATCH_TELEGRAM_BOT_TOKEN"
  chat_id_env: "SIGNALWATCH_TELEGRAM_CHAT_ID"

matching:
  brands:
    - Balenciaga
    - Rick Owens
    - Palm Angels
    - Off-White
    - Saint Laurent
    - Stone Island
    - Givenchy

polling:
  interval_seconds: 3600
  jitter_seconds: 600
```

Relative paths such as `storage.sqlite_path` are resolved relative to the YAML
file location.

## Usage

Run one monitoring cycle:

```bash
signalwatch check examples/tkmaxx-telegram.yml
```

Run continuously:

```bash
signalwatch watch examples/tkmaxx-telegram.yml
```

Stop watch mode with `Ctrl+C`.

## Telegram Setup

1. Create a bot with `@BotFather`.
2. Send `/start` to the bot.
3. Fetch updates to find your chat ID:

```bash
curl "https://api.telegram.org/bot<TOKEN>/getUpdates"
```

4. Export the required environment variables:

```bash
export SIGNALWATCH_TELEGRAM_BOT_TOKEN="<TOKEN>"
export SIGNALWATCH_TELEGRAM_CHAT_ID="<CHAT_ID>"
```

5. Run:

```bash
signalwatch check examples/tkmaxx-telegram.yml
```

Telegram messages use a compact HTML-formatted layout:

```text
❗ NEW ITEM

Gucci & Co - Leather belt
💷 £199.99 | RRP £410.00 | Save £210.01 (51%)
⚠️ ONLY 1 LEFT

https://example.com/products/1
```

## Matching

Brand matching is case-insensitive:

```yaml
matching:
  brands:
    - Balenciaga
    - Rick Owens
```

The following source values all match `Balenciaga`:

```text
Balenciaga
BALENCIAGA
balenciaga
```

If `matching` is omitted or `matching.brands` is empty, all newly seen items are
eligible for notification.

Storage still records every fetched item, not only matched items. Matching only
controls which new items are reported.

## Storage

SQLite is used as durable local state. The `seen_items` table stores:

- `source`
- `item_id`
- `title`
- `url`
- `first_seen_at`
- `last_seen_at`
- `metadata_json`

The primary key is `(source, item_id)`, which allows one database to store items
from multiple sources without collisions.

On a fresh database, all currently visible matching items are considered new.
After that, only previously unseen matching items trigger notifications.

## Polling Jitter

Watch mode uses:

```yaml
polling:
  interval_seconds: 3600
  jitter_seconds: 600
```

With this configuration, checks happen every 3000 to 4200 seconds. This avoids
requesting the source at exactly the same time every hour.

## Development

Run tests:

```bash
pytest
```

Run a syntax check:

```bash
python3 -m compileall src tests
```

## Current Limitations

- TK Maxx is the only implemented production source.
- Telegram notifications are text-only; product images are parsed but not sent.
- Watch mode runs as a foreground process, not as an installed service.
- Notification batching and retry policies are intentionally minimal.

## License

MIT License. See [LICENSE](LICENSE).
