简介:这是一份基于微信网页接口的高度自定义微信机器人开源项目,定位为轻量、可扩展的自动化助手,适合希望为个人微信或小团队快速搭建自动回复、消息转发、防撤回、留言统计等能力的开发者。项目将常见场景封装为清晰模块,开发者可依据实际业务调整关键词规则、转发对象和统计维度,用于社群运营、个人助理或微信接口学习均可。压缩包整体约71KB,代码量精简,结构轻巧,适合作为理解微信机器人整体架构与接口调用流程的中小型示例;由于包体不大,直接阅读源码即可快速定位核心逻辑并完成二次修改。目前已有1107人学习下载,尤其适合具备一定编程基础、想从零掌握微信机器人实现思路并快速上手的开发者。
1. 高度自定义的微信机器人,到底在自定义什么
如果你手头同时管着几个群,或者要给小团队做内部消息值班,多半遇到过这种尴尬:人不能 24 小时盯着屏幕,群里问了问题没人答,重要通知发出去又被刷掉,转过天的留言想统计只能手动翻聊天记录。市面上一类被打包成 zip 的微信机器人项目,就是冲着这四件事去的:自动回复、消息转发、防撤回、留言统计。它不需要自建服务器,不需要申请复杂的开放平台资质,能跑在普通电脑上,用你现有的微信账号就能动起来。
我的结论先说在前面:这类包不是玄学,是能跑通、能复用、能改造成自家工具的方案,但也不是解压双击就完事。自动回复规则怎么匹配、转发怎么防止两个机器人互怼成循环、防撤回怎么处理图片视频这类非文本消息、统计字段怎么建表,这些才是真正决定它“能用”还是“能跑”的分界线。适合的人群也很明确:小群群管、内部项目值班、个人消息归档爱好者;不适合指望它做对外客服系统的人。下面按我的实操顺序,从拆包到改配置一步步说。
2. 拆包这只微信机器人:目录、环境与第一次启动
2.1 解压后先看目录结构,别急着运行
这类压缩包解压出来通常是一个工程目录,而不是一个单文件 exe。不同作者打包习惯不一样,但绝大多数会分成四个区域:主程序入口、核心事件循环、插件目录、配置文件。拿到手第一件事不是双击运行,而是先用文件管理器或者命令行把目录结构捋一遍。
常见做法是先看根目录下有没有 README 或 requirements.txt,再看有没有 config 开头的文件。我就吃过不看 README 的亏,直接运行后日志报了一堆模块缺失,回头才发现依赖清单就写在根目录第一行。一个典型的目录结构大致长这样:
wechat-bot/ ├── main.py # 主入口,负责启动事件循环 ├── requirements.txt # 第三方依赖清单 ├── config.yaml # 核心配置:回复规则、转发映射、统计开关 ├── bot_core/ # 消息事件处理核心 │ ├── listener.py # 消息监听与事件分发 │ └── sender.py # 消息发送封装 ├── plugins/ # 功能插件按文件拆分 │ ├── auto_reply.py # 自动回复 │ ├── forward.py # 消息转发 │ ├── anti_recall.py # 防撤回 │ └── stats.py # 留言统计 └── data/ # 运行产生的缓存、数据库、日志main.py 是入口;bot_core 负责接收微信客户端抛出来的消息事件,再按事件类型分发给 plugins 下的不同模块。这种“事件循环 + 插件”的结构好处在于:你想关掉防撤回,不用改主程序,删掉 plugin 的注册行就行;想加一个新功能,写一个独立文件再注册进去即可。这也是“高度自定义”最直接的体现——自定义的不是微信本身,而是消息进来之后的分发逻辑。
2.2 环境准备:三个必做项,缺一不可
运行这类项目,环境依赖基本逃不开 Python 和几个第三方库。常见要求是 Python 3.8 以上,以及 websocket、requests、PyYAML 这类依赖。第一次装环境时,我建议用虚拟环境隔离,不要直接装进系统 Python,否则后面换项目时依赖版本冲突会非常头疼。
cd wechat-bot python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install -r requirements.txt python -c "import yaml; print(yaml.__version__)"前三行创建并激活虚拟环境,第四行按 requirements.txt 安装依赖,第五行验证 PyYAML 是否装好。requirements.txt 里常见的库还包括用于 WebSocket 长连接的 websocket-client、用于读取配置的 PyYAML、用于记录日志的 loguru。装完依赖后,如果 import 报错,多半是 Python 版本不对——有些底层库只支持到 3.10,你偏偏用了 3.12 就会编不过。
2.3 配置文件字段:先把这张“参数表”读懂
这类机器人最核心的自定义入口就是配置文件。我第一次上手时以为自动回复就是写几个关键词,看了配置才发现,规则匹配、转发映射、统计开关全在这一个文件里。不同项目的字段名会有差异,但语义基本对齐。下面是一个典型的 config.yaml 简化版,我用注释标了每个字段的作用:
# config.yaml account: save_login: true # 是否保留登录状态,避免每次重启扫码 max_retry: 3 # 连接断开后的最大重试次数 auto_reply: enable: true rules: - match_type: keyword # 匹配方式:keyword / regex / exact keyword: "在吗" reply: "在的,有什么可以帮你?" at_only: true # 是否只在被 @ 时触发 cooldown: 30 # 同一触发词 30 秒内不重复回复 forward: enable: true mappings: - from: "群A" to: ["群B", "好友C"] filter: contain: "日报" # 只转发包含“日报”的消息 exclude: "测试" # 包含“测试”则不转发 anti_recall: enable: true cache_expire: 3600 # 撤回缓存保留 1 小时 stats: enable: true db_path: "data/stats.db" # 留言统计存 SQLite aggregate: daily # 聚合周期:daily / weekly看到这里你应该能发现,自定义程度高指的是:同样一个包,你只开自动回复,不开转发,完全可行;自动回复可以只在被 @ 时响应,也可以所有消息都回;转发可以只转含指定关键词的消息。这种组合自由度,才是这个标题里“高度自定义”的真正含义。
2.4 第一次启动:从日志判断它是不是真的活着
环境装好、配置改完,就可以启动看看了。启动命令通常很简单,但启动后别急着去发消息测试,先盯半分钟日志,确认三件事:第一,连接是否建立;第二,事件监听是否注册成功;第三,是否进入心跳状态。
python main.py --config config.yaml # 正常日志大概长这样: # [INFO] 加载配置完成: config.yaml # [INFO] 微信连接已建立, 账号: xxx # [INFO] 插件注册完成: auto_reply, forward, anti_recall, stats # [INFO] 进入心跳监听, 每 30 秒检查一次连接状态启动参数里的 --config 用来指定配置文件,如果同一个包要管多个账号,可以复制多份配置轮流用。日志中“插件注册完成”这一行特别重要——如果这里少了某个模块,不是主程序坏了,是你没把对应开关打开,或者插件文件放错了位置。心跳日志则是判断长连接是否稳定的指标,30 秒一次是常见节拍;如果心跳中断又恢复,说明网络不稳定,后续会出现消息漏收。
3. 自动回复与消息转发:规则怎么定,消息怎么流
3.1 自动回复的四层匹配逻辑与冷却时间
自动回复看着简单,真放在生产环境里翻车最多的就是它。最常见的错误是把回复规则写成一长串 if-else,结果规则越来越多,互相覆盖,最后回什么全靠运气。常见做法是把匹配抽成四层,每层有明确优先级:精确匹配 > 关键词匹配 > 正则匹配 > 兜底回复。
精确匹配用于命令类消息,比如用户发“help”只回复帮助菜单,其他消息绝不能触发;关键词匹配用于业务咨询,比如内含“价格”“报价”就回复报价单关键词;正则匹配处理一类模式,比如手机号、订单号;兜底回复留给没接住的消息,通常回一句“已记录,会有人工跟进”。匹配层之间用优先级序号控制,命中即返回,不再往下层跑。
冷却时间(cooldown)是另一个容易忽略的参数。没有冷却时间时,群里有人连发十条“在吗”,机器人就连回十条,不但刷屏,还容易触发平台的频控。实践中我一般对同一触发词设置 30 到 60 秒冷却,对同一用户设置更细粒度的限流。下面这段伪代码概括了带冷却和优先级的路由逻辑:
# auto_reply.py —— 匹配优先级与冷却判断 import time last_trigger = {} def should_cooldown(rule_id, user_id, cooldown_sec): key = f"{rule_id}:{user_id}" now = time.time() if key in last_trigger and now - last_trigger[key] < cooldown_sec: return True last_trigger[key] = now return False def match_rule(msg, rules): for priority in ["exact", "keyword", "regex", "fallback"]: pool = [r for r in rules if r["match_type"] == priority] for rule in pool: if priority == "exact" and msg == rule["keyword"]: return rule if priority == "keyword" and rule["keyword"] in msg: return rule if priority == "regex" and __import__("re").search(rule["keyword"], msg): return rule if priority == "fallback": return rule return Nonematch_rule 按优先级顺序遍历规则池,命中即返回,不会出现低优先级规则抢跑的情况。should_cooldown 用“规则 ID + 用户 ID”做联合键,避免一个用户刷屏导致所有人被限制。参数建议:精确匹配规则不放冷却也可以,因为命令本身信息密度高;关键词和正则一定要配冷却,否则同一句触发语反复出现时会变成话痨。
3.2 转发链路:源到目标的桥梁,以及防循环机制
消息转发的配置核心是“映射表”:一个来源可以对应多个目标,来源可以是群也可以是好友,目标同理。很多人在这一步踩到同一个坑——只配了来源和去向,忘了配过滤条件,结果群里所有消息都被搬到另一个群,噪音巨大。合理做法是给每条转发规则加 contain/exclude 过滤,把“转发所有”变成“转发感兴趣的”。
更重要的坑是转发循环。假设你在群 A 配置了“把消息转发到群 B”,同时群 B 又配置了“把消息转发到群 A”,一条消息会在两个群里转圈,直到平台限流或程序卡死。我做转发模块时有个习惯:无论配置里有没有要求,每条被转发的消息都打一个本地标记(比如来源群 ID + 消息 ID),转发前先查标记,存在就直接丢弃。
# forward.py —— 带去重标记的转发模块 import hashlib forward_cache = set() def build_marker(msg, source_id): raw = f"{source_id}:{msg.id}" return hashlib.md5(raw.encode()).hexdigest() def should_forward(msg, rule): marker = build_marker(msg, rule["from"]) if marker in forward_cache: return False forward_cache.add(marker) text = msg.text or "" if rule["filter"].get("contain") and rule["filter"]["contain"] not in text: return False if rule["filter"].get("exclude") and rule["filter"]["exclude"] in text: return False return True很多人以为防循环靠配置约束就够了,实际上一旦机器人数量超过两个,人脑根本管不住转发关系。用消息 ID 计算指纹做去重,是从根上解决问题的手段。缓存集合建议每 24 小时清一次——转发消息的指纹只用于去重,不需要长期保留,否则内存会缓慢上涨。
3.3 参数调优:自动回复与转发共用的关键清单
这套参数在不同场景下差异很大,我按内部值班和公开群运营两种场景整理了一张对照表。同一套机器人,内部值班可以把冷却设短、转发放全量;公开群运营则要把回复收敛、转发收紧。
| 参数 | 内部值班取值 | 公开群运营取值 | 说明 |
|---|---|---|---|
| cooldown | 5-10 秒 | 60-120 秒 | 公开群消息噪声大,冷却不足会刷屏 |
| at_only | false | true | 公开群建议只在被 @ 时回复 |
| forward.contain | 留空 | 指定关键词 | 公开群无过滤转发必被屏蔽 |
| forward.exclude | 留空 | 广告/链接类词 | 过滤明显垃圾消息 |
| max_retry | 5 | 3 | 内部环境网络稳定,可放宽重试 |
| stats.aggregate | daily | weekly | 公开群数据量大,按周聚合更稳 |
参数没有绝对标准,但有一套安全底线:任何面向陌生用户的场景,自动回复都建议 at_only: true,转发都建议配 contain 过滤。没有这两条,机器人上线一小时内就会变成群里的噪音源。
4. 防撤回与留言统计:数据层怎么设计,才不漏不重
4.1 防撤回:先缓存后补发,核心在文件备份
防撤回的原理不复杂:机器人监听到新消息时,先把消息内容写入本地缓存;如果后续出现“撤回通知”事件,就从缓存里把原消息捞出来,按配置重新发到会话里。难点不在逻辑,而在缓存什么、缓存多久、遇到图片视频怎么处理。
文本消息缓存的是字符串,体积小,存 SQLite 或内存集合都行。图片和视频消息则麻烦得多,因为它们不是以文本内容形式到达的,而是一个文件路径或消息 ID。如果只缓存消息 ID,对方撤回后你拿着 ID 也取不到内容;常见做法是收到媒体消息时立即把文件复制到本地 data/cache 目录,再把文件路径和消息 ID 关联存储。这里我有一段常见的落盘思路:
# anti_recall.py —— 媒体消息先落盘再缓存 cache_pool = {} def on_new_message(msg): if msg.type == "image": local_path = download_to_local(msg.image_path, "data/cache") cache_pool[msg.id] = {"type": "image", "path": local_path} elif msg.type == "text": cache_pool[msg.id] = {"type": "text", "content": msg.text} # 定期清理:超过 cache_expire 的条目直接删 def on_recall_event(msg): original = cache_pool.get(msg.origin_id) if not original: return if original["type"] == "text": send_text(msg.conversation_id, f"【防撤回】{original['content']}") elif original["type"] == "image": send_image(msg.conversation_id, original["path"])download_to_local 是我自己封装的一个函数,本质是把缓存文件复制到本地目录。这步必须做,否则撤回发生后源文件被平台清理,补发就成了空话。cache_expire 建议设 1 到 2 小时,太短起不到防撤回作用,太长又占磁盘。
需要提醒的是,防撤回功能涉及他人隐私边界,合规用法是只处理你所在且有管理职责的会话,用于内部资料留存,而不是拿来监视对话双方。如果你部署的会话里有大量不相关用户,建议直接关掉这个模块。
4.2 留言统计:一张表加三个查询,周报就出来了
留言统计和防撤回不一样,它没有实时性压力,核心是把“谁在哪个会话说了什么、什么时候说的”存下来,方便后续按人、按日、按关键词聚合。通常用 SQLite 就够了,数据量大再换 MySQL 也不迟。
建表字段不必贪多,核心就五个:消息 ID、会话 ID、发送者 ID、消息文本、接收时间。下面这条建表语句几乎可以原样复用:
CREATE TABLE IF NOT EXISTS messages ( msg_id TEXT PRIMARY KEY, conv_id TEXT NOT NULL, sender_id TEXT NOT NULL, content TEXT, created_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_conv_time ON messages(conv_id, created_at);msg_id 用平台侧消息 ID,天然唯一;conv_id 区分群和单聊;created_at 存 Unix 时间戳,比字符串好排序。索引建在 conv_id + created_at 上,是因为最常见的查询是“某个会话最近一段时间的留言”,这个联合索引能让查询走索引而不是全表扫。统计查询上,常用的是这三个。
-- 统计某人当天的发言条数 SELECT sender_id, COUNT(*) AS cnt FROM messages WHERE conv_id = '群A' AND created_at >= :start_of_day GROUP BY sender_id ORDER BY cnt DESC; -- 统计当天各时段活跃度 SELECT strftime('%H', created_at, 'unixepoch', 'localtime') AS hour, COUNT(*) AS cnt FROM messages WHERE created_at >= :start_of_day GROUP BY hour; -- 检索包含某个关键词的留言 SELECT sender_id, content, created_at FROM messages WHERE conv_id = '群A' AND content LIKE '%日报%' ORDER BY created_at DESC LIMIT 50;注意 strftime 里的 localtime 参数,它把 Unix 时间戳转成本地时区的小时,否则统计结果会整体偏移 8 小时。这是留言统计最容易出错的细节,我最早一份周报就是没加 localtime,每天的数据全部错位,看起来凌晨最活跃,实际上是从上午被平移过去的。
4.3 与主循环集成:事件分发顺序决定数据完整性
防撤回和统计都不是独立程序,它们都依赖主循环把消息事件分发给各个插件。集成时最关键的一点是分发顺序:统计模块应该最先执行并落库,防撤回模块其次缓存,自动回复和转发最后响应。原因很简单,自动回复产生的新消息也会被统计模块记录,如果统计模块晚于自动回复执行,机器人自己说的话就会被漏掉。
# main.py —— 事件循环注册顺序 handlers = [ stats.on_message, # 1. 原始消息先入库 anti_recall.on_message, # 2. 防撤回缓存 auto_reply.on_message, # 3. 自动回复 forward.on_message, # 4. 转发 ] for handler in handlers: handler(event)顺序上先存原始数据,再做业务响应,这样无论机器人回复了什么、转发了什么,统计表里的数据都是完整闭环。如果你拿到手的 zip 包内部顺序不对,改 plugins 注册列表里的顺序即可,不需要动核心代码。这项调整往往被人忽视,但数据完整性全靠它撑着。
5. 微信机器人避坑:账号风控、消息遗漏与稳定运行
5.1 现象:启动后日志显示连接建立,但发消息完全没反应
原因多半不是代码坏了,而是消息回调没有正确订阅。有些实现里需要显式开启“接收消息通知”事件,配置文件里默认是关的,尤其是群消息,默认可能只接收被 @ 的消息。
解决:检查 config 中消息事件订阅的开关,把群聊消息的接收策略从“仅被 @ 时”改成“全部接收”。还要确认会话列表里是否真的有目标群,机器人不会自动同步所有群聊,有些包需要手动把群加入监听白名单。
5.2 现象:自动回复偶尔失灵,群里聊十句才回一句
这是最像玄学的一类问题。排掉网络因素后,大概率是规则匹配没有命中。比如用户发的是“在么”,你配置的关键词是“在吗”,一字之差就漏接。另一类原因是冷却时间设置过长,用户 1 分钟内问了三次,只回了第一次,后两次被冷却拦截。
解决:先在日志里看原始消息内容,确认消息文本里的实际措辞。如果是近义词漏接,把 match_type 从 keyword 改成 regex,再用“在么|在吗|在不在”做正则。如果是冷却拦截,把 cooldown 从 60 秒调低到 10 秒,或者只在同一用户重复触发时才生效。
5.3 现象:两个群之间消息来回转发,像回声一样停不下来
这就是第 3 章提到的转发循环。光靠转发映射表看不出问题,因为循环是跨机器人的——A 机器人把消息转到群 B,B 机器人又把消息转回群 A。没有消息指纹去重时,每转一圈就会出现一条新消息,实际上已经死循环了。
解决:确认转发模块有基于 message_id 的去重逻辑。开发层面可以给每条转发消息压一个不可见的标记字段,收到消息时先检查标记,如果发现“这条消息来源是机器人自己”,直接丢弃。更省事的做法是转发规则里强制加 filter.contain,让转发只发生在特定主题消息上,缩小循环面。
5.4 现象:防撤回的图片补发过来是空白或已失效
原因几乎可以锁定在媒体文件的缓存策略上。我排查过一类典型场景:anti_recall 模块拿到的是图片的文件路径,但它没有立即复制到本地,而是只存了路径。对方撤回后,源缓存被平台清理,路径就成了死链。
解决:收到媒体消息时立刻下载到本地 data/cache 目录,然后再缓存路径。同时给缓存目录加一个定期清理任务,按文件创建时间删除超过 24 小时的内容,防止磁盘空间被挤爆。文本消息不存在这个问题,但媒体消息不落盘就等于白做。
5.5 现象:账号被风控,提示需要重新验证或无法登录
这一类问题最严重,处理也最谨慎。触发风控的常见原因有三个:短时间内发送频率过高、新设备登录后立刻开始群发、长时间高频转发相同内容。机器人不会像人一样控制节奏,配置里的 cooldown 一旦放开,它就能在几秒内连发几十条,非常容易被判定为异常行为。
解决:第一,所有对外回复和转发都设置节流,每分钟不超过 10 到 15 条,这是多数场景下比较安全的节奏;第二,测试时不要拿常用主账号跑,用一个独立的小号,封了也不影响日常沟通;第三,如果账号已经触发验证,停掉机器人,让账号先休息一段时间,不要反复尝试登录。别指望机器人能绕过风控,任何这类尝试都不在技术落地范围里,最好的态度就是“慢一点,稳一点”。
6. 进阶技巧:把同一套机器人做成多账号与留言周报
6.1 多账号并行:一份代码,多份配置
同一台机器上跑多个账号,不用改任何代码,复制配置文件再改 account 字段即可。每个账号一个配置文件,一个独立的进程,日志按账号名分流,互不干扰。实际操作时注意 data 目录也要分账号隔离,否则两个进程同时写一个 SQLite 会出现数据库锁冲突。
6.2 把留言统计变成一份可读的周报
统计模块的数据不只是在后台躺着,可以写一个汇总脚本,每周一把上一周的留言数据聚合成 Markdown 日报或周报,主要内容包括:整体留言量、人均留言量、最活跃时段、高频关键词 Top 10。这比手动翻聊天记录高效得多,也是我认为这个方向真正做到降本增效的落地场景。
我自己的习惯是把统计结果和防撤回共用一份数据库,每周跑一次周报脚本,产物丢进团队内部文档里。跑了几周之后,我已经不需要翻聊天记录就能知道哪一天消息量异常、哪个话题讨论最密集。如果你也打算长期维护这套机器人,建议从一开始就把 data 目录纳入定期备份,日志保留一周即可,但留言统计数据值得长期留存。
最后一句实在话:这类机器人真正会卡住你的,不是代码,而是边界。规则别一次定太满,先从最小可用集跑一周,再逐步放开;账号务必用测试号起步;防撤回别在陌生人会话里开。这是我用掉了好几次账号教训换来的习惯,希望帮到你。
本文还有配套的精品资源,点击获取