""" Watch TelecomKz analytics events fire in real time while you tap the phone. This is the fastest way to learn a button's true event key: the Amplitude export and the ClickHouse stream lag by an hour or more, but the app sends the event the instant you tap, and this reads it off the wire. py tools/monitor_live_events.py --seconds 120 py tools/monitor_live_events.py --seconds 120 --map main_dashboard With --map, every observed key is written onto the hotspot whose caption matched the tap, with keyConfidence "observed" - the only confidence level in this project that means the key is real rather than derived from a button label. """ import argparse import json import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) import telecom_cdp as T from amplitude_hook import DRAIN_JS, INSTALL_HOOK_JS def stamp_observed(screen_id, pairs): """Write observed event keys onto matching hotspots of one screen.""" if not pairs: return 0 app_map = T.load_app_map() catalog = T.load_metrics_catalog() source = T.metrics_source() updated = 0 for screen in app_map.get("screens", []): if screen["id"] != screen_id: continue for hs in screen.get("hotspots", []): label = (hs.get("label") or "").strip().lower() if not label: continue for text, key, _via in pairs: if text and (text in label or label in text): hs["eventKey"] = key hs["keyConfidence"] = "observed" T.attach_metrics(hs, catalog, source) updated += 1 break if updated: T.save_app_map(app_map) return updated def monitor(seconds, map_screen_id=None, poll=0.6): device, target = T.connect() print("Connected to " + str(target.get("url"))) with T.CdpSession(target["webSocketDebuggerUrl"]) as session: hook = json.loads(session.evaluate("JSON.stringify(" + INSTALL_HOOK_JS + ")")) print("Hooked transports: " + ", ".join(hook.get("transports") or ["(none)"])) if hook.get("already"): print("(hook was already installed from an earlier run)") session.evaluate(DRAIN_JS) print("\nTap buttons on the phone now. Listening for " + str(seconds) + "s...\n") deadline = time.time() + seconds pending_click = None pairs = [] seen_keys = {} while time.time() < deadline: time.sleep(poll) try: batch = session.evaluate(DRAIN_JS) or [] except T.DeviceError as exc: # Keep going to the summary: events already observed are the whole # point of the run and must not be thrown away with the connection. print("\nLost the page: " + str(exc)) print("Reporting what was captured before the disconnect.") break for item in batch: kind = item.get("kind") if kind == "click": pending_click = (item.get("text") or "").strip() print(" [tap] " + (pending_click or "(no caption)") + " route=" + str(item.get("route"))) elif kind in ("amplitude", "connector", "metrika-goal"): key = item.get("eventType") if not key: continue tag = {"amplitude": "AMPLITUDE", "connector": "CONNECTOR", "metrika-goal": "metrika "}[kind] print(" [" + tag + "] " + key + (" <- " + pending_click if pending_click else "")) seen_keys[key] = seen_keys.get(key, 0) + 1 # Amplitude's own payload is authoritative. The Metrika goal name # is identical to event_type (verified on matching taps), so it is # accepted as a fallback when the Amplitude body cannot be read. if kind in ("amplitude", "connector", "metrika-goal") and pending_click: pairs.append((pending_click.lower(), key, kind)) pending_click = None print("\n--- summary ---") if not seen_keys: print("No analytics events observed. Did you tap anything on the phone?") for key, count in sorted(seen_keys.items(), key=lambda kv: -kv[1]): print(" " + str(count) + "x " + key) print("\nTap -> event pairs: " + str(len(pairs))) for text, key, via in pairs: print(" " + key + " <- " + text + " [" + via + "]") if map_screen_id and pairs: updated = stamp_observed(map_screen_id, pairs) print("\nWrote " + str(updated) + " observed keys into screen " + map_screen_id + ".") elif map_screen_id: print("\nNothing to write into " + map_screen_id + ".") def main(): parser = argparse.ArgumentParser(description="Live analytics event monitor for TelecomKz.") parser.add_argument("--seconds", type=int, default=60) parser.add_argument( "--map", dest="map_screen_id", default=None, help="Write observed keys onto this screen's hotspots in the app map", ) args = parser.parse_args() try: monitor(args.seconds, args.map_screen_id) except T.DeviceError as exc: print("Error: " + str(exc)) return 1 except KeyboardInterrupt: print("\nStopped.") return 0 if __name__ == "__main__": sys.exit(main())