2020-09-18

2020-09-18  本文已影响0人  你大爷临终前

---

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

author: jcq15

---

# 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

## 开始

照着[官方文档](https://github.com/wechaty/wechaty-puppet-padplus)初始化一些东西就可以。

首先检查 `Node` 版本

```shell

node --version

```

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

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

```shell

mkdir wechatbot

cd wechatbot

npm init -y

npm install ts-node typescript -g

tsc --init --target ES6

touch bot.ts

```

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

```typescript

// 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)

    }

  })

```

安装 `wechaty` 和 `qrcode-terminal`

```shell

npm install wechaty@latest

npm install wechaty-puppet-padplus@latest

npm install qrcode-terminal

```

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

```shell

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

```

然后安装就好了。

## 后端代码

```python

# 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 同时运行两个程序):

```shell

ts-node bot.ts

python3 backend.py

```

大功告成!

上一篇下一篇

猜你喜欢

热点阅读