cover_image

Wechaty 使用网页版微信实现机器人的思路

koofe KooFE前端团队
2022年09月12日 23:07

之前写了一篇介绍 「Wechaty 实现微信聊天机器人」的文章,对如何使用 Wechaty 做了简单介绍,它既可以对聊天信息进行监听,也可以通过 API 发送聊天信息。本文主要介绍它如何使用网页版微信实现机器人,为了让大家有更直观的认识,可以看一下下面的视频演示:

视频加载失败,请刷新页面再试

刷新

Wechaty 经过有五、六年的开发和迭代,功能相对来说已经比较稳定,但是也存在因微信升级导致不可用的风险。截止本文写作时(22 年 9 月),Wechaty 依然是可用的,如果您对此有任何疑问,请自己动手安装试用一下。

Wechaty 是一个比较受欢迎的微信机器人工具,该项目在 npm 下载量、github stars 数量如下所示:

图片 图片

github stars 的趋势:

图片

简介

Wechaty 是一个构建聊天机器人的通用 SDK,需要通过不同的 Puppet Provider 来实现各种 IM 聊天。Wechaty 支持的聊天工具包括 WhatsApp、WeChat、 WeCom、Gitter 以及 Lark,只需要使用相应的 Puppet Provider 即可。本文重点介微信机器人,下面表格中列出了微信(WeChat)的 Puppet Provider:

ProtocolPuppet Provider
WebPuppetPuppeteer
WindowsPuppetWxwork
MockPuppetMock
WebPuppetWechat4u
iPadPuppetRock
iPadPuppetPadLocal
WindowsPuppetDonut
iPadPuppetPadpro DEPRECATED
iPadPuppetPadchat DEPRECATED
iPadPuppetPadplus DEPRECATED
MacPuppetMacpro DEPRECATED

不难发现支持微信的 Provider 比较多,而且各个 Provider 实现思路也不一样, 这里只对 PuppetPuppeteer (wechaty-puppet-puppeteer) 展开介绍。

实现思路

1. 突破网页版限制

顾名思义,PuppetPuppeteer 使用了 Puppeteer,在微信的网页版中创建机器人。但是我们知道,微信网页版已经被限制使用,当我们扫码登陆成功之后,会显示如下文案:

「为了保障你的帐号安全,暂不支持使用网页版微信。你可以前往微信官网 https://weixin.qq.com/ 下载客户端登录。」

目前,这个问题是通过开启 UOS 协议登录来解决的,这里不对 UOS 展开讨论。具体的解决方案是, 通过 Puppeteer 拦截 /cgi-bin/mmwebwx-bin/webwxnewloginpage 请求,并在该请求的 Header 上添加 client-version 和 extspam 两个字段,具体源码如下所示:

const uosHeaders = {
'client-version' : UOS_PATCH_CLIENT_VERSION,
extspam : UOS_PATCH_EXTSPAM,
}

page.on('request', (req) => {
const url = new URL(req.url())
if (url.pathname === '/cgi-bin/mmwebwx-bin/webwxnewloginpage') {
const override = {
headers: {
...req.headers(),
...uosHeaders,
},
}
this.wrapAsync(req.continue(override))
}
})

经过这样的处理,就可以突破网页版的限制,在扫码登陆之后便可进入到聊天页面。如果对 PuppetPuppeteer 比较感兴趣,可以在启动机器人时设置无头模式,这样在 Chrome 中展示出网页版微信的界面。具体代码如下:

const bot = WechatyBuilder.build({
name: 'wechat-bot',
puppetOptions: {
+ head: true, // 关闭无头模式
+ uos: true, // 开启 uos 协议
},
puppet: 'wechaty-puppet-wechat',
})

2. 注入交互脚本

进入到聊天页面之后,PuppetPuppeteer 会在浏览器环境中注入脚本,通过注入的脚本来实现发送消息、创建群组等操作。实现注入脚本的源码如下:

 const WECHATY_BRO_JS_FILE = path.join(
codeRoot,
'src',
'wechaty-bro.js',
)

const sourceCode = fs.readFileSync(WECHATY_BRO_JS_FILE)
.toString()

let retObj = await page.evaluate(sourceCode) as undefined | InjectResult

在端到端 (E2E) 自动化测试中,基本上都是使用脚本来模拟用户的交互行为,以操控 DOM 元素的方式,执行某些操作或流程。但是 PuppetPuppeteer 的实现方式与此完全不同,wechaty-bro.js 的主要功能是实现和微信网页版的交互,但并没有操作 DOM。

3. 与微信网页版的交互

PuppetPuppeteer 对微信网页版有比较深入的理解,并没有通过操作 DOM 来实现发送消息、创建群组等功能。

微信网页版使用了 Angular,在微信的 JavaScript 中,对每种功能都封装成了单独的模块。比如下面的群组模块,具体见代码:

// https://res.wx.qq.com/t/wx_fed/webwx/res/static/js/index_fbe050f.js

angular.module('Services')
.factory('chatroomFactory', [
// ... ...
])

而上面的模块可以通过下面的代码获取到:

angular.element(document).injector().get('chatroomFactory');

// {
// addMember: ...,
// create: ...,
// // ...
// }

也就是说,只需在注入的脚本中,拿到微信网页版的封装的函数方法,便可以实现发送消息、创建群组等一系列功能。wechaty-bro.js 中的代码如下:

  const injector  = angular.element
const accountFactory = injector.get('accountFactory')
const chatroomFactory = injector.get('chatroomFactory')
const chatFactory = injector.get('chatFactory')
const contactFactory = injector.get('contactFactory')

...

WechatyBro.glue = {
accountFactory,
chatFactory,
chatroomFactory,
contactFactory,
...
}

在 Puppeteer 后台要对群组进行操作时,只需要执行以下代码即可:

this.page.evaluate(`
WechatyBro
.glue
.chatroomFactory
.addMember
.apply(
undefined,
{ ... },
)`

)

总结

以上就是 Wechaty 的 PuppetPuppeteer 实现微信机器人的一些实现思路,其中比较关键的两点:一个是 uos 登陆协议,另一个是对微信网页版中代码的使用。需要注意的是,当微信网页版的实现方式有调整时,聊天机器人存在失效的风险。



关注我们的公众号
继续滑动看下一个
KooFE前端团队
向上滑动看下一个