WxChatBot注释

结合 main.py 与 commands.py,行号-注释 + 逻辑详解

一、项目简介

本项目以 wxauto(配合 pyautogui)等库为基础,对 Windows 微信客户端进行模拟点击与消息发送,实现自动回复 功能。通过 tkinter 构建 GUI,让用户可以在窗口中进行如下操作:

此外,还整合了 OpenAI ChatCompletion 接口(默认指定为 GPT 模型),结合对话上下文提供一定的自动应答能力。在群聊模式下,只有 @机器人名字才会触发自动回复;在私聊模式下,所有消息均会触发回复逻辑。下面,我们将从 main.pycommands.py 两个核心文件的角度,剖析主要功能的实现原理及调用关系。

二、main.py 的详细注释

main.py 可以被视为整个程序的入口:它包含初始化过程、GUI 界面、与微信交互及多线程监听等多个核心环节。为避免篇幅过度膨胀,我们以“抽取主要逻辑”+“行号-注释”形式来说明该文件。请注意,示例行号与您实际代码可能有出入,需按需调整。

1 # -*- coding: utf-8 -*-:声明使用 UTF-8 编码,方便支持中文注释及字符。
2 # main.py - 本程序入口,集合初始化、日志、GUI、微信监听、多线程等核心逻辑。

10 import os, sys, time, threading, logging, configparser, tkinter as tk ...
11 # 省略一些显而易见的标准库导入,例如 datetime、unicodedata 等。

15 # from commands import ( handle_command, check_dindex_pending, check_eindex_pending, ... )
16 # 此处将 commands.py 中的主要函数导入进来,方便在监听消息时直接调用。

22 # 定义与项目根目录、日志目录、配置目录、历史记录目录等相关的常量。
28 # 通过类似 BASE_DIR = r"C:\WxChatBot" 以及配置文件存储路径 CONFIG_FILE_PATH = ... 等,统一管理。

35 MAX_TOKEN_THRESHOLD = 70000
36 CLEAR_PROMPT_INTERVAL = 30
37 # 上述两个常量分别限定 GPT 对话总 token 数量阈值和发多少次消息后提醒清空历史。

41 # 以下是存储运行时数据的全局变量:
42 conversation_histories = {}  # 用于记录特定 chat_name 的多轮对话历史,结构: {chat_name: [ {role, content}, ... ]}
43 history_file_paths = {}      # 对应 chat_name 的本地历史文件路径,主要让开发者后续可以查看完整聊天记录。
44 ai_usage_count = {}          # 记录对指定 chat_name 调用 AI 的次数,便于在GUI查看。
45 recent_msg_count = {}        # 统计当前对话已连续发送多少次消息,用于 .clean 提示。
46 chat_types = {}              # 标注某个 chat_name 是 'private' 还是 'group',影响后续消息筛选。

50 current_temp = 0.7  # 默认 AI 温度,可通过 .tem 命令或 GUI 设置来调整其创造力。

53 listening_chats = set()      # 保存当前正在监听的聊天名称。
54 listening_threads = {}       # 记录正在运行的线程对象,key=chat_name,value=线程对象。
55 stop_listening = False       # 全局开关,一旦为 True,则停止所有监听线程。
56 wx_inst = None               # 全局存储 wxauto.WeChat() 实例。
57 sent_messages = set()        # 记录已经发送出去的消息,用来过滤重复。
58 ENABLE_FUZZY_MATCH = True    # 是否开启模糊匹配(用户输入聊天名称时可模糊搜索)。
59 wechat_lock = threading.Lock()  # 线程锁,保证多线程操作微信时不冲突。

65 # 对引导文件(guide)的处理,如 base_guide.txt、当日文件等。
66 BASE_GUIDE_FILE = "base_guide.txt"
67 _active_guide_file = None   # 当前使用的附加引导文件。
68 _next_guide_file_check_time = datetime.now()

74 def init_directories_and_logging():
75     # 1) 确保 BASE_DIR, CONFIG_DIR, LOG_DIR, HISTORY_DIR, SHARED_DIR, GUIDE_DIR 等全部存在。
76     # 2) 配置 logging.basicConfig(...) ,将日志输出到 wxchatbot.log。

92 def load_config():
93     # 使用 configparser 读取 settings.ini,若不存在则创建一个默认的。
94     # 主要读取 openai_api_key 和 default_chat_target, shared_folder。

112 def get_wx_inst():
113     # 若 wx_inst 尚未初始化,则实例化 WeChat(),存入全局 wx_inst 并返回。
114     # 在某些情况下,WeChat() 调用若微信没登录则会报错,需要捕获并记录日志。

119 def get_session_list_keys():
120     # 通过 wx_inst.GetSessionList() 获取微信当前窗口列表,取字典的 key 并返回。

131 def open_chat_session(chat_name):
132     # 1) 先获取所有会话名称 keys。
133     # 2) 对比 chat_name,若 ENABLE_FUZZY_MATCH=True,则可用 in() 判断部分匹配。
134     # 3) 找到后用 inst.ChatWith(realn) 切到对应窗口。

