project,

Wechaty 将 TS 转发到 Python 的探索实践

jcq jcq Follow Sep 12, 2020 · 9 mins read
Wechaty 将 TS 转发到 Python 的探索实践

报时机器人,每逢整点在群里发送报时信息。

Wechaty 将 TS 转发到 Python 的探索实践

许多朋友可能对 Python 更为熟悉,而对 Typescirpt 则比较生疏。同时 Python 在文件处理、图像处理、机器学习等领域更为简易灵活。如果能将微信机器人接入 Python 将为开发者带来极大的便利。目前官方正在开发 Python 版本的机器人,在开发完善之前我们可以使用一些替代方案。本项目以一个简单的报时机器人为例,展示了如何将 Typescript 程序收到的消息转发给 Python 后端处理,可以作为 Python 版 Wechaty 正式上线之前的简易替代。

项目地址:https://github.com/jcq15/wechaty

联系作者:jcq15@mails.tsinghua.edu.cn

使用方法

报时机器人,每逢整点在群里发送报时信息。具体报时内容可以由群友设置。

基本功能:

  • 每逢整点自动报时
  • 发送“报时”立刻报时
  • 发送“修改模板 新模板”可以修改报时内容,其中\h 表示当前小时,\m 表示当前分钟,\s 表示当前秒。类似于其他语言的转义字符,\\ 表示字符 \。例如现在是 11:45:14\\\h:\mm 会被解析为 \11:45m

为了重点展示框架,本项目没有添加过多复杂的功能,后续可以在此基础上实现群友报时情况统计、排行榜等涉及文件操作的功能。

环境

CentOS 7

开始

照着官方文档初始化一些东西就可以。

首先检查 Node 版本

node --version

如果是 v10.16.0 以下,需要先更新 Node

创建文件夹,我的文件夹名字叫 wechatbot

mkdir wechatbot
cd wechatbot
npm init -y
npm install ts-node typescript -g
tsc --init --target ES6
touch bot.ts

上面我们新建了文件 bot.ts,这个文件就是主程序了,我们把官方示例代码放到这个文件里,不要忘了把 tokenname 改成你自己的:

// bot.ts
import { Contact, Message, Wechaty } from 'wechaty'
import { ScanStatus } from 'wechaty-puppet'
import { PuppetPadplus } from 'wechaty-puppet-padplus'
import QrcodeTerminal from 'qrcode-terminal'
import { FileBox }  from 'wechaty'

const token = your_token

const puppet = new PuppetPadplus({
  token,
})

const name  = your_name

const bot = new Wechaty({
  name,
  puppet, // generate xxxx.memory-card.json and save login data for the next login
})

var baoshi: RegExp = new RegExp('报时.*')   // 正则表达式,群名以“报时”开头

//报时器,整点触发
async function hourReport() {
    //当前时间
    var time = new Date();
    //小时
    var hours = time.getHours();
    //分钟
    var mins = time.getMinutes();
    //秒钟
    var secs = time.getSeconds();
    //下一次报时间隔
    var next = ((60 - mins) * 60 - secs) * 1000;
    //设置下次启动时间
    setTimeout(hourReport, next);
    //整点报时,因为第一次进来mins可能不为0所以要判断
    const room = await bot.Room.find({topic:baoshi})

    var request = require('request')
    request.get({url:'http://127.0.0.1:5000/clock'}, function (error, response, body) {  
        if (error) {
            console.log('Error :', error)
            return
        }
        console.log(' Body :', body)
        if(body.length > 0){
          room?.say(body)
        }
    })
}

bot.on('scan', (qrcode, status) => {
    if (status === ScanStatus.Waiting) {
      QrcodeTerminal.generate(qrcode, {
        small: true
      })
    }
  })
bot.on('login', async (user: Contact) => {
    console.log(`login success, user: ${user}`)
    //启动报时器
    hourReport();
  })
bot.on('message', async (msg: Message) => {
    console.log(`msg : ${msg}`)
    var room = msg.room()
    var topic = ''
    if(room){
      topic = await room.topic()
    }
    var contact = msg.from()

    //直接推给python处理,我们获得回复内容
    var request = require('request')
    var formData = {
      text: msg.text(),
      roomtopic: topic,
      date: JSON.stringify(msg.date()),
      contactid: contact?.id,
    }
    try{
      // 所有的东西都推到后端用python处理
      request.post({url:'http://127.0.0.1:5000/message', formData: formData}, function (error, response, body) {  
          if (error) {
              console.log('Error :', error)
              return
          }
          console.log(' Body :', body)
          var response = JSON.parse(body)
          if(body.length > 0){
            const type: string = response['type']
            if(type=='image'){
              const path: string = response['content']
              const filebox: FileBox = FileBox.fromFile(path)
              if(room){
                console.log('准备发啦!')
                room.say(filebox)
              }else{
                contact?.say(filebox)
              }
            }else if(type=='text'){
              const text: string = response['content']
              if(room){
                room.say(text)
              }else{
                contact?.say(text)
              }
            }else{
              //什么也不做
            }
          }
      })
    }catch(e){
      console.log(e)
    }
  })

安装 wechatyqrcode-terminal

npm install wechaty@latest
npm install wechaty-puppet-padplus@latest
npm install qrcode-terminal

这一步我遇到了点问题,装着装着就卡住不动了,因为某些不可描述的原因国外的网站连接质量不好,我们需要使用代理:

npm config set registry https://registry.npm.taobao.org

然后安装就好了。

后端代码

# backend.py
from flask import Flask
from flask import request
import json
import datetime

app = Flask(__name__)

# 全局变量
name = '报时'
model = r'淦!已经\h点\m分了!你今天学习了吗?'

# 获取报时内容
def gettext():
    response_text = ''
    status = False
    for c in model:
        if not status:
            if c == chr(92):
                status = True
            else:
                response_text += c
        else:
            status = False
            if c == chr(92):
                response_text += c
            elif c == 'h':
                response_text += str(datetime.datetime.now().hour)
            elif c == 'm':
                response_text += str(datetime.datetime.now().minute)
            elif c == 's':
                response_text += str(datetime.datetime.now().second)
            else:
                pass
    return response_text

def handle(data):
    global model
    text = data['text']
    if len(text) >= 6 and text[:4] == '修改模板':
        model = text[5:]
        return json.dumps({'type': 'text', 'content': '修改大成功!现在的模板是:\n'+model})
    elif text == '报时':
        return json.dumps({'type': 'text', 'content': gettext()})
    else:
        return json.dumps({'type': 'null'})

@app.route('/message', methods=['GET', 'POST'])
def message():
    if request.method == 'POST':
        data = request.form
        print(data)
        roomtopic = data['roomtopic']
        if roomtopic:     # 是群
            if len(roomtopic) >= 2 and roomtopic[0:2] == name:
                return handle(data)
    return json.dumps({'type':'null'})

# 返回当前报时内容
@app.route('/clock', methods=['GET'])
def clock():
    return gettext()

if __name__ == '__main__':
    app.run()

启动服务(可以使用 screen 同时运行两个程序):

ts-node bot.ts
python3 backend.py

大功告成!

Join Newsletter
Get the latest news right in your inbox. We never spam!
Written by jcq
要珍惜德行,却不要成为荣誉的奴隶,因为前者是永恒的,后者却很快会消失。