feat: initial flight monitor
- Python flight price monitor for BER↔EZE and other routes - Adapter pattern for extensibility (Kiwi/Tequila implemented) - OpenClaw alerting integration - Cron-friendly one-shot execution - Full logging of all checks - Graceful handling when API key not set Implements monkey-island/flight-monitor#2
This commit is contained in:
206
README.md
206
README.md
@@ -1,3 +1,205 @@
|
||||
# flight-monitor
|
||||
# Flight Price Monitor
|
||||
|
||||
Flight price monitor — alerts when BER→EZE drops below threshold
|
||||
Daily flight price monitor that checks configured routes and alerts when prices drop below threshold.
|
||||
|
||||
## Features
|
||||
|
||||
- ✈️ Monitor multiple flight routes (BER→EZE, etc.)
|
||||
- 🔔 OpenClaw alerts when prices drop below threshold
|
||||
- 📊 Extensible adapter pattern for multiple price sources
|
||||
- 📝 Full logging of all checks (hits and misses)
|
||||
- 🔧 Cron-friendly one-shot execution
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. Get a Kiwi API Key
|
||||
|
||||
1. Sign up at https://tequila.kiwi.com/portal/login
|
||||
2. Create an API key
|
||||
3. Set environment variable:
|
||||
|
||||
```bash
|
||||
export KIWI_API_KEY="your-api-key-here"
|
||||
```
|
||||
|
||||
Or create a `.env` file:
|
||||
|
||||
```bash
|
||||
KIWI_API_KEY=your-api-key-here
|
||||
```
|
||||
|
||||
### 3. Configure Routes
|
||||
|
||||
Edit `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"origin": "BER",
|
||||
"destination": "EZE",
|
||||
"trip_type": "round_trip",
|
||||
"threshold_eur": 700,
|
||||
"duration_weeks": [2, 3, 4],
|
||||
"preferred_months": [12, 1, 2, 3],
|
||||
"monitor_year_round": true
|
||||
}
|
||||
],
|
||||
"check_interval": "daily"
|
||||
}
|
||||
```
|
||||
|
||||
**Config Fields:**
|
||||
|
||||
- `origin`, `destination`: IATA airport codes
|
||||
- `threshold_eur`: Alert if price drops below this (in EUR)
|
||||
- `duration_weeks`: Trip lengths to search (in weeks)
|
||||
- `preferred_months`: Months to prioritize (1-12)
|
||||
- `monitor_year_round`: `true` to search all months, `false` to only search `preferred_months`
|
||||
|
||||
You can add multiple routes — the monitor will check them all.
|
||||
|
||||
### 4. Run
|
||||
|
||||
```bash
|
||||
python monitor.py
|
||||
```
|
||||
|
||||
Logs are written to `monitor.log` and stdout.
|
||||
|
||||
## Scheduling (Cron)
|
||||
|
||||
To run daily at 6 AM:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
Add:
|
||||
|
||||
```
|
||||
0 6 * * * cd /path/to/flight-monitor && /usr/bin/python3 monitor.py
|
||||
```
|
||||
|
||||
Or use OpenClaw cron (recommended):
|
||||
|
||||
```bash
|
||||
openclaw cron add \
|
||||
--schedule '0 6 * * *' \
|
||||
--command 'cd /path/to/flight-monitor && python monitor.py' \
|
||||
--name 'Flight Price Monitor'
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Adapter Pattern
|
||||
|
||||
The monitor uses an adapter pattern to support multiple price sources:
|
||||
|
||||
```
|
||||
adapters/
|
||||
base.py # Abstract FlightAdapter interface
|
||||
kiwi.py # Kiwi/Tequila API implementation
|
||||
```
|
||||
|
||||
Each adapter implements:
|
||||
|
||||
- `search_round_trip()` — returns normalized results
|
||||
- `get_name()` — adapter name
|
||||
|
||||
All results follow this schema:
|
||||
|
||||
```python
|
||||
{
|
||||
"price_eur": float,
|
||||
"airline": str,
|
||||
"departure": datetime,
|
||||
"return": datetime,
|
||||
"link": str, # Booking URL
|
||||
"source": str # Adapter name
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a New Adapter
|
||||
|
||||
1. Create `adapters/my_adapter.py`:
|
||||
|
||||
```python
|
||||
from adapters.base import FlightAdapter
|
||||
|
||||
class MyAdapter(FlightAdapter):
|
||||
def get_name(self) -> str:
|
||||
return "MySource"
|
||||
|
||||
def search_round_trip(self, origin, destination, departure_date, return_date, **kwargs):
|
||||
# Your implementation here
|
||||
# Must return list of normalized results (see schema above)
|
||||
pass
|
||||
```
|
||||
|
||||
2. Register in `adapters/__init__.py`:
|
||||
|
||||
```python
|
||||
from adapters.my_adapter import MyAdapter
|
||||
__all__ = [..., "MyAdapter"]
|
||||
```
|
||||
|
||||
3. Update `monitor.py` to use your adapter:
|
||||
|
||||
```python
|
||||
adapter = MyAdapter()
|
||||
```
|
||||
|
||||
You can also make the adapter configurable via `config.json` if needed.
|
||||
|
||||
## Alerting
|
||||
|
||||
When a price drop is detected, the monitor sends an alert via OpenClaw:
|
||||
|
||||
```bash
|
||||
openclaw system event --text "✈️ BER→EZE €650 on 2026-04-15 → 2026-04-29 (Lufthansa) — below €700! https://..." --mode now
|
||||
```
|
||||
|
||||
This routes the alert to your configured OpenClaw channels (Telegram, Discord, Signal, etc.).
|
||||
|
||||
## Logs
|
||||
|
||||
All checks are logged to `monitor.log` with timestamps:
|
||||
|
||||
```
|
||||
2026-03-21 18:00:00 [INFO] === Flight Monitor Starting ===
|
||||
2026-03-21 18:00:00 [INFO] Checking route BER→EZE (threshold: €700)
|
||||
2026-03-21 18:00:01 [INFO] 2026-04-15 → 2026-04-29: €850 (Lufthansa)
|
||||
2026-03-21 18:00:02 [INFO] 2026-05-01 → 2026-05-15: €620 (Air France)
|
||||
2026-03-21 18:00:02 [INFO] Alert sent via OpenClaw
|
||||
2026-03-21 18:00:03 [INFO] === Flight Monitor Complete ===
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"KIWI_API_KEY environment variable not set"**
|
||||
|
||||
- Set the `KIWI_API_KEY` env var (see Setup step 2)
|
||||
- The monitor will exit gracefully (not crash) if the key is missing
|
||||
|
||||
**No results returned**
|
||||
|
||||
- Check `monitor.log` for API errors
|
||||
- Verify your IATA codes are correct (e.g., "BER" not "Berlin")
|
||||
- Try a broader date range (increase `duration_weeks` or `preferred_months`)
|
||||
|
||||
**OpenClaw alert not sent**
|
||||
|
||||
- Verify `openclaw` CLI is in PATH
|
||||
- Check OpenClaw is running: `openclaw status`
|
||||
- Look for error messages in `monitor.log`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user