135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Deliver a completed edge task through the existing guarded WeChat sender."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import socket
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
|
|
HOME = Path.home()
|
|
ADAPTIVE_RECORDER = HOME / ".local" / "lib" / "wechat-health-watch" / "wechat_adaptive_recorder.py"
|
|
GUI_BRIDGE = HOME / ".local" / "lib" / "wechat-health-watch" / "wechat_adaptive_gui_bridge.py"
|
|
XDG_RUNTIME_DIR = Path(os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"))
|
|
GUI_SENDER_SOCKET = Path(
|
|
os.environ.get(
|
|
"JWEBOT_GUI_SENDER_SOCKET",
|
|
str(XDG_RUNTIME_DIR / "jwebot-gui-sender.sock"),
|
|
)
|
|
).expanduser()
|
|
GUI_SENDER_TIMEOUT_SECONDS = float(
|
|
os.environ.get("JWEBOT_GUI_SENDER_TIMEOUT_SECONDS", "55")
|
|
)
|
|
GUI_SENDER_MAX_PAYLOAD_BYTES = 65536
|
|
|
|
|
|
def load_adaptive_recorder():
|
|
spec = importlib.util.spec_from_file_location(
|
|
"jwebot_edge_adaptive_recorder",
|
|
ADAPTIVE_RECORDER,
|
|
)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError("adaptive recorder is unavailable")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def submit_gui_sender(payload: dict[str, str]) -> dict[str, object]:
|
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as connection:
|
|
connection.settimeout(GUI_SENDER_TIMEOUT_SECONDS)
|
|
connection.connect(str(GUI_SENDER_SOCKET))
|
|
connection.sendall((json.dumps(payload, ensure_ascii=False) + "\n").encode())
|
|
chunks: list[bytes] = []
|
|
size = 0
|
|
while True:
|
|
chunk = connection.recv(4096)
|
|
if not chunk:
|
|
break
|
|
chunks.append(chunk)
|
|
size += len(chunk)
|
|
if b"\n" in chunk:
|
|
break
|
|
if size > GUI_SENDER_MAX_PAYLOAD_BYTES:
|
|
raise RuntimeError("GUI sender response is too large")
|
|
try:
|
|
response = json.loads(b"".join(chunks).split(b"\n", 1)[0])
|
|
except (IndexError, json.JSONDecodeError, UnicodeDecodeError) as error:
|
|
raise RuntimeError("GUI sender response is invalid") from error
|
|
if not isinstance(response, dict) or response.get("ok") is not True:
|
|
raise RuntimeError("GUI sender returned an error")
|
|
return response
|
|
|
|
|
|
def main() -> int:
|
|
payload = json.load(sys.stdin)
|
|
if not isinstance(payload, dict):
|
|
raise RuntimeError("delivery payload must be an object")
|
|
expected_group = str(payload.get("group_fingerprint", ""))
|
|
reply = str(payload.get("reply", ""))
|
|
event_id = str(payload.get("event_id", ""))
|
|
if not expected_group:
|
|
raise RuntimeError("missing expected group fingerprint")
|
|
if not reply or len(reply) > 900:
|
|
raise RuntimeError("invalid delivery reply")
|
|
|
|
socket_payload = {
|
|
"op": "send",
|
|
"event_id": event_id,
|
|
"group_fingerprint": expected_group,
|
|
"reply": reply,
|
|
"source": "delivery-helper",
|
|
}
|
|
if GUI_SENDER_SOCKET.exists():
|
|
try:
|
|
submit_gui_sender(socket_payload)
|
|
return 0
|
|
except (FileNotFoundError, ConnectionRefusedError):
|
|
pass
|
|
|
|
adaptive = load_adaptive_recorder()
|
|
started = time.monotonic()
|
|
upstream = adaptive.load_upstream()
|
|
adaptive.configure_state(upstream)
|
|
original_capture = upstream.capture_image
|
|
|
|
def capture_with_current_layout():
|
|
adaptive.configure_layout(upstream)
|
|
return original_capture()
|
|
|
|
upstream.capture_image = capture_with_current_layout
|
|
upstream.GUI_BRIDGE = str(GUI_BRIDGE)
|
|
adaptive.configure_layout(upstream)
|
|
try:
|
|
upstream.send_reply(reply, expected_group, event_id=event_id)
|
|
except Exception as error:
|
|
adaptive.TELEMETRY.elapsed(
|
|
"delivery.gui.send",
|
|
started,
|
|
status="error",
|
|
group_id=expected_group,
|
|
chars=len(reply),
|
|
process="delivery-helper",
|
|
error_type=type(error).__name__,
|
|
)
|
|
raise
|
|
adaptive.TELEMETRY.elapsed(
|
|
"delivery.gui.send",
|
|
started,
|
|
group_id=expected_group,
|
|
chars=len(reply),
|
|
process="delivery-helper",
|
|
delivered=True,
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|