Skip to content

A simple, self-hosted monitoring tool that checks websites for conditions you defined and sends instant notifications. (Like help you monitor changes of class availability 24/7)

License

Notifications You must be signed in to change notification settings

t41372/monitor-worker-v2

Repository files navigation

Monitor Worker

Monitor Worker

A simple, self-hosted monitoring tool that checks websites for conditions you defined and sends instant notifications.

License Python Docker Code coverage


Perfect for monitoring course availability, product restocks, concert ticket sales, and more.

Let monitor worker check the website for you, 24/7!

Project Screenshot

✨ Features

  • πŸ” Two Check Modes:
    • Keyword Mode: Simple keyword matching with AND/OR logic
    • LLM Mode: Let LLM check the website and see if it should notify you.
  • πŸ”” Instant Notifications via Ntfy (free push notifications)
  • 😎 Easy setup with Docker: No email API key or some other API key required.
  • 🐳 Easy Deployment: Single Docker container
  • ⏰ Flexible Scheduling: Check intervals from 1 minute to 24 hours
  • πŸ“Š Full Transparency: All logs visible in the UI, nothing hidden
  • 🌍 Bilingual: Full English and Chinese support

πŸš€ Quick Start

1. Create a Ntfy Topic

  1. Install Ntfy app on your phone or just go to the website
  2. Create / Subscribe to a topic (e.g., my-monitor-alerts)
  3. Remember your topic URL: ntfy.sh/my-monitor-alerts

What is Ntfy? Ntfy is a free, open-source push notification service. You subscribe to a "topic" (like a channel), and anyone who knows the topic name can send you notifications. It's like a public bulletin board - simple but effective for personal use. Also, it's free of charge and doesn't need a public ip address.

2. Deploy with Docker

Create a docker-compose.yml file:

services:
  monitor-worker:
    image: ghcr.io/t41372/monitor-worker-v2:latest
    container_name: monitor-worker-v2
    ports:
      - "8080:8000"
    volumes:
      - ./data:/app/data
    environment:
      - TZ=UTC
    restart: unless-stopped

Then run:

docker compose up -d

3. Access the UI

Open http://localhost:8080 in your browser.

4. Configure

  1. Add Notification Channel: Settings β†’ Notification Channels β†’ Add your Ntfy topic
  2. Add LLM Provider (optional): Settings β†’ LLM Providers β†’ Add your OpenAI/compatible API
  3. Create Project: Projects β†’ New Project β†’ Configure URL, conditions, and interval

πŸ“– Usage Examples

Example 1: Course Availability (Keyword Mode)

Monitor when a course has open seats:

  • URL: https://university.edu/courses/cse333
  • Mode: Keyword
  • Keywords: OPEN, Available, Seats
  • Logic: OR (any match triggers)
  • Match Mode: Found

Example 2: Product Restock (LLM Mode)

Monitor when an Amazon product is back in stock:

  • URL: https://amazon.com/dp/B08XXXXX
  • Mode: LLM
  • Condition: "Notify me when the product is in stock and available for purchase, not just from third-party sellers"
  • Provider: Your configured OpenAI API

Example 3: Concert Tickets (Keyword Mode)

Monitor when tickets become available:

  • URL: https://ticketmaster.com/event/12345
  • Mode: Keyword
  • Keywords: Sold Out
  • Logic: OR
  • Match Mode: Not Found (notify when "Sold Out" disappears)

βš™οΈ Configuration

LLM Providers

Monitor Worker uses the OpenAI-compatible API format. You can use:

  • OpenAI (GPT-4o-mini recommended for cost-efficiency)
  • Azure OpenAI
  • Anthropic (via compatible proxies)
  • Local models via Ollama, LM Studio, etc.

Rate Limiting: Each provider has an independent rate limit (requests per minute). Set this based on your API tier to avoid hitting limits.

Check Intervals

  • Default: 30 minutes
  • Minimum: 1 minute
  • Maximum: 1440 minutes (24 hours)

Consider your use case and API costs when setting intervals. For course registration, 5-10 minutes is usually sufficient.

Crawler Settings

Configure crawl wait behavior in the UI: Settings β†’ Crawler.

Option Description
Wait until networkidle | load | domcontentloaded
Wait for CSS selector or JS condition (e.g., css:.content-loaded or js:() => window.ready === true)
Wait-for timeout Timeout in milliseconds
Extra delay Additional delay before capture (seconds)
Page timeout Overall page timeout (milliseconds)

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Docker Container                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  React Frontend  β”‚  β”‚      FastAPI Backend         β”‚  β”‚
β”‚  β”‚  (Vite + shadcn) β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β”‚                  │──│  β”‚  Scheduler (APScheduler)β”‚ β”‚  β”‚
β”‚  β”‚                  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β”‚                  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β”‚  SSE for logs   │◀─│  β”‚  Crawler (Crawl4AI)    β”‚ β”‚  β”‚
β”‚  β”‚                  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β”‚                  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚  Checker (LLM/Keyword)  β”‚ β”‚  β”‚
β”‚                        β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β”‚  SQLite DB      │◀─│  β”‚  Notifier (Apprise)    β”‚ β”‚  β”‚
β”‚  β”‚  (persistent)   β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ› οΈ Development

Prerequisites

  • Python 3.12+
  • Node.js 20+
  • uv (Python package manager)

Backend

cd backend
uv sync
uv run uvicorn monitor_worker.main:app --reload

Frontend

cd frontend
npm install
npm run dev

Testing

cd backend
uv run pytest --cov=monitor_worker --cov-report=term-missing

Docker Integration Tests

Run the full stack integration tests (app container + mock services):

./scripts/run_docker_integration_tests.sh

Code Quality

cd backend
uv run ruff check .
uv run ruff format .
uv run pyright
uv run bandit -r src

🀝 Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the GNU GPLv3.

About

A simple, self-hosted monitoring tool that checks websites for conditions you defined and sends instant notifications. (Like help you monitor changes of class availability 24/7)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages