Python + Tornado 搭建自動回覆微信公眾號

Rocky_Ansi發表於2017-04-24

1 通過 pip 安裝 wechat-python-sdk , Requests 以及 Tornado

pip install tornado
pip install wechat-sdk
pip install requests

2 訂閱號申請
要搭建訂閱號,首先需要在微信公眾平臺官網進行註冊,註冊網址: 微信公眾平臺
目前個人使用者可以免費申請微信訂閱號,雖然很多許可權申請不到,但是基本的訊息回覆是沒有問題的。

  • 伺服器接入

具體的接入步驟可以參考官網上的接入指南

本訂閱號的配置為:

Python + Tornado 搭建自動回覆微信公眾號

進行修改配置,提交時,需要驗證伺服器地址的有效性

Python + Tornado 搭建自動回覆微信公眾號

wechat.py

import tornado.escape
import tornado.web

from wechat_sdk import WechatConf
conf = WechatConf(
    token='your_token', # 你的公眾號Token
    appid='your_appid', # 你的公眾號的AppID
    appsecret='your_appsecret', # 你的公眾號的AppSecret
    encrypt_mode='safe',  # 可選項:normal/compatible/safe,分別對應於 明文/相容/安全 模式
    encoding_aes_key='your_encoding_aes_key'  # 如果傳入此值則必須保證同時傳入 token, appid
)

from wechat_sdk import WechatBasic
wechat = WechatBasic(conf=conf)

class WX(tornado.web.RequestHandler):
    def get(self):
        signature = self.get_argument('signature', 'default')
        timestamp = self.get_argument('timestamp', 'default')
        nonce = self.get_argument('nonce', 'default')
        echostr = self.get_argument('echostr', 'default')
        if signature != 'default' and timestamp != 'default' and nonce != 'default' and echostr != 'default' \
                and wechat.check_signature(signature, timestamp, nonce):
            self.write(echostr)
        else:
            self.write('Not Open')

wechat_main.py

#!/usr/bin/env python
#coding:utf-8

import tornado.web
import tornado.httpserver
from tornado.options import define, options
import os
import wechat

settings = {
            'static_path': os.path.join(os.path.dirname(__file__), 'static'),
            'template_path': os.path.join(os.path.dirname(__file__), 'view'),
            'cookie_secret': 'xxxxxxxxxxx',
            'login_url': '/',
            'session_secret': "xxxxxxxxxxxxxxxxxxxxxxx",
            'session_timeout': 3600,

            'port': 8888,
            'wx_token': 'your_token',
            }

web_handlers = [
        (r'/wechat', wechat.WX),
        ]

#define("port", default=settings['port'], help="run on the given port", type=int)

from tornado.options import define, options
define ("port", default=8888, help="run on the given port", type=int)

if __name__ == '__main__':
    app = tornado.web.Application(web_handlers, **settings)
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

cookie_secret session_secret 可以隨便填寫;
配置好程式原始碼後執行,確認執行無誤後再在公眾號設定頁面點選 提交 ,如果程式執行沒問題,會顯示接入成功。

3 接入圖靈機器人
要接入圖靈機器人,首先需要在官網申請API Key。

class TulingAutoReply:
    def __init__(self, tuling_key, tuling_url):
        self.key = tuling_key
        self.url = tuling_url

    def reply(self, unicode_str):
        body = {'key': self.key, 'info': unicode_str.encode('utf-8')}
        r = requests.post(self.url, data=body)
        r.encoding = 'utf-8'
        resp = r.text
        if resp is None or len(resp) == 0:
            return None
        try:
            js = json.loads(resp)
            if js['code'] == 100000:
                return js['text'].replace('<br>', '\n')
            elif js['code'] == 200000:
                return js['url']
            else:
                return None
        except Exception:
            traceback.print_exc()
            return None

4 編寫公眾號自動回覆程式碼

auto_reply = TulingAutoReply(key, url) # key和url填入自己申請到的圖靈key以及圖靈請求url

class WX(tornado.web.RequestHandler):
    def wx_proc_msg(self, body):
        try:
            wechat.parse_data(body)
        except ParseError:
            print('Invalid Body Text')
            return
        if isinstance(wechat.message, TextMessage): # 訊息為文字訊息
            content = wechat.message.content
            reply = auto_reply.reply(content)
            if reply is not None:
                return wechat.response_text(content=reply)
            else:
                return wechat.response_text(content=u"不知道你說的什麼")
        return wechat.response_text(content=u'知道了')

    def post(self):
        signature = self.get_argument('signature', 'default')
        timestamp = self.get_argument('timestamp', 'default')
        nonce = self.get_argument('nonce', 'default')
        if signature != 'default' and timestamp != 'default' and nonce != 'default' \
                and wechat.check_signature(signature, timestamp, nonce):
            body = self.request.body.decode('utf-8')
            try:
                result = self.wx_proc_msg(body)
                if result is not None:
                    self.write(result)
            except IOError as e:
                return

最終 wechat.py 程式碼如下:

import tornado.escape
import tornado.web
#from goose import Goose, ParseError
import json
import requests
import traceback


from wechat_sdk import WechatConf
conf = WechatConf(
    token='your_token', # 你的公眾號Token
    appid='your_appid', # 你的公眾號的AppID
    appsecret='your_appsecret', # 你的公眾號的AppSecret
    encrypt_mode='safe',  # 可選項:normal/compatible/safe,分別對應於 明文/相容/安全 模式
    encoding_aes_key='your_encoding_aes_key'  # 如果傳入此值則必須保證同時傳入 token, appid
)

from wechat_sdk import WechatBasic
wechat = WechatBasic(conf=conf)


class TulingAutoReply:
    def __init__(self, tuling_key, tuling_url):
        self.key = tuling_key
        self.url = tuling_url

    def reply(self, unicode_str):
        body = {'key': self.key, 'info': unicode_str.encode('utf-8')}
        r = requests.post(self.url, data=body)
        r.encoding = 'utf-8'
        resp = r.text
        if resp is None or len(resp) == 0:
            return None
        try:
            js = json.loads(resp)
            if js['code'] == 100000:
                return js['text'].replace('<br>', '\n')
            elif js['code'] == 200000:
                return js['url']
            else:
                return None
        except Exception:
            traceback.print_exc()
            return None



auto_reply = TulingAutoReply(key, url) # key和url填入自己申請到的圖靈key以及圖靈請求url

class WX(tornado.web.RequestHandler):
    def wx_proc_msg(self, body):
        try:
            wechat.parse_data(body)
        except ParseError:
            print('Invalid Body Text')
            return
        if isinstance(wechat.message, TextMessage): # 訊息為文字訊息
            content = wechat.message.content
            reply = auto_reply.reply(content)
            if reply is not None:
                return wechat.response_text(content=reply)
            else:
                return wechat.response_text(content=u"不知道你說的什麼")
        return wechat.response_text(content=u'知道了')

    def post(self):
        signature = self.get_argument('signature', 'default')
        timestamp = self.get_argument('timestamp', 'default')
        nonce = self.get_argument('nonce', 'default')
        if signature != 'default' and timestamp != 'default' and nonce != 'default' \
                and wechat.check_signature(signature, timestamp, nonce):
            body = self.request.body.decode('utf-8')
            try:
                result = self.wx_proc_msg(body)
                if result is not None:
                    self.write(result)
            except IOError as e:
                return

相關文章