# -*- coding: utf-8 -*- """修用户报的 4 个问题。 【4|最要命】设完配对码之后配对被立刻关掉,手表永远连不上 run_setup() 最后调用 run_silent(store, None),而 None 表示 "不开配对窗口" → allow_new_pairing=False。 结果:用户刚设完码,被控端马上把门锁上,第一次配对必然失败。 改法:设完码先开一个 60 分钟的配对窗口;并且**第一台设备配对成功后 自动把窗口关上**(既安全又不用用户操心)。 【1】手表点「连不上」显示"已打开",电脑上其实什么都没有 静默模式下 open_diagnostics 只写日志,不弹窗口。 与其在电脑上弹窗口(用户要的是零窗口),不如**把诊断结果回传给手表**, 在手表上显示 —— 用户看的就是手表。 【3】程序不能自己退出 全局兜底后面在手表端处理;这里先把服务端的自检请求做得不会打断任何东西。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows" r"\lanremote") DISCOVERY = ROOT / "discovery.py" SERVER = ROOT / "server.py" RUN = ROOT.parent / "run_agent.py" FIXES = [ # ---------------- 4. 设完码要开配对窗口 ---------------- (RUN, ''' # 黑终端一闪:修防火墙 if result["firewall"]: fix_firewall_with_flash(store) return run_silent(store, None)''', ''' # 黑终端一闪:修防火墙 if result["firewall"]: fix_firewall_with_flash(store) # 关键:设完码必须开一个配对窗口。 # 之前这里传 None(= 不开窗口 → allow_new_pairing=False), # 结果用户刚设完码,被控端立刻就把门锁上,手表第一次配对必然失败, # 报"被控端已关闭新设备配对"。 # 第一台设备配对成功后服务端会自动把窗口关上。 store.set("close_pair_after_first", True) return run_silent(store, SETUP_PAIR_MINUTES)'''), (RUN, '''# ====================================================================== # 黑终端一闪:提权修防火墙 # ======================================================================''', '''# 设完配对码之后留多久给手表来配对 SETUP_PAIR_MINUTES = 60 # ====================================================================== # 黑终端一闪:提权修防火墙 # ======================================================================'''), # ---------------- 1. 自检应答带上诊断结果 ---------------- (DISCOVERY, '''def build_selfcheck_ack(name: str) -> bytes: payload = {"lanremote": ACK_MAGIC, "name": name} return json.dumps(payload, ensure_ascii=False).encode("utf-8")''', '''def build_selfcheck_ack(name: str, note: str = "") -> bytes: """自检应答。 note 带上电脑这边的诊断结论 —— 手表拿到就直接显示, 不需要在被控端弹窗口(用户要的是全程无窗口)。 """ payload = {"lanremote": ACK_MAGIC, "name": name, "note": note} return json.dumps(payload, ensure_ascii=False).encode("utf-8")'''), (DISCOVERY, ''' build_selfcheck_ack(str(self.store.get("server_name", ""))),''', ''' build_selfcheck_ack( str(self.store.get("server_name", "")), self._selfcheck_note),'''), ] # on_selfcheck 回调允许返回一段说明 CALLBACK_OLD = ''' if self.on_selfcheck: try: self.on_selfcheck(addr[0])''' CALLBACK_NEW = ''' if self.on_selfcheck: try: # 回调可以返回一段话,随 ACK 一起回给手表 produced = self.on_selfcheck(addr[0]) if isinstance(produced, str) and produced: self._selfcheck_note = produced[:1500]''' # 加字段 FIELD_OLD = " self.on_selfcheck = None" FIELD_NEW = (" self.on_selfcheck = None\n" " # 最近一次自检的结论,随 ACK 回给手表\n" " self._selfcheck_note = \"\"") def main() -> int: applied, skipped = 0, [] for path, old, new in FIXES: text = path.read_text(encoding="utf-8") if old not in text: skipped.append(old.splitlines()[0][:50]) print(f" [跳过] {path.name} :: {old.splitlines()[0][:50]}") continue path.write_text(text.replace(old, new, 1), encoding="utf-8") applied += 1 print(f" {path.name}: OK") # discovery 的回调与字段 text = DISCOVERY.read_text(encoding="utf-8") for old, new, tag in ((CALLBACK_OLD, CALLBACK_NEW, "回调返回值"), (FIELD_OLD, FIELD_NEW, "_selfcheck_note 字段")): if old in text: text = text.replace(old, new, 1) applied += 1 print(f" discovery / {tag}: OK") else: skipped.append(tag) print(f" [跳过] discovery / {tag}") DISCOVERY.write_text(text, encoding="utf-8") # ------------- server:自检回调返回诊断文本 + 配对后自动关窗口 ------------- stext = SERVER.read_text(encoding="utf-8") old_self = ''' self._log(f"[自检] 收到来自 {from_ip} 的请求,正在打开网络自检")''' new_self = ''' self._log(f"[自检] 收到来自 {from_ip} 的请求,正在生成诊断结论") note = self._selfcheck_report(from_ip)''' if old_self in stext: stext = stext.replace(old_self, new_self, 1) applied += 1 print(" server / 自检生成结论: OK") # 在 _on_selfcheck_request 之前插入 _selfcheck_report anchor = " def _on_selfcheck_request(self, from_ip: str) -> None:" helper = ''' def _selfcheck_report(self, from_ip: str) -> str: """生成一段给手表看的诊断结论(不弹任何窗口)。""" try: from . import firewall from .diagnostics import collect, verdict data = collect(self, self.store) level, text = verdict(data) ips = data.get("ips") or [] port = self.port blocked = data.get("firewall", {}).get("blocked_count", 0) lines = [ f"电脑:{data.get('name') or '被控端'}", f"本机地址:{', '.join(ips[:2]) if ips else '读不到'}", f"监听端口:{port}", f"收到过的连接:{data.get('tcp_connections', 0)} 次", f"阻止规则:{blocked} 条", "", f"结论:{text}", ] return "\\n".join(lines) except Exception as exc: # noqa: BLE001 return f"电脑端诊断失败:{exc}" ''' if anchor in stext and "_selfcheck_report" not in stext: stext = stext.replace(anchor, helper + anchor, 1) applied += 1 print(" server / _selfcheck_report: OK") # 配对成功后自动关配对窗口 old_pair = ''' session.device_id = device_id session.device_name = device_name session.authed = True''' new_pair = ''' session.device_id = device_id session.device_name = device_name session.authed = True # 首次配对成功后自动把配对窗口关上 —— 用户设完码只需配一次, # 之后不用再操心"要不要关配对"。 if self.store.get("close_pair_after_first"): self.pairing_enabled = False self.store.set("allow_new_pairing", False) self.store.set("close_pair_after_first", False) self._log("[配对] 首台设备已配对,配对窗口已自动关闭")''' if old_pair in stext: stext = stext.replace(old_pair, new_pair, 1) applied += 1 print(" server / 首台配对后关窗口: OK") SERVER.write_text(stext, encoding="utf-8") print(f"\n 应用 {applied} 处") if skipped: print(f" 跳过: {skipped}") import ast ok = True for label, path in (("discovery.py", DISCOVERY), ("server.py", SERVER), ("run_agent.py", RUN)): try: ast.parse(path.read_text(encoding="utf-8")) print(f" {label}: 语法 OK") except SyntaxError as exc: print(f" {label}: 语法错误 {exc}") ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())