Files

148 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""微信自动回复接管脚本 v2wechatauto-replica
功能:监听微信新消息,当【真实好友】发来私聊消息时,自动回复。
规则:
- 只回复 wxid_ 开头的真实好友(公众号 gh_、群聊 @chatroom、
文件传输助手 filehelper、特殊号 @openim/@placeholder/brandsessionholder 一律不碰)
- 跳过自己发出去的消息(sender_id == 2
- 同一好友 COOLDOWN_SEC 秒内只回一次(防刷屏)
- GUI 发送全局串行(多个好友同时来消息不会抢窗口)
用法(项目目录下,独立 venv):
.venv\\Scripts\\python.exe wechat_daemon.py
前置:微信 4.x 已登录、桌面未锁屏、管理员权限(提密钥)。
"""
import sys
import os
import time
import threading
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.db import WeChatDB, Listener
from wechatauto.guia import WeChatGUI
# ==================== 自动回复配置(改这里) ====================
AUTO_REPLY = True # 总开关
DEFAULT_REPLY = "【自动回复】已收到您的消息,稍后回复您。"
# 关键词规则:消息命中关键词时用对应话术回复;未命中则用 DEFAULT_REPLY
KEYWORD_RULES = {
# "你好": "你好呀,请问有什么可以帮您?",
# "价格": "具体价格您可以看下我发给您的报价单。",
}
# 只回复这些前缀的会话(真实好友)。留空 = 不限制前缀
REPLY_ONLY_PREFIX = ("wxid_",)
# 防抖:同一会话内多少秒内不重复自动回复
COOLDOWN_SEC = 30
# 黑名单会话(精确匹配 username,绝不自动回复)
BLOCKLIST = {"filehelper"}
# ==============================================================
_gui = None
_gui_lock = threading.Lock()
_last_reply = {} # username -> 上次回复时间戳
_send_lock = threading.Lock()
def get_gui():
global _gui
with _gui_lock:
if _gui is None:
_gui = WeChatGUI()
return _gui
def should_reply(username: str) -> bool:
"""是否对某会话启用自动回复。"""
if not AUTO_REPLY:
return False
if username in BLOCKLIST:
return False
if username.endswith("@chatroom"):
return False
if not REPLY_ONLY_PREFIX:
return True
return username.startswith(REPLY_ONLY_PREFIX)
def pick_reply(content: str) -> str:
for kw, reply in KEYWORD_RULES.items():
if kw in content:
return reply
return DEFAULT_REPLY
def on_message(msg: dict, lst: Listener):
username = msg.get("username", "")
sender_id = msg.get("sender_id", "")
content = str(msg.get("content", ""))
# 跳过自己发出去的消息(避免回复自己的回复 → 死循环)
if str(sender_id) == "2":
return
if not should_reply(username):
return
# 防抖
now = time.time()
last = _last_reply.get(username, 0)
if now - last < COOLDOWN_SEC:
return
_last_reply[username] = now
reply = pick_reply(content)
nick = _last_reply.get("_db") and _last_reply["_db"].get_nickname(username) or username
# 全局串行发送,避免多个会话并发抢同一微信窗口
with _send_lock:
try:
r = get_gui().send_msg(reply, who=username, verify=True)
print(f"[自动回复] {username} ({nick}) <- {reply} | ok={r.is_success} {r['message']}",
flush=True)
except Exception as e: # noqa: BLE001
print(f"[自动回复失败] {username}: {e!r}", flush=True)
def main():
db = WeChatDB()
info = db.get_self_info()
nick = info.get("nick_name") or info.get("username")
_last_reply["_db"] = db
print(f"已接管微信: {nick} ({info.get('username')})", flush=True)
print(f"自动回复: {'开' if AUTO_REPLY else '关'} | 默认话术: {DEFAULT_REPLY} | "
f"只回前缀: {REPLY_ONLY_PREFIX} | 防抖 {COOLDOWN_SEC}s", flush=True)
lst = Listener(db, interval=1.0)
# add_all:监听所有已有会话 + 自动发现新会话
lst.add_all(on_message)
print("自动回复监听已启动(真实好友发消息即自动回复,公众号/群聊不碰),Ctrl+C 退出...",
flush=True)
lst.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
lst.stop()
print("已停止监听。", flush=True)
if __name__ == "__main__":
main()