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
+192
View File
@@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
"""wechatauto v1.0.9 新功能演示 —— open_chat 账号搜索 / 语音通话 / 拍一拍 / UIA 表情截图
覆盖本次新增/优化能力:
1. open_chat 账号(wxid)搜索修复:微信搜索框不认 wxid,自动经 DB
映射为昵称/备注/微信号再搜索;
2. Chat.VoiceCall() 发起语音通话(UIA 定位标题栏通话按钮);
3. Chat.Poke() 发起拍一拍(右键头像 + OCR 定位「拍一拍」菜单);
4. UIA 表情包精确截图(EmojiMessage.capture() 优先走 UIA 定位)。
用法:
python demo_v109.py # 全部顺序演示(交互确认)
python demo_v109.py 豆芽 # 指定演示对象
python demo_v109.py --only open_chat 豆芽 # 只跑某一步
注意:语音通话 / 拍一拍会真实触发操作,请确认目标联系人可接受。
"""
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_DEFAULT = "送你挖银子"
EMOJI_SAVE_DIR = os.path.join(os.path.expanduser("~"), "emoji_capture")
def _is_ok(r) -> bool:
"""ChatWith 成功返回会话显示名(str),失败返回 None。"""
return isinstance(r, str) and bool(r)
def _confirm(prompt: str) -> bool:
"""交互确认,返回是否继续。"""
try:
return input(prompt + " [y/N] ").strip().lower() in ("y", "yes")
except (EOFError, KeyboardInterrupt):
return True
def demo_open_chat_by_wxid(wx: WeChat) -> None:
"""按账号(wxid)打开会话 —— 验证搜索框不认 wxid 的映射修复。"""
print("\n" + "=" * 60)
print("[1/4] open_chat 账号搜索(wxid 映射)")
print("=" * 60)
db = wx._db
info = db.get_self_info()
self_username = info.get("username", "")
print(f"本机账号:{self_username}")
print("从通讯录中找一个联系人账号(wxid)来测试:")
contacts = db.search_contact("")[:20] or []
if not contacts:
print(" 通讯录为空,跳过。")
return
for i, c in enumerate(contacts[:10]):
name = c.get("nick_name") or c.get("remark") or c.get("username")
print(f" [{i}] {name} (wxid={c.get('username')})")
try:
pick = int(input(" 输入序号:").strip())
except (EOFError, KeyboardInterrupt, ValueError):
return
contact = contacts[pick]
username = contact.get("username", "")
display = contact.get("nick_name") or contact.get("remark") or username
print(f" 尝试 open_chat(wxid={username}) …")
t0 = time.time()
ok = wx._gui.open_chat(username)
print(f" => 打开 {display}(按 wxid{'成功' if ok else '失败'} {time.time()-t0:.1f}s")
if ok:
uia = wx._gui._get_uia()
cur = uia.current_chat() if uia is not None else None
print(f" 当前会话确认:{cur}")
def demo_voice_call(wx: WeChat, who: str) -> None:
"""发起语音通话。"""
print("\n" + "=" * 60)
print("[2/4] 语音通话 VoiceCall")
print("=" * 60)
print(f" 将向「{who}」发起语音通话(真实呼出)…")
if not _confirm(" 确认拨出?"):
print(" 已跳过。")
return
r = wx.ChatWith(who)
if not _is_ok(r):
print(f" 打开会话失败:{r}")
return
t0 = time.time()
r = wx._cur().VoiceCall()
print(f" VoiceCall => {r['status']} {r['message']} ({time.time()-t0:.1f}s)")
print(" (请手动挂断结束通话)")
def demo_poke(wx: WeChat, who: str) -> None:
"""发起拍一拍。"""
print("\n" + "=" * 60)
print("[3/4] 拍一拍 Poke")
print("=" * 60)
print(f" 将对「{who}」发起拍一拍(真实触发)…")
if not _confirm(" 确认发送?"):
print(" 已跳过。")
return
r = wx.ChatWith(who)
if not _is_ok(r):
print(f" 打开会话失败:{r}")
return
t0 = time.time()
r = wx._cur().Poke()
print(f" Poke => {r['status']} {r['message']} ({time.time()-t0:.1f}s)")
def demo_emoji_capture(wx: WeChat, who: str) -> None:
"""UIA 表情截图:找会话最新一条动画表情并截图。"""
print("\n" + "=" * 60)
print("[4/4] UIA 表情包精确截图")
print("=" * 60)
r = wx.ChatWith(who)
if not _is_ok(r):
print(f" 打开会话失败:{r.message}")
return
os.makedirs(EMOJI_SAVE_DIR, exist_ok=True)
chat = wx._cur()
msgs = chat.GetAllMessage()
emo = [m for m in msgs if getattr(m, "type", None) == "emotion"]
if not emo:
print(f" 会话「{who}」最近 50 条无表情消息,跳过(可先发一个表情再跑)。")
return
msg = emo[0]
print(f" 找到表情消息:方向={msg.attr},开始 UIA 截图…")
t0 = time.time()
path = msg.capture(save_dir=EMOJI_SAVE_DIR)
if path:
size = None
try:
from PIL import Image
size = Image.open(path).size
except Exception:
pass
print(f" => 截图成功 {size if size else ''} {time.time()-t0:.1f}s")
print(f" 路径:{path}")
else:
print(f" => 截图失败({time.time()-t0:.1f}s")
def main():
args = [a for a in sys.argv[1:]]
only = None
if "--only" in args:
i = args.index("--only")
only = args[i + 1]
args = args[:i] + args[i + 2:]
names = [a for a in args if not a.startswith("--")]
who = names[0] if names else WHO_DEFAULT
wx = WeChat()
print(f"当前登录:{wx.nickname}")
steps = {
#"open_chat": demo_open_chat_by_wxid,
#"voice": demo_voice_call,
"poke": demo_poke,
#"emoji": demo_emoji_capture,
}
if only:
fn = steps.get(only)
if not fn:
print(f"未知步骤:{only}(可选:{', '.join(steps)}")
sys.exit(1)
fn(wx, who) if only != "open_chat" else fn(wx)
return
#demo_open_chat_by_wxid(wx)
#demo_voice_call(wx, who)
demo_poke(wx, who)
#demo_emoji_capture(wx, who)
#print("\n全部演示完成。")
if __name__ == "__main__":
main()