fix: isolate personal wechat wcf host

This commit is contained in:
Codex
2026-06-13 16:17:25 +00:00
parent 5d484d1ac4
commit 3b6d22d817
5 changed files with 359 additions and 106 deletions
+79 -6
View File
@@ -5,6 +5,7 @@ import sys
import time
from datetime import datetime, timezone
import wcferry.client as wcf_client
from wcferry import Wcf
@@ -15,10 +16,23 @@ DEBUG = os.environ.get("WCF_DEBUG", "false").lower() == "true"
STOP = False
class WcfProcessExit(Exception):
def __init__(self, code):
super().__init__(f"wcferry requested process exit: {code}")
self.code = code
def now_iso():
return datetime.now(timezone.utc).isoformat()
def write_json(path, payload):
tmp = f"{path}.tmp"
with open(tmp, "w", encoding="utf-8") as fp:
json.dump(payload, fp, ensure_ascii=False, indent=2, sort_keys=True)
os.replace(tmp, path)
def log(event, **fields):
os.makedirs(STATE_ROOT, exist_ok=True)
payload = {"ts": now_iso(), "event": event, **fields}
@@ -26,6 +40,20 @@ def log(event, **fields):
print(line, flush=True)
with open(os.path.join(STATE_ROOT, "wcf-host.log"), "a", encoding="utf-8") as fp:
fp.write(line + "\n")
fp.flush()
def write_state(**fields):
os.makedirs(STATE_ROOT, exist_ok=True)
payload = {
"ts": now_iso(),
"isLogin": None,
"qrcodePresent": False,
"commandPort": PORT,
"messagePort": PORT + 1,
**fields,
}
write_json(os.path.join(STATE_ROOT, "status.json"), payload)
def handle_signal(_signum, _frame):
@@ -33,13 +61,58 @@ def handle_signal(_signum, _frame):
STOP = True
def patch_wcferry_exit():
def raise_exit(code=0):
raise WcfProcessExit(code)
wcf_client.os._exit = raise_exit
signal.signal(signal.SIGTERM, handle_signal)
signal.signal(signal.SIGINT, handle_signal)
def connect_wcf():
patch_wcferry_exit()
attempt = 0
while not STOP:
attempt += 1
local_init = attempt == 1 or attempt % 15 == 0
mode = "local-init" if local_init else "remote-dial"
try:
write_state(phase="connecting", connectAttempt=attempt, connectMode=mode)
log("wcf-host-connect-attempt", attempt=attempt, mode=mode, port=PORT)
if local_init:
wcf = Wcf(port=PORT, debug=DEBUG, block=False)
else:
wcf = Wcf(host="127.0.0.1", port=PORT, debug=DEBUG, block=False)
log("wcf-host-connected", attempt=attempt, mode=mode, port=PORT)
return wcf
except WcfProcessExit as exc:
log("wcf-host-connect-retry", attempt=attempt, mode=mode, requestedExit=exc.code)
except Exception as exc:
log("wcf-host-connect-error", attempt=attempt, mode=mode, error=f"{type(exc).__name__}: {exc}"[:1000])
time.sleep(min(2 + attempt, 10))
return None
def main():
log("wcf-host-start", port=PORT, debug=DEBUG)
wcf = Wcf(port=PORT, debug=DEBUG, block=False)
os.makedirs(STATE_ROOT, exist_ok=True)
with open(os.path.join(STATE_ROOT, "wcf-host.pid"), "w", encoding="ascii") as fp:
fp.write(str(os.getpid()))
log(
"wcf-host-start",
port=PORT,
debug=DEBUG,
pid=os.getpid(),
appData=os.environ.get("APPDATA", ""),
userProfile=os.environ.get("USERPROFILE", ""),
)
wcf = connect_wcf()
if wcf is None:
log("wcf-host-stop-before-connect")
return
while not STOP:
try:
logged_in = bool(wcf.is_login())
@@ -47,10 +120,11 @@ def main():
if not logged_in:
try:
qrcode = wcf.get_qrcode()
except Exception:
except Exception as exc:
log("wcf-host-qrcode-error", error=f"{type(exc).__name__}: {exc}"[:1000])
qrcode = ""
state = {
"ts": now_iso(),
"phase": "ready",
"isLogin": logged_in,
"qrcodePresent": bool(qrcode),
"commandPort": PORT,
@@ -58,8 +132,7 @@ def main():
}
if qrcode:
state["qrcode"] = qrcode
with open(os.path.join(STATE_ROOT, "status.json"), "w", encoding="utf-8") as fp:
json.dump(state, fp, ensure_ascii=False, indent=2, sort_keys=True)
write_state(**state)
log("wcf-host-status", isLogin=logged_in, qrcodePresent=bool(qrcode))
time.sleep(5)
except Exception as exc: