微信机器人之开发体验

作者: wencst 分类:javascript,linux,程序设计 发布时间: 2020-01-28 13:34 阅读: 1,096 次

前言

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

如果文章对您有用,扫一下支付宝的红包,不胜感激!

欢迎加入QQ群进行技术交流:656897351(各种技术、招聘、兼职、培训欢迎加入)



Leave a Reply