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

97 lines
4.3 KiB
Markdown

# 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`