
即时通讯后端AI 应用【免费下载链接】wechatferry-项目地址https://gitcode.com/gh_mirrors/wec/wechatferry点击查看免费下载本文围绕 wechatferry 仓库中的 Wechaty 集成文档 docs/integrations/wechaty.md 展开讲解如何通过wechatferry/puppet把 wechatferry基于 PC Hook 的微信机器人协议客户端接入 Wechaty 机器人框架从安装、最小可用示例DingDong 机器人、消息事件管线到自定义缓存管理器CacheManager的完整实现路径。读完本篇你将能够独立搭建一个可运行的 Wechaty WechatFerry 机器人并理解 Puppet 内部的登录、缓存与消息分发机制必要时自行替换缓存层。Wechaty 在 wechatferry 集成体系中的位置wechatferry 是 Node 生态下针对 WeChatFerryPC Hook 协议的第三方客户端其包结构按“底层到上层”分为三层wechatferry/core封装 sdk.dll 与 TCP 连接、wechatferry/agent封装核心、更易用的事件式 Agent、wechatferry/puppet对接 Wechaty 的 Puppet 实现。README 中对该包的定位非常直白wechatferry/puppet 的目标是“让 Wechaty 免费协议PC Hook重放荣光”。在 集成指南 中官方给出了三种集成方式Nuxt 模块、Wechaty Puppet、纯 NodeAgent。而 Node 集成文档 中有明确建议始终推荐你使用 Wechaty而不是直接使用 AgentWechaty 能极大的减少你的心智负担。也就是说Wechaty 集成是项目主推的用法之一。Wechaty 本身是一个历史悠久、API 抽象良好的微信机器人框架提供 Bot/Contact/Room/Message 等统一领域模型允许通过替换 Puppet 接入不同协议端因此wechatferry/puppet的职责就是把 WechatFerry 协议的能力映射到 Wechaty 的 Puppet 抽象上让所有 Wechaty 生态代码可以直接跑在 PC Hook 协议上。适用前提来自 集成指南需要 64 位 Windows 系统需要受支持版本的微信客户端文档标注当前需要的版本为 3.9.12.17并提前打开登录机器人账号仓库 README 同时包含免责声明本项目仅供学习交流请遵守相关法规与授权边界。安装Wechaty 集成需要同时安装 Wechaty 本体与wechatferry/puppet两个依赖支持 pnpm / yarn / npm# pnpm pnpm add wechaty wechatferry/puppet # yarn yarn add wechaty wechatferry/puppet # npm npm install wechaty wechatferry/puppet从 packages/puppet/package.json 可以看到wechatferry/puppet的核心运行时依赖包括依赖作用wechatferry/agentworkspacePuppet 的底层数据来源提供 wcf 能力wechatferry/coreworkspace由 agent 传递依赖封装 dll 与 TCP 连接wechaty-puppet(^1.20.2)Wechaty Puppet 基类与 payload 类型unstorage(^1.15.0)默认缓存实现UnstorageCacheManager的存储后端knex(^3.1.0)由 agent 用于直接查询微信数据库file-box图片/文件消息的统一 FileBox 抽象p-retry缓存刷新联系人/群/成员的重试机制xml2js解析 AppMsg 等 XML 负载名片、链接、朋友圈等最小可用示例DingDong 机器人文档给出的最小示例是一个经典的 DingDong 机器人收到文本ding就回复dong。完整代码与逐行说明如下// index.ts import { WechatferryPuppet } from wechatferry/puppet import { WechatyBuilder } from wechaty const puppet new WechatferryPuppet() const bot WechatyBuilder.build({ puppet }) bot.on(message, (msg) { msg.text() ding msg.say(dong) }) .start()逐行解析new WechatferryPuppet()创建 Puppet 实例。构造函数接受一个可选的PuppetWcferryUserOptions定义见 packages/puppet/src/types.tsexport interface PuppetWcferryUserOptions { agent?: WechatferryAgent /** 自定义缓存管理器 */ cacheManager?: CacheManager }不传任何参数时resolvePuppetWcferryOptionspackages/puppet/src/puppet.ts#L17-L23会补齐默认值agent new WechatferryAgent()、cacheManager new UnstorageCacheManager(createStorage())。也就是说默认情况下 Puppet 内部自己创建 Agent 与基于内存的 unstorage 缓存。WechatyBuilder.build({ puppet })用指定的 Puppet 构建 Bot。你也可以在这里挂接 storage持久化、logger 等 Wechaty 标准组件。bot.on(message, ...)Wechaty 的领域事件监听msg.say(dong)最终会落到 Puppet 的messageSendText。bot.start()启动 Wechaty进而触发 Puppet 的onStart。启动与登录的底层调用链WechatferryPuppet继承自wechaty-puppet的Puppet基类其启动流程在 packages/puppet/src/puppet.ts 中可以完整看到onStart()puppet.ts#L42-L49注册 Agent 的login/logout/error事件转发然后调用this.agent.start()启动底层 wcf 连接并启动 Puppet 心跳见下文。Agent 侧的登录检测WechatferryAgent.start()packages/agent/src/agent.ts#L41-L50会wcf.start()、启动每秒一次的checkLoginStatus轮询、监听message/sended事件并转发检测到登录状态变化时 emitlogin携带用户信息或logout。login(userId)puppet.ts#L51-L65Puppet 收到 login 后执行loadContactList()→loadRoomList()→updateContactCache(userId)把全量联系人和群列表含每个群成员写入缓存随后调用基类super.login()、emitready并把onMessage绑定到 Agent 的消息事件上——此后每条微信消息都会进入事件管线下一节。onStop()停止心跳、移除 Agent 全部监听并agent.stop()Agent 侧会先 emitlogout再断开 wcf 连接。此外还有两个值得注意的机制登录二维码authQrCodegetter 会调用this.agent.wcf.refreshQrcode()刷新二维码puppet.ts#L81-L89Wechaty 的扫码登录流程可以据此获取二维码。Puppet 心跳startPuppetHeart每 15 秒 emit 一次heartbeatpuppet.ts#L1015-L1025用于让 Wechaty 的 Puppet 守护机制认为该 Puppet 存活。消息事件管线一条消息如何变成 Wechaty 事件onMessagepuppet.ts#L91-L146是整个 Puppet 的核心分发点处理顺序为系统消息兜底若消息带roomid但没有sender把 sender 置为fmessage微信系统账号保证后续解析不会因空 sender 失败cacheManager.setMessage(messageId, message)先把原始消息写入缓存这是后续messageRawPayload能取到历史消息的前提parseEvent(puppet, message)按注册的解析器链判定事件类型按事件类型 emit 对应的 Wechaty Puppet 事件。事件类型定义在 packages/puppet/src/events/events.tsexport enum EventType { Message, // 普通消息 Post, // 朋友圈 Friendship, // 好友请求/变更 RoomInvite, // 群邀请 RoomJoin, // 入群 RoomLeave, // 退群/被移出 RoomTopic, // 群名变更 }各解析器在 packages/puppet/src/events/index.ts 中通过addEventParser注册parseEvent依次尝试每个解析器第一个成功解析出 payload 的即为最终事件类型全部失败则兜底为普通Message事件。分发逻辑中的几个细节Messageemitmessage仅携带messageIdpayload 从缓存读取——这就是示例中bot.on(message)的来源PostemitpostWechaty 侧对应朋友圈能力postSearch通过messageSearch按Message.Post类型检索实现见 puppet.ts#L785-L808Friendship先把 friendship payload 写入缓存再 emitfriendshipRoomInvite缓存群邀请后 emitroom-inviteRoomJoin仅 emitroom-join注释明确写着DO NOT UPDATE LISTS HERE不在事件流里同步刷新成员列表RoomLeaveemitroom-leave并对每个被移出成员执行cacheManager.deleteRoomMember(roomId, memberId)保持缓存一致RoomTopicemitroom-topic后触发updateRoomCache(roomId)刷新群信息。自发消息的回补机制PC Hook 协议下“发送成功”与“消息入库”存在时间差。Puppet 通过onSendMessage(timeout 5)puppet.ts#L148-L173轮询agent.getLastSelfMessage(localId)最多 5 次、每次间隔 1 秒等待自己发出的消息落到消息库然后把它转换为事件消息、写入缓存并 emitmessage——这样 Wechaty 中say()之后也能收到自己的message事件行为与其他 Puppet 保持一致。所有messageSend*方法末尾都会调用该回补。群消息的 能力messageSendTextpuppet.ts#L408-L455对群聊做了增强显式传入mentionIdList时按成员 wxid 群发 文本中包含all时自动替换为所有人并 全群内部使用notifyall文本匹配[mention:...]占位符时通过mentionTextParser解析、从缓存取群成员列表getRoomMemberList替换成真实昵称再调用agent.sendText(conversationId, text, mentions)发送。能力边界哪些 API 可用哪些不可用wechatferry/puppet参考了 puppet-padlocal 的设计但并非 Puppet 规范的完整实现。从 puppet.ts 的源码可以梳理出明确的能力清单已实现可直接调用联系人contactList/contactRawPayload/contactAlias只读/contactAvatar以FileBox.fromUrl返回头像 URL/tagContactList只读消息messageRawPayload(Parser)/messageImage/messageFile图片、视频、文件、表情/messageRecall基于agent.revokeMsg/messageUrl解析 AppMsg XML/messageMiniProgram/messageContact解析名片消息并回填一个“伪联系人”进缓存发送messageSendText含群 能力/messageSendFile按 mediaType 分流图片与普通文件/messageSendUrl富文本链接额外支持name/account两个扩展字段见文件末尾的declare module声明/messageForward群聊roomList/roomRawPayload(Parser)/roomTopic、roomAnnounce只读/roomAvatar/roomMemberList/roomMemberRawPayload/roomAdd注意成员数超过 40 时自动改用“邀请入群”而非直接添加puppet.ts#L550-L569/roomDel/roomInvitationRawPayload朋友圈postSearch仅 Moment/postRawPayload(Parser)解析 XML 并顺带把附属消息写入缓存。未实现调用即抛出Method not supported错误contactSelfQRCode/contactSelfName/contactSelfSignature、contactPhone/contactDescription/contactCorporationRemark、conversationReadMark、messageSendContact/messageSendLocation/messageSendMiniProgram/messageLocation、roomCreate/roomQuit/roomQRCode群二维码以及群公告写入、全部friendshipSearch/Add/Accept、标签增删tagContactAdd/Remove/Delete、postPublish/tap/tapSearch等。使用建议在编写业务前先对照上面清单确认目标能力是否在“已实现”列表内不可用的能力直接抛错属于设计预期而非故障。缓存管理为什么需要 CacheManager以及如何自定义这是文档中最容易被忽视、却最能体现 wcf 协议特点的一节。PC Hook 方案的特点是数据源是本地微信数据库 实时事件流。对于“当前正在发生的”事件消息、入群、好友请求等原始负载在事件到达后会丢失事件只带 ID而 Wechaty 的 Puppet 协议又要求随时能通过messageRawPayload(id)/roomRawPayload(id)等接口按 ID 取回 payload。因此wechatferry/puppet把“按 ID 取数据”这一层抽象成了独立的CacheManager接口文档原文也点明了这一点“wcf 使用的是 hook 方案不一定需要缓存所以你可以自己实现从 agent 中直接查询”。CacheManager接口完整定义在 packages/puppet/src/cache-manager/cache-manager.ts按领域分为五组分组关键方法MessagegetMessage/setMessage/hasMessageContactgetContact/setContact/deleteContact/getContactIds/getAllContacts/hasContact/getContactCount/setContactListRoomgetRoom/setRoom/deleteRoom/getRoomIds/getRoomCount/hasRoom/setRoomListRoom MembergetRoomMember/setRoomMember/deleteRoomMember/setRoomMemberList/getRoomMemberList/getRoomMemberIds其他getFriendship/setFriendshipgetRoomInvitation/setRoomInvitation/deleteRoomInvitation所有方法均允许返回T | PromiseTMaybePromise因此同步内存实现与异步 Redis/数据库实现都可以直接适配。默认实现UnstorageCacheManager默认实现 UnstorageCacheManager 基于unstorage按前缀划分为多个命名空间wcf:message // 消息 wcf:contact // 联系人 wcf:room // 群 wcf:room-invitation // 群邀请 wcf:friendship // 好友关系 wcf:room-member:roomId // 每个群一个成员命名空间构造时传入任意 unstorageStorage实例即可Puppet 默认使用createStorage()内存后端。这意味着默认情况下缓存是进程内存级的机器人重启后清空登录时会通过loadContactList/loadRoomList重新全量灌入puppet.ts#L995-L1013但历史消息 ID 不会恢复——重启后对旧消息 ID 调用messageRawPayload会抛出Message not found错误。自定义缓存接口写法与完整示例按文档的写法实现CacheManager接口即可import { CacheManager } from wechatferry/puppet class MyCacheManager implements CacheManager { // pass }实际项目中需要补全全部方法。下面给出一个“无缓存”风格的示意实现——即把消息类查询直接丢弃、把联系人/群查询委托给 Agent 实时查询帮助你理解接口各方法的语义实际可用版本还需处理Promise化与批量方法import type { CacheManager } from wechatferry/puppet import type { WechatferryAgentContact, WechatferryAgentChatRoom, WechatferryAgentChatRoomMember, WechatferryAgentEventMessage, } from wechatferry/agent import type * as PUPPET from wechaty-puppet class InMemoryCacheManager implements CacheManager { private messages new Mapstring, WechatferryAgentEventMessage() private contacts new Mapstring, WechatferryAgentContact() private rooms new Mapstring, WechatferryAgentChatRoom() private members new Mapstring, Mapstring, WechatferryAgentChatRoomMember() // Message getMessage(id: string) { return this.messages.get(id) ?? null } setMessage(id: string, p: WechatferryAgentEventMessage) { this.messages.set(id, p) } hasMessage(id: string) { return this.messages.has(id) } // Friendship private friendships new Mapstring, PUPPET.payloads.Friendship() getFriendship(id: string) { return this.friendships.get(id) ?? null } setFriendship(id: string, p: PUPPET.payloads.Friendship) { this.friendships.set(id, p) } // Contact getContact(id: string) { return this.contacts.get(id) ?? null } setContact(id: string, p: WechatferryAgentContact) { this.contacts.set(id, p) } deleteContact(id: string) { this.contacts.delete(id) } getContactIds() { return [...this.contacts.keys()] } getAllContacts() { return [...this.contacts.values()] } hasContact(id: string) { return this.contacts.has(id) } getContactCount() { return this.contacts.size } setContactList(payload: WechatferryAgentContact[]) { for (const c of payload) this.contacts.set(c.userName, c) } // Room getRoom(id: string) { return this.rooms.get(id) ?? null } setRoom(id: string, p: WechatferryAgentChatRoom) { this.rooms.set(id, p) } deleteRoom(id: string) { this.rooms.delete(id) } getRoomIds() { return [...this.rooms.keys()] } getRoomCount() { return this.rooms.size } hasRoom(id: string) { return this.rooms.has(id) } setRoomList(payload: WechatferryAgentChatRoom[]) { for (const r of payload) this.rooms.set(r.userName, r) } // Room Invitation private invitations new Mapstring, PUPPET.payloads.RoomInvitation() getRoomInvitation(id: string) { return this.invitations.get(id) ?? null } setRoomInvitation(id: string, p: PUPPET.payloads.RoomInvitation) { this.invitations.set(id, p) } deleteRoomInvitation(id: string) { this.invitations.delete(id) } // Room Member private memberMap(roomId: string) { if (!this.members.has(roomId)) this.members.set(roomId, new Map()) return this.members.get(roomId)! } getRoomMember(roomId: string, contactId: string) { return this.memberMap(roomId).get(contactId) ?? null } setRoomMember(roomId: string, contactId: string, p: WechatferryAgentChatRoomMember) { this.memberMap(roomId).set(contactId, p) } deleteRoomMember(roomId: string, contactId: string) { this.memberMap(roomId).delete(contactId) } setRoomMemberList(roomId: string, payload: WechatferryAgentChatRoomMember[]) { const m this.memberMap(roomId) for (const member of payload) m.set(member.userName, member) } getRoomMemberList(roomId: string) { return [...this.memberMap(roomId).values()] } getRoomMemberIds(roomId: string) { return [...this.memberMap(roomId).keys()] } }注入方式则是利用PuppetWcferryUserOptionsimport { WechatferryPuppet } from wechatferry/puppet import { WechatyBuilder } from wechaty const bot WechatyBuilder.build({ puppet: new WechatferryPuppet({ cacheManager: new InMemoryCacheManager(), // agent 也可以同时自定义 // agent: new WechatferryAgent({ wcf: myCore }), }), })几点实践提示若需要跨重启保留会话上下文例如记住某条消息 ID 用于messageForward可以基于UnstorageCacheManager的存储构造方式换用 Redis/SQLite 等持久化 unstorage driver而不是从零实现接口若你的架构是“每条消息都实时落库、随时可从数据库重建”则完全可以像文档建议的那样实现一个“直查 Agent/数据库”的 CacheManager跳过内存缓存缓存刷新类操作updateContactCache/updateRoomCache/updateRoomMemberCache等puppet.ts#L901-L993内部使用p-retry重试 3 次命中缓存 miss 时会回源 Agent 查询并回写替换缓存实现时注意保持“miss 可回源”的语义。生产化建议安全模式包装器文档主文之外仓库还提供了一个与 Wechaty Puppet 直接配套的节流插件wechatferry/plugins的createSafeModePuppetdocs/plugins/safe-mode.md。它通过Proxy拦截所有以messageSend开头的方法实现三级限速packages/plugins/src/safe-mode/safe-mode.ts#L5-L38维度默认值同一会话发送间隔1 秒/条不同会话之间的间隔3 ~ 5 秒随机全局发送上限40 条/分钟import { createSafeModePuppet } from wechatferry/plugins import { WechatferryPuppet } from wechatferry/puppet const puppet createSafeModePuppet(new WechatferryPuppet())由于它包装的是 Puppet 实例本身可以与任意 Wechaty 构建方式自由组合。文档同样强调安全模式只控制发送频率不是绕过风控的魔法。测试用例中的集成验证方式仓库的 test/robot.test.ts 展示了在不启动真实微信进程的前提下如何验证 Puppet 集成值得参考其思路用Proxy包装Wechatferrycore实例把所有send*方法替换为打日志的空实现getSelfWxid返回固定的filehelper用该 core 构造WechatferryAgent再new WechatferryPuppet({ agent })最后经createSafeModePuppet包装后交给WechatyBuilder.build({ puppet })通过agent.emit(message, fakeMessage)注入伪造的系统消息如你邀请小茸茸加入了群聊断言room-join/room-leave事件解析出的群 ID、邀请者/被邀请者名称是否正确——这直接验证了上文“事件管线”中RoomJoin/RoomLeave解析器与昵称提取逻辑safeMode用例则连发 10 条消息验证节流包装不阻塞正常发送。这种“core 打桩 agent 注入消息”的模式也为在自己的项目中为wechatferry/puppet编写集成测试提供了模板。小结Wechaty 是 wechatferry 主推的集成方式之一安装wechatywechatferry/puppet即可运行WechatferryPuppet默认自建WechatferryAgent与内存UnstorageCacheManager消息流为Agent 事件 → 写缓存 →parseEvent分类Message/Post/Friendship/RoomInvite/RoomJoin/RoomLeave/RoomTopic→ emit Wechaty Puppet 事件自发消息通过轮询getLastSelfMessage回补CacheManager是理解该 Puppet 的关键抽象它解耦了“按 ID 取 payload”的实现可按需替换为内存、持久化 unstorage driver 或直查 Agent/数据库的实现编写业务前先对照能力清单Method not supported的方法较多如roomCreate、roomQRCode、朋友圈发布等并建议用createSafeModePuppet包装以控制发送频率运行前提64 位 Windows 指定版本的微信客户端当前文档标注 3.9.12.17并遵守仓库 README 的使用声明。相关源码入口WechatferryPuppet 主实现、CacheManager 接口、默认 unstorage 缓存实现、事件解析注册、Puppet 选项类型、集成测试、原始集成文档。赞分享即时通讯后端AI 应用【免费下载链接】wechatferry-项目地址https://gitcode.com/gh_mirrors/wec/wechatferry点击查看免费下载相关推荐wechatferry 微信机器人框架完全指南从 sdk.dll 桥接到 Wechaty 生态的 TypeScript 实践wechatferry 微信机器人框架完全指南从 sdk.dll 桥接到 Wechaty 生态的 TypeScript 实践 wechatferry 是一个基即时通讯后端AI 应用如何快速掌握WeChatFerry微信机器人2025完整实践指南WeChatFerry是一款功能强大的微信机器人底层框架通过Hook技术深度集成微信客户端支持接入各类AI大模型让开发者能够轻松构建智能对话机器人。无论你微信机器人开发实战WeChatFerry框架完整指南微信机器人开发实战WeChatFerry框架完整指南 还在为微信机器人开发头疼吗想要快速构建智能对话助手却不知从何下手今天我们就来深度解析WeChatFe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考