Initial commit: Lunch Notifier source from server

This commit is contained in:
mattie726 2026-08-25 22:53:07 -07:00 committed by Barely Removable
commit b568287528
9 changed files with 878 additions and 0 deletions

115
scheduler.py Normal file
View file

@ -0,0 +1,115 @@
"""
Runs scraper.py on a schedule inside the container.
HTTP trigger server on PORT (default 2323):
GET / health check / status
POST /send trigger scraper immediately
Env vars:
PHONE_NUMBER destination phone (required)
TEXTBELT_KEY textbelt key (default: "textbelt" free tier)
RUN_TIME HH:MM 24h time to fire daily (default: 07:00)
RUN_DAYS comma-sep days: mon,tue,wed,thu,fri (default)
PORT HTTP port (default: 2323)
"""
import logging
import os
import threading
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
import schedule
from scraper import main as run_scraper # now a plain sync function
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger(__name__)
RUN_TIME = os.environ.get("RUN_TIME", "07:00")
RUN_DAYS = [d.strip().lower() for d in os.environ.get("RUN_DAYS", "mon,tue,wed,thu,fri").split(",")]
PORT = int(os.environ.get("PORT", "2323"))
DAY_MAP = {
"mon": schedule.every().monday,
"tue": schedule.every().tuesday,
"wed": schedule.every().wednesday,
"thu": schedule.every().thursday,
"fri": schedule.every().friday,
"sat": schedule.every().saturday,
"sun": schedule.every().sunday,
}
_last_run: str = "never"
_last_result: str = "n/a"
def job():
global _last_run, _last_result
log.info("Running scheduled lunch menu check...")
try:
run_scraper()
_last_result = "ok"
except SystemExit:
_last_result = "sms_failed"
except Exception as e:
log.exception("Scraper failed")
_last_result = f"error: {e}"
_last_run = time.strftime("%Y-%m-%d %H:%M:%S")
class Handler(BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
log.info("HTTP %s", fmt % args)
def _respond(self, code: int, body: str):
data = body.encode()
self.send_response(code)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if self.path == "/":
status = (
f"lunch-notifier running\n"
f"schedule : {', '.join(RUN_DAYS)} at {RUN_TIME}\n"
f"phone : {os.environ.get('PHONE_NUMBER', 'NOT SET')}\n"
f"last run : {_last_run}\n"
f"last result: {_last_result}\n\n"
f"POST /send to trigger immediately"
)
self._respond(200, status)
else:
self._respond(404, "not found")
def do_POST(self):
if self.path == "/send":
log.info("Manual trigger via HTTP POST /send")
threading.Thread(target=job, daemon=True).start()
self._respond(200, "Triggered — check container logs for result.")
else:
self._respond(404, "not found")
def start_http_server():
server = HTTPServer(("0.0.0.0", PORT), Handler)
log.info(f"HTTP trigger server listening on port {PORT}")
server.serve_forever()
# ── Schedule setup ────────────────────────────────────────────────────────────
for day in RUN_DAYS:
if day in DAY_MAP:
DAY_MAP[day].at(RUN_TIME).do(job)
log.info(f"Scheduled: {day} at {RUN_TIME}")
else:
log.warning(f"Unknown day '{day}', skipping.")
log.info(f"Phone: {os.environ.get('PHONE_NUMBER', 'NOT SET')}")
threading.Thread(target=start_http_server, daemon=True).start()
while True:
schedule.run_pending()
time.sleep(30)