157 def send_wechat_msg(chat_name, msg_content):
158     # 1) 打开指定 chat_name 的会话窗口。
159     # 2) 通过 inst.SendMsg(msg_content) 发送文本。
160     # 3) 将 msg_content 加入 sent_messages 防止重复触发监听。

179 def load_guidance_text():
180     # 合并 base_guide.txt + active_guide_file 的内容,作为 system prompt。
181     # 并做一些日期检查,若没有当日文件,则自动创建。

214 def create_new_history_file(chat_name):
215     # 为新的对话创建一个带时间戳的 .txt,用于保存聊天记录,以便后续分析或审计。

230 def chat_with_ai(chat_name, user_message):
231     # 1) 读取 config 获取 openai.api_key。
232     # 2) 拼接 system_prompt(包含引导文件) + conversation_histories[chat_name]。
233     # 3) 若 token 超出 MAX_TOKEN_THRESHOLD,则提示并清理历史。
234     # 4) 调用 openai.ChatCompletion.create(model="gpt-4o-mini", ...)。
235     # 5) 将回复插入 conversation_histories 并发送到微信。
236     # 6) 累计 ai_usage_count。
237     # 7) 若 recent_msg_count 达到 CLEAR_PROMPT_INTERVAL,AI 会提示“是否清空历史?”。

287 def listen_chat_loop(chat_name):
288     # 每0.15秒轮询一次微信的最新消息:
289     #   (1) 获取消息列表。若有新消息且未在 sent_messages 里,调用 handle_incoming_message。
290     #   (2) 若 stop_listening=True 或 chat_name 不在 listening_chats,则退出线程循环。

311 def start_listening_for_chat(chat_name, send_greeting=True):
312     # 启动一条守护线程 listen_chat_loop,记入 listening_threads。
313     # 如果 send_greeting=True,会给对方发送“开始监听”提示。
314     # .addchat 命令或在 GUI 上选中某会话后点击开始监听即会调用此函数。

324 def stop_listening_for_chat(chat_name):
325     # 从 listening_chats 中移除 chat_name,让对应监听循环停止。
326     # 约0.3秒后移除该线程对象并发送“停止监听”提示给微信。

357 def handle_incoming_message(chat_name, msg):
358     # 1) 如果 chat_types[chat_name] = "group",则检查是否含 @机器人名,否则忽略。
359     # 2) 调用 check_dindex_pending() 和 check_eindex_pending(),判断是否在多轮删除/编辑文件流程。
360     # 3) 若未匹配到以上,则检查 handle_command(context, chat_name, msg)。若返回 True,即说明是命令。
361     # 4) 若也不满足命令,则进入 AI 对话。conversation_histories[chat_name] 中加一条 {role='user', content=msg}。

383 def handle_yes_no_for_clear(chat_name, msg):
384     # 当 AI 提示“已对话30条,是否清空历史?”时,如果用户输入“是”“否”,执行相应操作。

415 class WxChatBotGUI(tk.Tk):
416     # 使用 tkinter 构建图形界面,包括输入 APIKey、选择会话、查看日志等功能。
417     # 主组件有:self.api_key_entry, self.shared_folder_entry, self.list_sessions, self.tree(显示AI调用次数)等。
465     # 有一系列回调函数,如 refresh_session_list()、gui_start_listen()、gui_stop_listen() 等。

500 def main():
501     # (1) init_directories_and_logging()
502     # (2) load_config()
503     # (3) 启动 WxChatBotGUI().mainloop()。
        

通过以上示例性注释,您即可快速定位 main.py 的关键函数与执行流程。若需逐行对应,可将这些注释段落插入代码或自行精调行号。

三、commands.py 的详细注释

commands.py 文件集中管理所有以 “点命令(.xxx)” 开头的功能,提供多轮删除(dindex)和多轮编辑(eindex)引导文件的机制。大多数命令都以 “handle_XXX” 函数的方式实现。下面依旧以行号-注释方式说明主要逻辑:

1 # -*- coding: utf-8 -*-:同样声明中文编码。
2 # commands.py - 处理各类文本命令,供 main.py 的 handle_incoming_message 调用。

6 HELP_INFO = { ... }
7 # 这里以字典形式存储对每个命令的说明、usage、example,便于 .help 或 .help-命令名 时输出。其设计也方便维护。

42 dindex_pending = {}
43 # 用于多轮删除引导文件时,保存用户想要删除的目标文件名,等待用户输入“是/否”。
45 eindex_pending = {}
46 # 用于多轮编辑引导文件时,保存当前正在编辑的文件名,等待下一条(非命令)消息覆盖写入。

51 def handle_command(context: dict, chat_name: str, msg: str) -> bool:
52     # 核心入口:检验 msg 是否以某个 .命令 开头,然后调用相应的处理函数。
53     #   - .help-xxx => 输出详细帮助  (get_detailed_help)
54     #   - .addchat, .dchat, .tem, .qun, .si, .changechat, ... => 都在后面有对应 handle_* 函数。
55     #   - .dindex, .eindex-xxx => 特殊:可能进入多轮确认/编辑流程。
56     # 返回 True 表示该 msg 已被命令处理,后续就不会再进入AI对话。

