AtAnyRate/ARCHITECTURE.md
ilia 1a7298f755 Initial commit: EventRate pipeline, fuzzy dedup, Airbnb retries
Wire up Ticketmaster, SeatGeek, Telegram, scoring, Playwright stubs.
Deduplicate events with fuzzy venue/name matching. Retry calendar
updates on transient failures. Backlog tasks marked complete.

Made-with: Cursor
2026-04-04 12:31:53 -04:00

4.3 KiB

Architecture

High-level flow

┌──────────────┐     ┌──────────────┐
│ Ticketmaster │     │   SeatGeek   │
│   Provider   │     │   Provider   │
└──────┬───────┘     └──────┬───────┘
       │                    │
       └────────┬───────────┘
                ▼
        ┌───────────────┐
        │  Normalize &   │
        │  Deduplicate   │
        └───────┬───────┘
                ▼
        ┌───────────────┐
        │  Impact Score  │
        └───────┬───────┘
                ▼
        ┌───────────────┐
        │   Filter by    │
        │  date window   │
        └───────┬───────┘
                │
        ┌───────┴───────┐
        ▼               ▼
┌──────────────┐ ┌──────────────┐
│   Telegram   │ │   Airbnb     │
│   Alert      │ │   Calendar   │
│  (primary)   │ │  (optional)  │
└──────────────┘ └──────────────┘

Module map

src/
├── main.py                 # CLI entrypoint, orchestration
├── config.py               # Pydantic settings from env vars
├── models.py               # NormalizedEvent dataclass
├── log.py                  # Structured logging configuration
├── dedup.py                # Deduplication across providers
├── providers/
│   ├── base.py             # EventProvider abstract base class
│   ├── ticketmaster.py     # Ticketmaster Discovery API
│   └── seatgeek.py         # SeatGeek API
├── scoring/
│   └── impact.py           # Rule-based impact scoring
├── notifications/
│   └── telegram.py         # Telegram bot message sender
└── airbnb/
    ├── auth.py             # Playwright storage state management
    └── calendar.py         # Calendar navigation and price updates

Key design decisions

1. Provider abstraction

All event providers implement EventProvider.fetch() -> list[NormalizedEvent]. This makes it trivial to add new sources (Eventbrite, PredictHQ, scraping) without touching the orchestration layer.

2. Normalized event model

A single NormalizedEvent dataclass acts as the shared contract between providers, deduplication, scoring, and output formatting. Fields: name, date, venue, source, url, raw metadata.

3. Airbnb automation is isolated and optional

The src/airbnb/ module is completely decoupled from event ingestion. If Playwright breaks (selectors change, login expires), the system degrades gracefully to Telegram-only alerts. The main runner catches all Airbnb errors and logs them without crashing.

4. Storage state for auth

Airbnb authentication uses Playwright's storage_state API. A one-time manual login script saves cookies/localStorage to state.json. Subsequent headless runs load this state. No passwords are stored in code or env vars.

5. Configuration via environment

All secrets and tunables live in env vars (loaded from .env in local dev). No config files to manage, easy to override in Docker/cron.

6. No database

For weekly runs processing dozens of events, in-memory processing is sufficient. If persistence becomes necessary (e.g., tracking price change history), a simple JSON file or SQLite would be the first step.

Error handling strategy

  • Each provider's fetch() is wrapped in try/except; one failing source does not block others.
  • Telegram send failures are logged but do not block Airbnb updates.
  • Airbnb automation failures are logged and reported via Telegram if possible.
  • The main runner returns a nonzero exit code if all providers fail.

Future extension points

  • New providers: subclass EventProvider
  • New notification channels: add modules under notifications/
  • Smarter scoring: replace scoring/impact.py internals
  • Multi-listing: extend config and loop in main.py