Python搭建聊天機器人微信訂閱號

Python利用AIML和Tornado搭建聊天機器人微信訂閱號

AIML ,全名爲Artificial Intelligence Markup Language(人工智能標記語言),是一種創建自然語言軟件代理的XML語言,是由Richard Wallace和世界各地的自由軟件社區在1995年至2002年發明的。

它的雛形是一個名爲”A.L.I.C.E.” (“Artificial Linguistic Internet Computer Entity”)的高度擴展的Eliza機器人。ALICE總共贏得3次每年度的Loebner獎,並且在2004年獲得了Chatterbox Challenge的冠軍。由於A.L.I.C.E. 的AIML設置是在GNU GPL協議下發布的,所以已經有許多基於該程序和AIML庫的“克隆ALICE”出現。目前AIML已經有了Java,Ruby,Python, C ,C#,Pascal等語言的版本。

TornadoFriendFeed 使用的可擴展的非阻塞式 web 服務器及其相關工具的開源版本。這個 Web 框架看起來有些像 web.py 或者 Google 的 webapp,不過爲了能有效利用非阻塞式服務器環境,這個 Web 框架還包含了一些相關的有用工具 和優化。

Tornado 和現在的主流 Web 服務器框架(包括大多數 Python 的框架)有着明顯的區別:它是非阻塞式服務器,而且速度相當快。得利於其 非阻塞的方式和對 epoll 的運用,Tornado 每秒可以處理數以千計的連接,這意味着對於實時 Web 服務來說,Tornado 是一個理想的 Web 框架。我們開發這個 Web 服務器的主要目的就是爲了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用裏每一個活動用戶都會保持着一個服務器連接。

微信公衆平臺 是運營者通過公衆號爲微信用戶提供資訊和服務的平臺,而公衆平臺開發接口則是提供服務的基礎,開發者在公衆平臺網站中創建公衆號、獲取接口權限後,可以通過閱讀本接口文檔來幫助開發。

Python可以很方便地利用Tornado框架以及AIML搭建一個聊天機器人微信公衆號,本文簡單介紹下如何用Python編寫簡單的聊天機器人。不過由於目前AIML上缺少高質量的中文語料庫而不支持中文聊天。因此本文搭建的爲英文聊天機器人。

此搭建方式需要一臺具有固定公網ip地址的主機一臺。也可以利用現有的雲主機或者雲平臺,例如 sina app engine

此微信訂閱號的完整源代碼可以通過此鏈接下載:聊天機器人訂閱號源代碼

1 實現效果

搭建的微信訂閱號可以直接回複用戶發送的英文消息與用戶聊天。

我自己搭建了一個訂閱號CuriousGuys,可以添加此訂閱號後直接發送英文消息。
要添加CuriousGuys可以直接掃碼:
CuriousGuys

效果截圖:
Chat with CuriousGuys

2 安裝相關Python庫

需要用到Tornado以及aiml庫。

Linux下安裝Tornado可以直接用以下腳本:

pip install tornado

其它操作系統下安裝Tornado可以參考Tornado官方網站

Linux下安裝aiml也可以直接pip安裝:

pip install aiml

其它操作系統下的安裝請參考AIML官方網站

3 獲取alice資源

Python aiml安裝完成後在Python安裝目錄下的 Lib/site-packages/aiml下會有alice子目錄,將此目錄複製到工作區。
或者在Google code上下載alice brain: aiml-en-us-foundation-alice.v1-9.zip

4 訂閱號申請

要搭建訂閱號,需要在微信官網進行註冊,註冊網址:微信公衆平臺

目前個人用戶可以免費申請微信訂閱號,雖然很多權限申請不到,但是基本的消息回覆是沒有問題的。

5 服務器接入

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

本訂閱號的配置爲:
CuriousGuys服務器配置

配置裏的URL爲服務器提供訂閱號後臺的url路徑,本文用到的源代碼配置的是 http://server_ip/wx 其中 server_ip 是運行源代碼的主機的公網ip地址。這個可以通過修改源代碼裏的config.py來配置。

