32

微信机器人之开发体验

 4 years ago
source link: https://www.wencst.com/archives/1990
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

目前在公司中的消息通知大部分使用邮件、短信、钉钉、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比较好,所以推荐给大家使用。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK