cd /home/bbl/html/w1hd-tracker

# 0) Optional: remove any stray copies in the web dir so nothing calls the wrong one
sudo rm -f /var/www/html/w1hd-tracker/sendBILL.sh /var/www/html/w1hd-tracker/sendBILL.py 2>/dev/null || true

# 1) Ensure venv + aprslib
python3 -m venv venv
./venv/bin/pip install --upgrade pip aprslib

# 2) Write a clean sendBILL.py (fixed braces + ACK wait)
cat > sendBILL.py <<'PY'
#!/usr/bin/env python3
import os, sys, time, re, threading, random, pathlib, aprslib

# ------------ Config via env ------------
CALLSIGN = os.environ.get("APRS_CALLSIGN", "W1HD")
PASSCODE = os.environ.get("APRS_PASSCODE", "27799")
HOST     = os.environ.get("APRS_HOST", "noam.aprs2.net")
PORT     = int(os.environ.get("APRS_PORT", "14580"))
DST      = os.environ.get("APRS_DST", "W1HD-9")     # recipient

APP_PROG = os.environ.get("APRS_APP_PROG", "w1hd-sender")
APP_VER  = os.environ.get("APRS_APP_VER",  "1.2")
ACK_WAIT_SEC = int(os.environ.get("APRS_ACK_WAIT", "180"))
STATE_FILE   = os.environ.get("APRS_MSGID_FILE",
                              "/home/bbl/html/w1hd-tracker/.msgid")

def ts():
    return time.strftime("%Y-%m-%dT%H:%M:%S%z")

def note(msg):
    print(f"{ts()}  {msg}", flush=True)

def next_msgid(state_path: str) -> int:
    try:
        p = pathlib.Path(state_path)
        n = int(p.read_text().strip()) if p.exists() else random.randint(0, 99)
    except Exception:
        n = random.randint(0, 99)
    n = (n + 1) % 100
    try:
        pathlib.Path(state_path).write_text(f"{n}\n")
    except Exception:
        pass
    return n

def pad9(callsign: str) -> str:
    return (callsign[:9] + " " * 9)[:9]

def build_raw(src: str, dst: str, text: str, msgid: int) -> str:
    # payload format: :<addressee-9>:<text>{NN   (no closing brace)
    payload = f":{pad9(dst)}:{text}" + "{" + f"{msgid:02d}"
    return f"{src}>APRS,TCPIP*:{payload}\r\n"

def send_message(text: str, msgid: int) -> str:
    rawline = build_raw(CALLSIGN, DST, text, msgid)
    note(f"CONNECT (TX) → {HOST}:{PORT} as {CALLSIGN} (vers {APP_PROG} {APP_VER})")
    tx = aprslib.IS(CALLSIGN, passwd=PASSCODE, host=HOST, port=PORT)
    tx.appid = f"{APP_PROG} {APP_VER}"
    tx.connect()
    note("LOGIN OK; sending APRS message …")
    tx.sendall(rawline)
    try:
        tx.disconnect()
    except Exception:
        pass
    note(f"TX OK → {DST}  id={msgid:02d}")
    print("RAW:", rawline.strip(), flush=True)
    return rawline

def wait_for_ack(msgid: int, timeout_sec: int) -> bool:
    target = f"ack{msgid:02d}"
    got_ack = threading.Event()
    msg_re = re.compile(r":(.{9}):(.*)")

    def handler(pkt):
        line = pkt.decode("utf-8", "ignore") if isinstance(pkt, bytes) else str(pkt)
        if line.startswith("#") or ":" not in line or ">" not in line:
            return
        m = msg_re.search(line)
        if not m:
            return
        addressee = m.group(1).rstrip()
        content   = m.group(2).strip()
        if addressee.upper() == CALLSIGN.upper():
            note(f"RX → {CALLSIGN}: {content}")
            if content.lower() == target.lower():
                got_ack.set()

    def rx_thread():
        try:
            rx = aprslib.IS(CALLSIGN, passwd=PASSCODE, host=HOST, port=PORT)
            rx.appid = f"{APP_PROG} {APP_VER}"
            rx.set_filter(f"m/{CALLSIGN}")
            note(f"CONNECT (RX) → {HOST}:{PORT} with filter m/{CALLSIGN}")
            rx.connect()
            rx.consumer(handler, raw=True)
        except Exception as e:
            note(f"(ack listener note) {e}")

    t = threading.Thread(target=rx_thread, daemon=True)
    t.start()

    start = time.time()
    spinner = "|/-\\"
    i = 0
    while time.time() - start < timeout_sec:
        if got_ack.is_set():
            note(f"ACK RECEIVED: ack{msgid:02d}")
            return True
        left = int(timeout_sec - (time.time() - start))
        sys.stdout.write(f"\rWaiting for ACK (ack{msgid:02d}) {spinner[i%4]}  {left:3d}s ")
        sys.stdout.flush()
        i += 1
        time.sleep(0.25)
    sys.stdout.write("\n")
    return False

def main():
    text = " ".join(sys.argv[1:]).strip() if len(sys.argv) > 1 else "Hello from Hal — please ACK me!"
    msgid = next_msgid(STATE_FILE)

    note(f"SENDING → {DST}  msg='{text}'  id={msgid:02d}")
    try:
        send_message(text, msgid)
    except Exception as e:
        note(f"TX ERROR: {e}")
        sys.exit(2)

    note(f"Waiting up to {ACK_WAIT_SEC}s for ack{msgid:02d} (messages to {CALLSIGN}) …")
    got = wait_for_ack(msgid, ACK_WAIT_SEC)
    if got:
        note("✅ DELIVERED (peer ACKed)")
        sys.exit(0)
    else:
        note("⌛ NO ACK (timeout). Could still be delivered (peer offline / no-ACK client / RF-only).")
        sys.exit(1)

if __name__ == "__main__":
    main()
PY
chmod +x sendBILL.py

# 3) Write a tiny *shell* wrapper (this one is NOT Python)
cat > sendBILL.sh <<'SH'
#!/usr/bin/env bash
set -euo pipefail
cd /home/bbl/html/w1hd-tracker
exec ./venv/bin/python ./sendBILL.py "$@"
SH
chmod +x sendBILL.sh
