🚀 Initial commit: BSN Chatbot — Multi-Channel Intake System

WhatsApp webhook, admin dashboard, public frontend, media handling, LLM auto-summarize, hall/stand detection, iOS-style backend
This commit is contained in:
Hermes Agent
2026-06-17 13:22:19 +02:00
commit bbefb8807b
6 changed files with 2191 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""
BSN Intake Supervisor — starts Flask app + Cloudflare tunnel.
Keeps both alive, restarts on crash, prints the public URL.
"""
import subprocess
import time
import os
import sys
import re
BASE_DIR = "/home/hermes/workspace/bsn-chatbot"
VENV_PYTHON = "/home/hermes/venv/bin/python3"
CLOUDFLARED = "/home/hermes/.local/bin/cloudflared"
# Load .env
env = os.environ.copy()
env_file = os.path.join(BASE_DIR, ".env")
if os.path.exists(env_file):
with open(env_file) as f:
for line in f:
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, _, val = line.partition("=")
key = key.strip()
val = val.strip().strip('"').strip("'")
env[key] = val
def start_flask():
"""Start the Flask app as a subprocess."""
proc = subprocess.Popen(
[VENV_PYTHON, os.path.join(BASE_DIR, "app.py")],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
return proc
def start_cloudflared():
"""Start the Cloudflare tunnel."""
proc = subprocess.Popen(
[CLOUDFLARED, "tunnel", "--url", "http://127.0.0.1:5002"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
return proc
def main():
LOG = open("/tmp/bsn_supervisor.log", "w")
def log(msg):
LOG.write(msg + "\n")
LOG.flush()
log("🚀 BSN Intake Supervisor starting...")
# Start Flask
flask = start_flask()
log("[flask] started")
time.sleep(2)
# Verify Flask is up
import urllib.request
try:
urllib.request.urlopen("http://127.0.0.1:5002/health", timeout=5)
log("[flask] health check: OK")
except Exception as e:
log(f"[flask] health check FAILED: {e}")
# Start Cloudflared
cf = start_cloudflared()
log("[cloudflared] started")
# Wait for tunnel URL to appear in output
tunnel_url = None
deadline = time.time() + 20
buffer = ""
while time.time() < deadline:
line = cf.stdout.readline() if cf.stdout else ""
if line:
buffer += line
m = re.search(r"(https://[a-z-]+\.trycloudflare\.com)", buffer)
if m:
tunnel_url = m.group(1)
break
if flask.poll() is not None:
log(f"[flask] CRASHED! exit={flask.returncode}")
break
if cf.poll() is not None:
log(f"[cloudflared] CRASHED! exit={cf.returncode}")
break
if tunnel_url:
log(f"\n✅ TUNNEL: {tunnel_url}")
log(f" Webhook: {tunnel_url}/whatsapp/webhook")
log(f" Admin: {tunnel_url}/admin")
else:
log("❌ Failed to get tunnel URL")
log("\n[supervisor] Running. Press Ctrl+C to stop.\n")
# Keep running, restart children if they die
try:
while True:
if flask.poll() is not None:
log("[flask] died, restarting...")
flask = start_flask()
if cf.poll() is not None:
log("[cloudflared] died, restarting...")
cf = start_cloudflared()
time.sleep(5)
except KeyboardInterrupt:
log("\n[supervisor] Shutting down...")
flask.terminate()
cf.terminate()
flask.wait()
cf.wait()
if __name__ == "__main__":
main()