Token 可以設置爲任意字符串,不過要將源代碼config.py裏的settings[‘wx_token’]改爲設置的字符串。

EncodingAESKey 可以選擇隨機生成。

消息加密方式可以設置爲比較簡單的明文模式。

接受並處理微信服務器發送的接入請求的關鍵代碼爲Tornado的一個Handle(在源代碼裏的handle/wx.py中):

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 config.settings['wx_test'] or (signature != 'default' and timestamp != 'default' and nonce != 'default' and echostr != 'default' and check_wx_request(signature, timestamp, nonce)):
            self.write(echostr)
        else:
            self.write('Not Open')

此代碼的作用就是驗證消息是來自微信官方服務器後直接返回echostr。

配置好程序源代碼後運行,確認運行無誤後再點擊 提交 ,如果程序運行沒問題,會顯示接入成功。

6 程序編寫

關鍵部分爲利用aiml回覆用戶發送的消息。

aiml載入語料庫的代碼爲(在config.py中):

import aiml
cur_dir = os.getcwd()
print 'cur_dir:', cur_dir
os.chdir('./res/alice')
alice = aiml.Kernel()
alice.learn("startup.xml")
alice.respond('LOAD ALICE')
os.chdir(cur_dir)
print 'cur_dir:', os.getcwd()

服務器回覆用戶消息的關鍵代碼爲(在handle/wx.py中):

# -*- coding: utf-8 -*-
import tornado.escape
import tornado.web

from BaseHTTPServer import HTTPServer
import base64
import sys
import json
import time

import config
import time
import hashlib
import urllib
import os
import random

from util.xml2json import *

# 處理xml格式的用戶消息
def wx_proc_msg(msg_body):
    try:
        jmsg = xml2json(msg_body)
        msgjson = json.loads(jmsg)
        msg = msgjson['xml']
        MsgType = msg['MsgType']

        main_content = {}
        main_content['MsgType'] = msg['MsgType']
        main_content['CreateTime'] = msg['CreateTime']
        main_content['ToUserName'] = msg['FromUserName']
        main_content['FromUserName'] = msg['ToUserName']

        if MsgType == 'text':
            req = msg['Content']
            respond = config.alice.respond(req)
            if respond == None or len(respond) < 1:
                respond = '''Sorry I can't understand you'''
            main_content['Content'] = respond
            result = {}
            result['xml'] = main_content.copy()

            result = json2xml(result)
            return result

        elif MsgType == 'image':
            main_content['MsgType'] = 'text'
            main_content['Content'] = '''Sorry I can't read picture.'''
            result = {}
            result['xml'] = main_content

            return json2xml(result)

        elif MsgType == 'voice':
            pass
        elif MsgType == 'video':
            pass
        elif MsgType == 'shortvideo':
            pass
        elif MsgType == 'location':
            pass
        elif MsgType == 'link':
            pass
        else:
            pass

    except Exception, e:
        print 'Error when process this message:', msg_body
        print e
    return ''

# 驗證消息是否來自微信官方服務器
def check_wx_request(signature, timestamp, nonce):
    token = config.settings['wx_token']
    arr = [token, timestamp, nonce]
    arr.sort()
    sh = hashlib.sha1(arr[0] + arr[1] + arr[2]).hexdigest()
    if sh == signature:
        return True
    else:
        return False

# Tornado的Handle,用於接收並處理用戶消息
class WX(tornado.web.RequestHandler):
    def post(self):
        signature = self.get_argument('signature', 'default')
        timestamp = self.get_argument('timestamp', 'default')
        nonce = self.get_argument('nonce', 'default')
        if config.settings['wx_test'] or (signature != 'default' and timestamp != 'default' and nonce != 'default' and check_wx_request(signature, timestamp, nonce)):
            body = self.request.body
            try:
                self.write(wx_proc_msg(body))
            except IOError, e:
                return
發佈了143 篇原創文章 · 獲贊 176 · 訪問量 385萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章