115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
"""
|
||
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)
|