lunch-notifier/scheduler.py

115 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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)