|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Headless LXST voice caller — dials the iOS Columba sim's telephony |
| 3 | +destination so its incoming-call / CallKit path fires, for debugging the |
| 4 | +"call screen dismisses + reappears as Unknown" bug on the sim (full log access). |
| 5 | +
|
| 6 | +Uses Sideband's own ReticulumTelephone (which wraps LXST.Telephone), so it's |
| 7 | +wire-identical to a Sideband user placing the call. Joins the host's shared |
| 8 | +RNS instance (lxmd) exactly like the interop peer, so it shares Columba's |
| 9 | +transport. |
| 10 | +
|
| 11 | +Usage: |
| 12 | + PYTHONPATH unneeded — paths injected below. |
| 13 | + python3 voice_caller.py <columba_identity_hex> [ring_seconds] |
| 14 | +""" |
| 15 | +import sys, os, time |
| 16 | + |
| 17 | +# LXST + Sideband are sibling checkouts; override with LXST_SRC / SIDEBAND_SRC |
| 18 | +# (SIDEBAND_SRC matches the interop conftest). Default to ~/repos/<name>. |
| 19 | +for _name, _env, _default in ( |
| 20 | + ("LXST", "LXST_SRC", "~/repos/LXST"), |
| 21 | + ("Sideband", "SIDEBAND_SRC", "~/repos/Sideband"), |
| 22 | +): |
| 23 | + _path = os.environ.get(_env, os.path.expanduser(_default)) |
| 24 | + if not os.path.isdir(_path): |
| 25 | + sys.exit(f"{_name} checkout not found at {_path} — set {_env} to its path.") |
| 26 | + sys.path.insert(0, _path) |
| 27 | + |
| 28 | +import RNS # noqa: E402 |
| 29 | +from sbapp.sideband.voice import ReticulumTelephone # noqa: E402 |
| 30 | + |
| 31 | +COLUMBA_ID_HEX = sys.argv[1] if len(sys.argv) > 1 else "695f23533fe3547183f1b550d8552ae8" |
| 32 | +RING_SECONDS = int(sys.argv[2]) if len(sys.argv) > 2 else 25 |
| 33 | + |
| 34 | + |
| 35 | +def main(): |
| 36 | + RNS.Reticulum() # connect to the shared lxmd instance (share_instance=Yes) |
| 37 | + identity = RNS.Identity() |
| 38 | + print(f"[caller] my identity={RNS.prettyhexrep(identity.hash)}", flush=True) |
| 39 | + |
| 40 | + phone = ReticulumTelephone(identity, owner=None) |
| 41 | + # NB: don't call phone.start() — its run() loop is Sideband hw-state polling |
| 42 | + # and isn't present on this build; the wrapped LXST.Telephone drives the |
| 43 | + # call protocol itself. We just need to keep the process alive. |
| 44 | + time.sleep(1.0) |
| 45 | + |
| 46 | + phone.announce() # so Columba can resolve/path us (matches the 'correct name' case) |
| 47 | + print("[caller] announced own telephony", flush=True) |
| 48 | + |
| 49 | + id_bytes = bytes.fromhex(COLUMBA_ID_HEX) |
| 50 | + tel_dest = RNS.Destination.hash_from_name_and_identity("lxst.telephony", id_bytes) |
| 51 | + print(f"[caller] Columba telephony dest = {tel_dest.hex()}", flush=True) |
| 52 | + |
| 53 | + if not RNS.Transport.has_path(tel_dest): |
| 54 | + RNS.Transport.request_path(tel_dest) |
| 55 | + deadline = time.time() + 30 |
| 56 | + while not RNS.Transport.has_path(tel_dest) and time.time() < deadline: |
| 57 | + time.sleep(0.5) |
| 58 | + if not RNS.Transport.has_path(tel_dest): |
| 59 | + print("[caller] NO PATH to Columba telephony after 30s — is the sim " |
| 60 | + "announcing lxst.telephony + bridged through lxmd? aborting.", flush=True) |
| 61 | + return 1 |
| 62 | + print(f"[caller] path to telephony resolved ({RNS.Transport.hops_to(tel_dest)} hops); DIALING", flush=True) |
| 63 | + |
| 64 | + result = phone.dial(id_bytes) |
| 65 | + print(f"[caller] dial() -> {result}", flush=True) |
| 66 | + |
| 67 | + # state: 0=AVAILABLE 1=CONNECTING 2=RINGING 3=IN_CALL (is_* are @property, no parens) |
| 68 | + _names = {0: "AVAILABLE", 1: "CONNECTING", 2: "RINGING", 3: "IN_CALL"} |
| 69 | + for i in range(RING_SECONDS): |
| 70 | + st = phone.state |
| 71 | + print(f"[caller] t={i:>2}s state={st}({_names.get(st, '?')})", flush=True) |
| 72 | + time.sleep(1.0) |
| 73 | + |
| 74 | + print("[caller] hanging up", flush=True) |
| 75 | + try: |
| 76 | + phone.hangup() |
| 77 | + except Exception as e: |
| 78 | + print(f"[caller] hangup error: {e}", flush=True) |
| 79 | + time.sleep(2.0) |
| 80 | + return 0 |
| 81 | + |
| 82 | + |
| 83 | +if __name__ == "__main__": |
| 84 | + sys.exit(main()) |
0 commit comments