150 lines
4.3 KiB
Python
150 lines
4.3 KiB
Python
import json
|
|
import os
|
|
import signal
|
|
import sys
|
|
import time
|
|
from datetime import datetime, timezone
|
|
|
|
import wcferry.client as wcf_client
|
|
from wcferry import Wcf
|
|
|
|
|
|
PORT = int(os.environ.get("WCF_COMMAND_PORT", "10086"))
|
|
STATE_ROOT = os.environ.get("WCF_STATE_ROOT", r"C:\UniDesk\personal-wechat\wcf-state")
|
|
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}
|
|
line = json.dumps(payload, ensure_ascii=False, sort_keys=True)
|
|
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):
|
|
global STOP
|
|
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():
|
|
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())
|
|
qrcode = ""
|
|
if not logged_in:
|
|
try:
|
|
qrcode = wcf.get_qrcode()
|
|
except Exception as exc:
|
|
log("wcf-host-qrcode-error", error=f"{type(exc).__name__}: {exc}"[:1000])
|
|
qrcode = ""
|
|
state = {
|
|
"phase": "ready",
|
|
"isLogin": logged_in,
|
|
"qrcodePresent": bool(qrcode),
|
|
"commandPort": PORT,
|
|
"messagePort": PORT + 1,
|
|
}
|
|
if qrcode:
|
|
state["qrcode"] = qrcode
|
|
write_state(**state)
|
|
log("wcf-host-status", isLogin=logged_in, qrcodePresent=bool(qrcode))
|
|
time.sleep(5)
|
|
except Exception as exc:
|
|
log("wcf-host-error", error=f"{type(exc).__name__}: {exc}"[:1000])
|
|
time.sleep(3)
|
|
try:
|
|
wcf.cleanup()
|
|
except Exception:
|
|
pass
|
|
log("wcf-host-stop")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|