Skip to main content

Configurable monitoring and alerting engine for domain-specific signals.

Project description

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:

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.

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

Configuration

Example Telegram configuration:

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:

signalwatch check examples/tkmaxx-telegram.yml

Run continuously:

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:
curl "https://api.telegram.org/bot<TOKEN>/getUpdates"
  1. Export the required environment variables:
export SIGNALWATCH_TELEGRAM_BOT_TOKEN="<TOKEN>"
export SIGNALWATCH_TELEGRAM_CHAT_ID="<CHAT_ID>"
  1. Run:
signalwatch check examples/tkmaxx-telegram.yml

Telegram messages use a compact HTML-formatted layout:

❗ 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:

matching:
  brands:
    - Balenciaga
    - Rick Owens

The following source values all match Balenciaga:

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:

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:

pytest

Run a syntax check:

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

signalwatch-0.1.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

signalwatch-0.1.0-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

Details for the file signalwatch-0.1.0.tar.gz.

File metadata

  • Download URL: signalwatch-0.1.0.tar.gz
  • Upload date:
  • Size: 22.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for signalwatch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7aaa2a0761d3b2dca2b2a9302fca5242a708a52f440e402b4bac6e0afaa5f49e
MD5 aa73f852818ae9ab20d25b7df00e1bf5
BLAKE2b-256 af4df794a70ba88e7783b6130564314f0ffa69b5b672fd554c2c5824db70bca8

See more details on using hashes here.

File details

Details for the file signalwatch-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: signalwatch-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for signalwatch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5e5daf8a56fddec627e351b117c0daaadc1f6cde15350fcd1e5a12c763e6cea6
MD5 182eafac7f1d1d50f06e998244f1c0b8
BLAKE2b-256 5245ea5dc3ae722c65cf226cc988b9f0ce017071c2cf60679aaac2c612b90683

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page