feat: 微信自动化客服(wechatauto-replica) 干净历史导入 - AI 自动回复/语音收发/朋友圈发布

This commit is contained in:
2026-09-16 10:12:49 +08:00
commit c3b98f4c4e
69 changed files with 19828 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
"""wechatauto v1.0.9 稳健性演示 —— 失败可回退,绝不崩溃
演示「失败回退」设计原则:
1. 微信未打开时执行 voice_call / poke / open_chat
返回明确失败(WxResponse.failure / False),而非崩溃/抛异常;
2. 微信已打开时展示正常成功路径(语音通话、拍一拍);
3. 截图失败自动回退连通域(表情截图不中断)。
用法:
python demo_robust.py # 自动检测微信状态演示
python demo_robust.py --force-off # 强制演示"微信未打开"分支
"""
from __future__ import annotations
import os
import sys
import time
try:
os.system("chcp 65001 >nul 2>&1")
except Exception:
pass
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except AttributeError:
pass
from wechatauto import WeChat
from wechatauto.wx import WxResponse
WHO = "送你挖银子"
def _show(tag: str, r) -> None:
"""统一打印 WxResponse 结果。"""
if isinstance(r, WxResponse):
status = r["status"]
msg = r["message"] or ""
print(f" [{tag}] {status} :: {msg}")
return
print(f" [{tag}] 返回值 {r!r}")
def _guard(func):
"""包装:任何异常都记录为失败而非崩溃。"""
def wrapper(*a, **k):
try:
return func(*a, **k)
except Exception as e:
import traceback
print(f" [异常拦截] {type(e).__name__}: {e}")
print(f" (演示失败回退:未崩溃,异常已兜底)")
return WxResponse.failure(f"异常已拦截:{e}")
return wrapper
def _wechat_alive() -> bool:
"""不实例化 WeChat/WeChatGUI(微信未打开时构造会抛异常),直接探测主窗口。"""
try:
import ctypes
from wechatauto.guia import WX_MAIN_WIN_TITLE
hwnd = ctypes.windll.user32.FindWindowW(None, WX_MAIN_WIN_TITLE)
return bool(hwnd)
except Exception:
return False
def demo_wechat_closed() -> None:
"""微信未打开/未登录时调用各功能,验证返回失败而非崩溃。
注意:不触发热激活(写 Weixin.dll 的 Qt accessibility gate)——微信
未登录时没有可用的 ``mmui::MainWindow``,热激活无意义且属不必要的内存
写入。演示通过给驱动注入「未就绪」状态,让各功能走失败回退分支。
"""
print("\n" + "=" * 60)
print("场景 A:微信未打开/未登录 —— 功能应优雅失败")
print("=" * 60)
try:
from wechatauto.uia_driver import WeChatUIA
eng = WeChatUIA(timeout=8.0) # 可无窗口构造
print(" 构造 UIA 驱动成功(未触碰微信进程)…")
# 阻止 ensure_window 触发热激活:微信未登录时打桩掉窗口探测与
# 唤醒路径,让各功能直接走失败返回(不拉起微信、不写其进程内存)。
eng._find_main = lambda: None
eng._login_window = lambda: None
eng._wechat_hwnds = lambda: []
eng.ensure_window = lambda *a, **k: False
print(" 已打桩窗口探测(微信未登录,跳过热激活与拉起)…")
except Exception as e:
import traceback
print(f" [构造拦截] {type(e).__name__}: {e}")
print(f" (演示失败回退:驱动不可用时降级,不中断)")
_show("voice_call", WxResponse.failure(f"UIA 驱动不可用:{e}"))
_show("poke", WxResponse.failure(f"UIA 驱动不可用:{e}"))
return
t0 = time.time()
ok = _guard(eng.voice_call)(who=WHO)
_show("voice_call",
WxResponse.success("已发起通话") if ok
else WxResponse.failure("UIA 驱动不可用,无法发起通话"))
print(f" (耗时 {time.time()-t0:.1f}s,未崩溃)")
t0 = time.time()
ok = _guard(eng.poke)(who=WHO)
_show("poke",
WxResponse.success("已对 " + WHO + " 拍一拍") if ok
else WxResponse.failure("拍一拍失败(未找到对方消息或菜单不可识别)"))
print(f" (耗时 {time.time()-t0:.1f}s,未崩溃)")
print("\n -> 微信未打开/未登录时:返回明确失败信息,无异常栈、无崩溃。")
print(" 验证调用方只需判断返回值即可安全降级,不干扰其他功能。")
def demo_wechat_open(wx: WeChat) -> None:
"""微信已打开时展示正常成功路径。"""
print("\n" + "=" * 60)
print("场景 B:微信已打开 —— 功能正常工作")
print("=" * 60)
print(f" 打开会话「{WHO}」并验证…")
r = wx.ChatWith(WHO)
_show("ChatWith", r)
if not isinstance(r, str) or not r:
print(" 会话打开失败,跳过成功路径演示。")
return
chat = wx._cur()
try:
ans = input(" 发起语音通话? [Y/n] ").strip().lower()
except (EOFError, KeyboardInterrupt):
ans = ""
if ans not in ("n", "no"):
_show("voice_call", _guard(chat.VoiceCall)())
time.sleep(1)
print(" (请手动挂断)")
try:
ans = input(" 发起拍一拍? [Y/n] ").strip().lower()
except (EOFError, KeyboardInterrupt):
ans = ""
if ans not in ("n", "no"):
_show("poke", _guard(chat.Poke)())
print("\n -> 成功路径正常。")
def main():
args = [a for a in sys.argv[1:]]
force_off = "--force-off" in args
alive = _wechat_alive()
if force_off:
alive = False
print(f" 微信主窗口存活:{alive}")
try:
if alive:
wx = WeChat()
print(f"当前登录:{wx.nickname}")
demo_wechat_open(wx)
demo_wechat_closed()
else:
demo_wechat_closed()
except RuntimeError as e:
# 探测误判(如残留窗口/登录态)导致构造失败 → 降级到未打开分支
print(f" [探测误判] WeChat 构造失败:{e}")
print(" (降级演示:按微信未打开处理)")
demo_wechat_closed()
print("\n稳健性演示完成。")
if __name__ == "__main__":
main()