This repo is a personal command center for “is anything going sideways near my family?”
Every ten minutes it wakes up, pulls official alerts for your home/work, scrapes your trusted news outlets, lets ChatGPT read the articles for you, and only pings you when a story is both local and urgent. When something matters you get an email plus a Pushover notification (including the loud, repeating “break glass” alert on your phone).
You don’t have to be a developer—think of it as a tailored warning siren you can run locally, in GitHub, or on Google Cloud with a point-and-click dashboard.
| Need | What the system does |
|---|---|
| Weather + quake warnings | Talks to NWS and USGS directly, respects their severity levels, and only wakes you up when it’s serious. |
| Hyperlocal intelligence | Pulls RSS + Google News for your exact cities, counties, and highways. Full text is fed to OpenAI, which classifies relevance/severity. |
| Zero spam | Multi-source confirmation, dedupe, and per-location cooldowns keep chatter out of your inbox. |
| Instant delivery | Email and Pushover (including emergency priority with custom sounds). |
| Command dashboard | Launch python -m scripts.cli dashboard to edit locations, allowlist domains, dry-run, rebuild keywords, and sync the latest CI run—all without touching a terminal. |
- Gather — Official feeds (NWS, USGS, EONET) + curated RSS + live Google News feeds per location. RSS hits are deduped and only from allow-listed publishers.
- Enrich — Every news link is fetched, cleaned, and summarized. Google News redirects are resolved to the real publisher so allowlists work.
- Judge — ChatGPT (via OpenAI API) reads each article and assigns: relevant? category? severity 1–3? It only passes items that mention your city/county/roads.
- Fuse — Surges (news volume), hysterias (multiple sources confirming), and official warnings join forces. This keeps false positives low.
- Alert — When thresholds are met, an alert is written to
data/latest_run.json, logged to SQLite, and sent over email + Pushover. Emergency alerts repeat until acknowledged. - Review — The dashboard can sync the latest cloud run so you see exactly who said what, when, and why it did (or didn’t) notify you.
| Item | Why |
|---|---|
| GitHub repo with Actions enabled | Runs the 10‑minute job for you while you sleep. |
| Secrets in Settings → Actions → Secrets and variables → Actions | OPENAI_API_KEY, NWS_USER_AGENT, GMAIL_USER, GMAIL_APP_PASSWORD, ALERT_EMAIL_TO, PUSHOVER_USER_KEY, PUSHOVER_APP_TOKEN. Optional: GH_BOT_NAME, GH_BOT_EMAIL, GH_PUSH_TOKEN. |
| Pushover + Gmail app password | Outbound notifications. You can swap other providers if you prefer. |
| Location data | config/locations.yaml already includes Harper’s Ferry and Frederick; add your own IDs/lat/lon/roads. |
Tip: use Python 3.11/3.12 so Streamlit/pyarrow wheels install cleanly. On 3.13+, the core CLI still works but the dashboard dependencies are skipped.
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Optional knobs – mirrors the defaults in CI
export LLM_CLASSIFY_NEWS=1
export OPENAI_API_KEY=sk-...
export LLM_MAX_ITEMS=10
export LLM_MAX_CHARS=1000
export LLM_EMIT_ALERTS=1
export LLM_MIN_SEVERITY=3
export LLM_CONFIRM_MIN_SEVERITY=2
export LLM_ALLOW_CATEGORIES="evacuation,hazmat,lockdown,outage,disaster,severe_weather,public_health,crime"
export LLM_EMERGENCY_CATEGORIES="evacuation,hazmat,lockdown,outage,disaster,public_health,crime"
export NEWS_REQUIRE_HAZARD=0 # let the LLM decide what’s relevant
export PUSHOVER_PRIORITY2_SOUND=siren
export PUSHOVER_EMERGENCY_RETRY=30
export PUSHOVER_EMERGENCY_EXPIRE=300
# Build derived keywords + run a dry check
python -m scripts.cli rebuild-keywords
python -m scripts.prepper_alerts --dry-run
# Launch the dashboard
PYTHONPATH=. python -m scripts.cli dashboard --port 8501
# visit http://localhost:8501Useful tooling:
# See what RSS / Google News is returning for a location
PYTHONPATH=. python -m scripts.cli debug-news --location home --limit 10
# Fire a real, end-to-end alert (no code changes needed)
PYTHONPATH=. python -m scripts.cli send-test --priority 2 --sound siren --retry 30 --expire 300The workflow lives at .github/workflows/prepper-alerts.yml and runs every 10 minutes. It now fails fast if any required secret is missing, so check the Actions tab if you stop getting alerts. The job sequence is:
- Validate secrets → fail loudly if any are empty.
- Checkout + install deps.
- Validate configs + rebuild keywords.
- Run the orchestrator (non dry-run) → writes
data/latest_run.jsonanddata/latest_run.meta.json. - Always upload the snapshot artifact (even if no alerts were generated).
- Optionally commit artifacts back if
GH_PUSH_TOKENis provided.
Use the dashboard “CI Sync” panel (with a Personal Access Token) to pull the latest artifact. You can pick a specific run from the dropdown and it shows the run number, commit SHA, and timestamp so you know what dataset you’re browsing.
- Add news sources:
config/settings.yamlalready ships with DC/Baltimore/WV outlets. Add more allowlist domains + RSS feeds for your county; the more local, the better. - Change thresholds: use the dashboard “Thresholds” tab or edit
config/settings.yamlto adjust surge factors, quake magnitudes, etc. - Disable hazard prefilter: Set
NEWS_REQUIRE_HAZARD=0(default in CI) so the LLM sees all stories and decides what’s relevant. Use1only if you need ultra conservative intake. - Test end-to-end:
PYTHONPATH=. python -m scripts.cli send-test --priority 2sends the loud Pushover alert and an email so you can verify device settings.
Want your family to see the dashboard without installing Python? Deploy the Streamlit app to Google Cloud Run with Workspace SSO. Follow the numbered steps below (copied from the original instructions) once you’re happy with your config.
- Prereqs: Install the gcloud SDK, have a Workspace domain + subdomain (e.g.,
alerts.yourdomain.com), and note the entry pointdashboard/app.py. - Create an image:
gcloud builds submit --tag gcr.io/$PROJECT_ID/$SERVICE - Deploy:
gcloud run deploy ... --allow-unauthenticated=falseetc. - Enable IAP & custom domain: follow the commands in the original Cloud Run section.
This gives you a private dashboard on the internet that only Workspace users can access. (Actions continues to run the data collection in the background.)
- Actions says “missing secret”: set the missing key under repo Settings → Actions → Secrets.
- Dashboard shows old providers (newsapi/gdelt/wiki): click “Sync latest CI snapshot” and pick the most recent successful run. If the sidebar warns about legacy providers, you’re still viewing an old artifact.
- No “home” news: expand the allowlist with more Jefferson County/Winchester/Loudoun sources or temporarily set
NEWS_REQUIRE_HAZARD=0while tuning. - NWS error 403: your
NWS_USER_AGENTis blank or not descriptive. Use something likeprepper-alerts/1.0 (you@example.com). - Pushover emergency didn’t repeat: set
PUSHOVER_EMERGENCY_RETRY/EXPIREin env/Actions and make sure Critical Alerts are allowed on your iPhone.
Turn the Streamlit dashboard into a Google Workspace–protected site:
- Prereqs: Install the gcloud SDK, ensure you have a Google Workspace domain plus a subdomain (e.g.,
app.yourdomain.com), and note that the entry point isdashboard/app.py. - Create the container image: A
Dockerfile(already in the repo) builds the Streamlit app for Cloud Run. - Set variables (adjust values to your org):
PROJECT_ID="YOUR_GCP_PROJECT_ID" REGION="us-east1" SERVICE="preppers-dashboard" DOMAIN="app.yourdomain.com" GROUP_EMAIL="dash-viewers@yourdomain.com" SUPPORT_EMAIL="you@yourdomain.com"
- Authenticate + pick project/region:
gcloud auth login gcloud config set project "$PROJECT_ID" gcloud config set run/region "$REGION"
- Enable services (idempotent):
gcloud services enable run.googleapis.com iam.googleapis.com iap.googleapis.com \ secretmanager.googleapis.com cloudbuild.googleapis.com - Build + deploy a private Cloud Run service:
gcloud builds submit --tag gcr.io/$PROJECT_ID/$SERVICE gcloud run deploy "$SERVICE" \ --image gcr.io/$PROJECT_ID/$SERVICE \ --allow-unauthenticated=false \ --region "$REGION" \ --memory 1Gi
- Configure Identity-Aware Proxy (IAP) and Workspace auth:
gcloud iap oauth-brands create \ --application_title="Preppers Dashboard" \ --support_email="$SUPPORT_EMAIL" || true BRAND="$(gcloud iap oauth-brands list --format='value(name)')" gcloud iap oauth-clients create "$BRAND" --display_name="Preppers IAP Client" || true gcloud iap web enable --resource-type=compute gcloud iap web add-iam-policy-binding \ --member="group:$GROUP_EMAIL" \ --role="roles/iap.httpsResourceAccessor"
- Map your custom domain (creates DNS records to add at your registrar):
gcloud run domain-mappings create \ --service "$SERVICE" \ --domain "$DOMAIN" \ --region "$REGION" || true
- Update DNS: Add the records printed by the previous step; once propagated, only members of
$GROUP_EMAILcan reachhttps://$DOMAINvia Workspace SSO.
-
Create a GitHub repository named
preppers-alerts(MIT) and push this project. -
Configure secrets under Settings → Actions → Secrets and variables → Actions (required for CI to run successfully):
OPENAI_API_KEYNWS_USER_AGENTGMAIL_USER,GMAIL_APP_PASSWORD,ALERT_EMAIL_TOPUSHOVER_USER_KEY,PUSHOVER_APP_TOKEN- Optional:
GH_BOT_NAME,GH_BOT_EMAIL,GH_PUSH_TOKEN
-
Install Python 3.11 locally, create a virtual environment, and install dependencies:
python -m venv .venv source .venv/bin/activate pip install -r requirements.txt -
Run the orchestration workflow locally (dry-run first):
# Optional: enable LLM-based filtering of RSS items export LLM_CLASSIFY_NEWS=1 export OPENAI_API_KEY=sk-... # required if enabling LLM export LLM_MAX_ITEMS=10 # cap items per location (optional) export LLM_MAX_CHARS=1000 # cap full-text chars per item (optional) export LLM_EMIT_ALERTS=1 # emit alerts from LLM-accepted items (optional) export LLM_MIN_SEVERITY=3 # only emit alerts for severity >= 3 export LLM_CONFIRM_MIN_SEVERITY=2 # only count toward confirmation if severity >= 2 export LLM_ALLOW_CATEGORIES="evacuation,hazmat,lockdown,outage,disaster,severe_weather,public_health,crime" export LLM_EMERGENCY_CATEGORIES="evacuation,hazmat,lockdown,outage,disaster,public_health,crime" # which LLM categories become Pushover emergency export NEWS_REQUIRE_HAZARD=0 # send everything to the LLM; it will filter by locality + severity export PUSHOVER_PRIORITY2_SOUND=siren # optional critical sound override export PUSHOVER_EMERGENCY_RETRY=30 # retry every 30s for emergency alerts export PUSHOVER_EMERGENCY_EXPIRE=300 # give up after 5 minutes python -m scripts.cli rebuild-keywords python -m scripts.prepper_alerts --dry-run
-
Launch the dashboard for config CRUD/observability:
PYTHONPATH=. python -m scripts.cli dashboard --port 8501 # Then open http://localhost:8501 -
Inspect intake when tuning queries:
PYTHONPATH=. python -m scripts.cli debug-news --location home --limit 10 PYTHONPATH=. python -m scripts.cli debug-news --location work --limit 10
See SECURITY.md for coordinated disclosure guidance.