Default to America/Phoenix and email-alert once when the session expires
All checks were successful
Deploy / deploy (push) Successful in 25s

Phoenix doesn't observe DST, so schedule times stay meaningful
year-round without needing a seasonal adjustment.

notify.py reuses lunch-notifier's existing Gmail-SMTP-with-app-password
pattern rather than adding a new notification channel. The alert fires
exactly once per expiry (not on every failed scheduled run while it's
down) via a dedupe flag in notify_state.json, reset the moment a fresh
session is successfully imported.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
This commit is contained in:
Barely Removable 2026-08-31 20:20:06 -07:00
parent 48d8bac332
commit 3b08af7938
7 changed files with 111 additions and 10 deletions

34
notify.py Normal file
View file

@ -0,0 +1,34 @@
"""Email alerts via Gmail SMTP + app password — the same pattern already
used by lunch-notifier on this box, reused here rather than adding a new
notification service. Silently a no-op if GMAIL_USER/GMAIL_APP_PASSWORD
aren't set, so the alert feature is opt-in.
"""
import logging
import os
import smtplib
from email.mime.text import MIMEText
logger = logging.getLogger("massprice.notify")
GMAIL_USER = os.environ.get("GMAIL_USER")
GMAIL_APP_PASSWORD = os.environ.get("GMAIL_APP_PASSWORD")
ALERT_EMAIL = os.environ.get("ALERT_EMAIL") or GMAIL_USER
def send_alert(subject, body):
"""Best-effort: logs and returns False on any failure rather than
raising, since a broken alert channel shouldn't break automation."""
if not GMAIL_USER or not GMAIL_APP_PASSWORD or not ALERT_EMAIL:
return False
msg = MIMEText(body)
msg["Subject"] = subject
msg["From"] = GMAIL_USER
msg["To"] = ALERT_EMAIL
try:
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(GMAIL_USER, GMAIL_APP_PASSWORD)
server.sendmail(GMAIL_USER, [ALERT_EMAIL], msg.as_string())
return True
except Exception:
logger.exception("Failed to send alert email")
return False