微信机器人之开发体验
前言
目前在公司中的消息通知大部分使用邮件、短信、钉钉、App通知、websocket通知、微信企业版等等,针对于QQ和微信这种目前使用量较大的工具,通知机制并不完善。当然,主要是TX本身的功能要求决定了无法做类似的通知。
本篇文章主要讨论微信机器人的开发经验。
可选方案
桌面工具
这类的工具主要是嫁接到微信的桌面工具来使用,基于微信的windows客户端的功能做了节选。使用比较简单,但相对并不是很安全,工具也不够完整。
浏览器工具
浏览器工具是基于微信网页版进行开发,作为开发者来说,这类工具最受开发者欢迎,不需要服务器,只需要了解网页版API调用过程,就可以实现想要的功能。但是现在微信对网页版登录是有很多限定的,大部分微信已经不允许登录网页版了。而且网页版工具也有很大的限制,功能也不够完整,无法代替微信。
win sock开发
这个开发说得直白一些,是“桌面工具”的前提,桌面工具无非就是用这个方法分析并开发出来的。这个开发会很困难,相当于是解析微信windows客户端的exe文件,也有很大的风险,毕竟不是官方认可的内容。
iPad协议开发
ipad协议是微信在ipad上提供的一种接口协议,这个协议目前可使用的内容上来说,是功能最全的,只要分析出ipad协议的接口,就可以使用相应的功能,在ipad上的功能也是很全的。难点是,目前对ipad协议并没有被微信公开,只是很多公司有私下的研究公布,自己分析代价很大。
wechaty
基本信息
wechaty是句子科技使用nodeJS针对于微信开发出来的协议,其中包含网页版和ipad协议。wechaty github地址是: https://github.com/wechaty/wechaty
wechaty默认使用时是基于网页版协议的,如果需要使用ipad协议,需要在github上做申请,会有专人审核接入。
开发之前
我使用时是基于wechaty-puppet-padplus协议做的开发。
具体的可以参考:https://github.com/wechaty/wechaty-getting-started
环境准备
linux
node
typescript
微信
基于node:10.15.0-alpine系统需要对操作系统有一些初始化操作:
apk add build-base
apk add zlib-dev
apk add python
npm config set registry https://registry.npm.taobao.org
npm config set disturl https://npm.taobao.org/dist
开发过程
在开发过程中使用ts-node命令进行执行(对于typescript小白来说也查了很多资料)。
先把github上例子的ts文件拿下来执行,创建执行脚本:
export PATH=$PATH:/wechaty/padplus/node_modules/.bin/
ts-node $1
会自动提示登录二维码,例子中发送的消息会自动返回。
工具使用
我对wechaty的基本需求有两点:
1.定时的自动向指定人员发送变化的消息,面对不同人员不同时间发送不同的消息;
2.为其他程序端提供相应的接口。
因此,我做了一个mybot.ts:
import { Wechaty,Message } from 'wechaty'
import { PuppetPadplus } from 'wechaty-puppet-padplus'
import { generate } from 'qrcode-terminal'
import * as express from 'express';
import * as urlencode from 'urlencode';
import * as bodyParser from 'body-parser';
const urlencodedParser = bodyParser.urlencoded({ extended: false })
const token = 'xxxxxxxx' //此处需要替换你自己申请的token
const puppet = new PuppetPadplus({
token,
})
const name = 'anan'
const app = express(); // 用于声明服务器端所能提供的http服务
const bot = new Wechaty({
puppet,
name, // generate xxxx.memory-card.json and save login data for the next login
})
bot
.on('scan', onScan)
.on('message', onMessage )
.start()
function onScan (qrcode, status) {
generate(qrcode, { small: true }) // show qrcode on console
}
async function onMessage(msg){
console.log("=============================")
if (msg.self() || msg.from().name() === '微信团队') {
console.log("myself message")
return
}
console.log(`msg : ${msg}`)
console.log(`from: ${msg.from().name()}: ${msg.from().id}`)
console.log(`to: ${msg.to()}`)
console.log(`text: ${msg.text()}`)
console.log(`isRoom: ${msg.room()}`)
console.log("=============================")
if (msg.type() == Message.Type.Text) {
if (msg.room()){
const topic = await msg.room().topic()
console.log(`roomTopic: ${topic}`)
const room = await msg.room()
const memberList = await room.memberList()
for (let i = 0; i < memberList.length; i++) {
console.log(`member${i}`)
const member = memberList[i]
console.log(`member${i}: ${member.id},${member.name()},${member.alias()},${member.friend()},${member.avatar()}`)
}
if (await msg.mentionSelf()){
room.say("recieved message mentioned me!",memberList[0],memberList[1])
}else{
room.say("recieved message \n not metioned me!")
}
}else {
await msg.say('recieved message sent myself only')
}
return
}else {
console.log("message is not text")
console.log(`msg : ${msg}`)
await msg.say('message is not text')
}
}
async function sendMessage(contact:string, contacttype:string, message:string, msgtype:string, metion:string){
console.log(`params:${contact},${contacttype},${message},${msgtype},${metion}`)
if (contacttype == 'room'){
const room = await bot.Room.find({topic: contact})
console.log(`getroom: ${room}`)
const metionContact = await room.member(metion)
console.log(`get metion: ${metionContact}`)
if (metionContact){
await room.say(message, metionContact)
}else {
await room.say(message)
}
} else {
const contactCard = await bot.Contact.find({name: contact})
if (!contactCard){
console.log(`get contact card is null`)
return
}
await contactCard.say(message)
}
}
// 声明一个处理get请求的服务
app.get('/', (req, resp) => {
resp.send("Hello Express");
});
app.get("/send",urlencodedParser, (req, resp) => {
console.log(`url: ${req.url}`)
console.log(decodeURI(req.url))
//decodeURI(decodeURIComponent(escape(req.url)), "UTF-8")
//const req = decodeURI(request)
const contactvalue = decodeURI(req.query.contact)
const contacttypevalue = decodeURI(req.query.contacttype)
const messagevalue = decodeURI(req.query.message).replace(/\\n/g,"\n")
const msgtypevalue = decodeURI(req.query.msgtype)
const metionvalue = decodeURI(req.query.metion)
console.log(`query:${contactvalue},${contacttypevalue},${messagevalue},${msgtypevalue},${metionvalue}`)
sendMessage(contactvalue, contacttypevalue, messagevalue, msgtypevalue, metionvalue)
resp.send("接收到发送微信请求");
});
const server = app.listen(8000, "localhost", () => {
console.log("服务器已启动, 地址是:http://localhost:8000");
});
这个机器人主要做的是开通了一个send的http get方法,直接通过浏览器访问此接口就可以发送消息,另外还可以扩展方法来获取微信上的信息。
于是针对需求1的定时发送,我做了如下的脚本:
curl -G --data-urlencode "contact=中文测试" --data-urlencode "message=${message}" http://127.0.0.1:8000/send?contacttype=room\&msgtype=text
我们可以通过定时的执行这个脚本,就可以发送给相应的人或者群消息了,这个脚本里有很多都可以做成参数。
contact:人或者群名称
message:消息的详细内容
contacttype:room/person标识群或者个人
msgtype:定义消息的类型
注意此处对应中文的地方,一定要用urlencode,否则系统无法检索和识别
总结
目前市面上可以使用的,免费的,功能较全的,还是wechaty比较好,所以推荐给大家使用。