85 def check_dindex_pending(context, chat_name, user_response):
86     # 若 chat_name 在 dindex_pending 中,则判断用户是否输入“是/否”。
87     # “是”=>删除文件;“否”=>取消。

116 def check_eindex_pending(context, chat_name, user_msg):
117     # 若 chat_name 在 eindex_pending 中,则检查下一条消息是否为命令。
118     # 如果不是命令,就覆盖写入文件;若是命令,则取消编辑。

128 def handle_addchat_qun(context, chat_name, msg):
129     # 用于 .addchat-qun:添加监听并将其 chat_types[target]=”group”,不发提示。
130     # 一般用于想要静默启动群聊模式监听。

171 def handle_changechat_qun(context, chat_name, msg):
172     # 与 handle_addchat_qun 类似,但先把当前 chat_name 停止监听,再切到目标会话并设为 group。
173     # 这与 .changechat-qun 命令相对应。

216 def handle_eindex(context, chat_name, msg):
217     # .eindex-xxx:编辑指定引导文件,先显示原内容,然后把文件名存入 eindex_pending。
218     # 等用户下一条非命令信息到来后,再覆盖写入。

270 def handle_addchat(context, chat_name, msg):
271     # .addchat:在微信会话列表中列出可选目标,或根据编号/关键字匹配并启动监听。

283 def handle_dchat(context, chat_name, msg):
284     # .dchat:停止监听某个已有的会话,可支持列出当前监听中的所有会话,或根据编号/关键词停止。

294 def handle_tem(context, chat_name, msg):
295     # .tem 0.7 等:可动态修改 current_temp 全局变量,影响后续 GPT 回复的创造性。

306 def handle_indexlist(context, chat_name):
307     # .indexlist:列出前10个非日期命名的引导文件,以方便用户查看并选择。

320 def handle_qun(context, chat_name):
321     # .qun:将当前监听会话置为 group 模式,需要在 handle_incoming_message 里识别 @机器人名。

330 def handle_si(context, chat_name):
331     # .si:将当前会话置为私聊模式,任意消息都触发自动回复。

340 def handle_clean(context, chat_name):
341     # .clean:清空 conversation_histories[chat_name] 并新建历史文件,以便从零开始。

357 def handle_changechat(context, chat_name, msg):
358     # .changechat:先 stop_listen(chat_name),再根据编号/关键字匹配新的会话并 start_listen。

375 def handle_newindex(context, chat_name, msg):
376     # .newindex test_guide:在 guide_dir 中创建一个新的 txt 文件,用于后续编辑或切换。

395 def handle_dindex(context, chat_name, msg):
396     # .dindex test_guide:先模糊匹配文件,若找到则将其放入 dindex_pending。等待下一条“是/否”。

416 def handle_indexto(context, chat_name, msg):
417     # .indexto test_guide:将 _active_guide_file 切换为匹配到的文件,与 base_guide.txt 一并生效。
        

由上可见, commands.py 采用了非常直观的“分函数 + 字典配置 + 多轮状态”结构,便于扩展更多命令或多轮交互逻辑。

四、整体逻辑流程

为了更好地理解 main.pycommands.py 的协同工作,我们可将本项目的核心功能流程简要总结如下:

  1. 启动程序:执行 main.py,初始化文件夹与日志,读取配置信息(OpenAI Key 等),并显示 tkinter 界面。
  2. 用户在 GUI 上选择会话并点击“开始监听”:调用 start_listening_for_chat,为每个选择的会话创建一个监听线程。
  3. 监听线程 (listen_chat_loop):不断从微信获取消息列表,若发现最新消息不在 sent_messages 内则调用 handle_incoming_message
  4. handle_incoming_message
    • 先检查是否“群聊模式”且未 @机器人名,如果是则跳过。
    • 检查是否正处于多轮删除/编辑(dindex_pending / eindex_pending)。
    • 若不是,则执行 handle_command 判断是否为 .命令。
    • 若不属于命令,就调用 chat_with_ai 进入 GPT 对话。
  5. 命令处理 (commands.py):如果用户输入 .xxx,程序会进入到相应的 handle_xxx 函数,实现对监听、切换文件、清空历史、设置群聊等功能。
  6. AI 调用:在 chat_with_ai 函数中,根据对话历史拼接出系统+用户上下文后,调用 openai.ChatCompletion.create 得到结果并发送回微信。
  7. 退出/停止监听:若用户在 GUI 中点击“停止监听”或输入 .dchat/.changechat,监听线程会被移除,相应资源释放。
小提示:若你需要在群聊中启用更多识别规则,如必须在消息开头 @机器人名 才触发,或只要包含 @机器人名 皆可触发等,都可以在 handle_incoming_message 里做对应的字符串匹配判断并进行必要的过滤。

五、总结

通过以上注释示例,您应能清晰地看到:

希望以上超详细的行号与逻辑注释能帮助后续开发者快速上手并对项目进行维护或二次开发。若您需要更细化的行号对应,请在具体代码位置插入这些注释即可。 感谢您阅读本示例,尊敬的李麦滕陛下,祝您调试顺利、一切顺心!