Recently, I borrowed my girlfriend’s official number. I feel that if I only use it to send articles, it is too wasteful to provide these functions of wechat. Think about it, start with the simplest, make a chatbot.

There are several Python solutions for chatbots: AIML, chatterBot, Turing Chatbot and Microsoft Xiaoice.

After considering that may do some customized demand, here I chose a chatterBot (making project address: https://github.com/gunthercox/ChatterBot).

Chatterbot is a Python interface based on a set of rules and machine learning algorithms. With clear structure, good scalability, simple and practical characteristics.

Here’s how chatterBot works:

ChatterBot workflow

  1. An input adapter retrieves data from an input source such as a terminal or API
  2. Input source will be specified by the logical processing module (logic Adapter) respectively, logical processing module will match the closest to the input data of the training focus known sentence A, then according to the sentence to find the most relevant results B, if there are multiple logical processing module is returned to the different results, will be returned to one of the most relevant results.
  3. The output adapter returns the matched result to the terminal or API.

It is worth mentioning that chatterBot is a modular project with input Adapter, Logic Adapter, Storage Adapter, Output Adapter, and Trainer modules.

LogicAdapter is a plug-in design. When the main process starts up, it adds all the logic processing plug-ins defined by the user to the Logic context and then passes them to the MultiLogicAdapter for processing. The MultiLogicAdapter calls each LogicAdapter in turn. When the LogicAdapter is called, the can_process method is executed to determine whether the input can match the logic processing plug-in. A question such as “what’s the weather like today?” obviously requires a hit to the weather plugin, in which case the can_process of the time plugin returns False. After hitting the target, the logic Adapter calculates the Statement object and confidence, and the MultiLogicAdapter takes the response with the highest confidence and proceeds to the next step.

Now let’s see how chatterBot works

ChatterBot installation & Use

The installation

ChatterBot is written in Python and can be installed using PIP:

pip install chatterbotCopy the code

The Chinese version of chatterBot requires Python3 or later. It is recommended to develop in python3. x environment

test

Open iPython and test the input

In [1]: from chatterbot import ChatBot  # import ChatBot

In [2]: momo = ChatBot('Momo', trainer='chatterbot.trainers.ChatterBotCorpusTrainer')/Users/gs /. Virtualenvs/py3 / lib/python3.6 / site - packages/chatterbot/storage/jsonfile py: 26: UnsuitableForProductionWarning: The JsonFileStorageAdapter is not recommendedfor production environments.
  self.UnsuitableForProductionWarning  If you want to deploy the storage Adapter on the server side, you should avoid using this format, because it is too slow

In [3]: momo.train("chatterbot.corpus.chinese")  # specify training set, here we use Chinese

# Below is the result of the conversation
In [4]: momo.get_response('hello'Out[4]: <Statement text: Hello > In [5]: momo.get_response('What's up?'Out[5]: <Statement text: nothing.> In [6]: momo.get_response('Do you know all about it? ') Out[6]: <Statement text: beautiful than ugly.> In [7]: momo.get_response('Are you a programmer? 'Out[7]: <Statement text: I am a programmer > In [8]: momo.get_response('What language do you use? ') Out[8]: <Statement text: I often use Python, Java and C++.>Copy the code

At this point, you can talk to the robot, but now with so little training data, the robot can only return simple conversations.

This is the default Chinese conversation training data Chinese training data address: https://github.com/gunthercox/chatterbot-corpus/tree/master/chatterbot_corpus/data/chinese.

So how do we add training data?

Training robot

ChatterBot has built-in training class, which includes two methods. One is to type a list to train, such as “hello “,” I’m not good “, which is the former’s answer, and the other is to train by importing Corpus format files. Custom training modules are also supported, but ultimately both types are converted.

The chatterBot trains by calling the train() function, but before doing so it needs to be set up with set_trainer(). Such as:

In [12]: from chatterbot.trainers import ListTrainer  # Import the ListTrainer class of the training module

In [13]: momo.get_response('What's your name? ')  # Now is not the question, because we did not train before thisOut[13]: <Statement text: I am baking a cake.> In [14]: momo.set_trainer(ListTrainer)# specify the training mode

In [15]: momo.train(['What's your name? '.'My name is Mojo! '])  # training

In [16]: momo.get_response('What's your name? ')  # Now the robot can answerOut[16]: <Statement text: >Copy the code

The trained data exists by default in./database.db, where jsonDB is used.

With the introduction of the chatterBot here first, specific usage can be reference documentation: chatterBot Tutorial: http://chatterbot.readthedocs.io/en/stable/tutorial.html

Next, I’ll show you how to use chatterBot in your project.

Create projects using Sanic

Sanic is a Python3.5+ based web framework with the same class as Flask, and it writes code very fast.

In addition to things like Flask, Sanic also supports processing requests as asynchronous requests. This means you can write non-blocking fast code using the new async/await syntax.

If you are not familiar with Sanic, please refer to my previous article: Python Web Framework Sanci Quick Start. You can type [Sanic] in the public number to get the address of the article.

Sanic is used here because it looks a lot like Flask, I’ve been using Flask for a long time, and it was written specifically for Python3.5, using coroutines.

First, I will build a project. I have already built the project here. The project structure is as follows:

.├ ── LICENSE ├─ readme.md ├─ manage.py# Run the file to start the project using the python manage.py command├── Momo │ ├─ __init__. Py │ ├─ app.py# Create app module│ ├─ Exercises ─ Helper.py │ ├─ Settings# Application configuration│ ├─ ├─ ├─ ├─ ├.py │ ├─ ├.py# Test module│ └ ─ ─ mweixin. Py# wechat message processing module├ ─ ─ requirements. TXT └ ─ ─ supervisord. ConfCopy the code

Source code I have uploaded to Github, interested can have a look, can also directly pull down the test. Project code address

Let’s focus on the hello.py file and helper.py.

# hello.py
# -*- coding: utf-8 -*-

from sanic import Sanic, Blueprint
from sanic.views import HTTPMethodView
from sanic.response import text

from momo.helper import get_momo_answer  # import get robot answer get function


blueprint = Blueprint('index', url_prefix='/')


class ChatBot(HTTPMethodView):
    # Chatbot HTTP request processing logic
    async def get(self, request):
        ask = request.args.get('ask')
        If there is no value, return 'What did you say?'
        if ask:
            answer = get_momo_answer(ask)
            return text(answer)
        return text('What did you say? ')


blueprint.add_route(ChatBot.as_view(), '/momo')Copy the code
# helper.py
from chatterbot import ChatBot

momo_chat = ChatBot(
    'Momo'.Use mongodb to store data
    storage_adapter='chatterbot.storage.MongoDatabaseAdapter'.Here we specify three adpaters
    logic_adapters=[
        "chatterbot.logic.BestMatch"."chatterbot.logic.MathematicalEvaluation".# Math module
        "chatterbot.logic.TimeLogicAdapter".# Time module
    ],
    input_adapter='chatterbot.input.VariableInputTypeAdapter',
    output_adapter='chatterbot.output.OutputAdapter',
    database='chatterbot',
    read_only=True
)


def get_momo_answer(content):
    # get the robot return result function
    response = momo_chat.get_response(content)
    if isinstance(response, str):
        return response
    return response.textCopy the code

Run the command python manage.py to start the project.

Access the URL in your browser: http://0.0.0.0:8000/momo? Are you a programmer

The results

So far, we have started a Web project, and we can talk to the robot by visiting the URL. It’s time to join the wechat official number!

Access the wechat official account

The premise

  1. Have a wechat public account that can be used (subscription service account is ok, if not, you can use the test account provided by wechat)
  2. Have a server accessible from the outside world (VPS or public cloud can be used by new AWS users for free for one year, you can try it)
  3. The server is configured with python3 (virtualenvWrapper is recommended).

WeChat set

Log in to wechat official account: mp.weixin.qq.com

Open: Development > Basic Configuration

View public number development information:

Public number Basic information

Enable server configuration:

Set the request URL, here is the URL you configured (requires external access, only port 80 or 443)

Enabling server Configuration


Configuring the Server

For details, see the official document: Access Guide

If your server address has been configured, clicking Submit should now be successful. If not, let’s look at how to configure the server address.

Code sample

Let’s take a look at the view code of wechat request:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from six import StringIO

import re
import xmltodict
from chatterbot.trainers import ListTrainer

from sanic import Blueprint
from sanic.views import HTTPMethodView
from sanic.response import text
from sanic.exceptions import ServerError

from weixin import WeixinMpAPI
from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt

from momo.settings import Config

blueprint = Blueprint('weixin', url_prefix='/weixin')


class WXRequestView(HTTPMethodView):

    def _get_args(self, request):
        # Obtain wechat request parameters, add token concatenation into the complete request parameters
        params = request.raw_args
        if not params:
            raise ServerError("invalid params", status_code=400)
        args = {
            'mp_token': Config.WEIXINMP_TOKEN,
            'signature': params.get('signature'),
            'timestamp': params.get('timestamp'),
            'echostr': params.get('echostr'),
            'nonce': params.get('nonce'),}return args

    def get(self, request):
        This step is a get request. The parameters can be obtained using request.raw_args
        args = self._get_args(request)
        weixin = WeixinMpAPI(**args) Python-weixin can instantiate a WeixinMpAPI object directly
        if weixin.validate_signature(): Verify that the parameter is valid
            # If the parameter is won, we will return the echostr parameter sent from wechat to wechat, otherwise return FAIL
            return text(args.get('echostr') or 'fail')
        return text('fail')

blueprint.add_route(WXRequestView.as_view(), '/request')Copy the code

Here I use the wechat SDK python-Weixin I wrote in Python to handle wechat requests, which can be installed using PIP:

pip install python-weixinCopy the code

The latest version of this package has some problems with Python3 encryption and decryption and can be installed directly from Github:

pip install git+https://github.com/zongxiao/python-weixin.git@py3Copy the code

Then update the app.py file:

# -*- coding: utf-8 -*-
from sanic import Sanic
from momo.settings import Config


def create_app(register_bp=True, test=False):
    # to create the app
    app = Sanic(__name__)
    if test:
        app.config['TESTING'] = True
    Import configuration from object
    app.config.from_object(Config)
    register_blueprints(app)
    return app


def register_blueprints(app):
    from momo.views.hello import blueprint as hello_bp
    from momo.views.mweixin import blueprint as wx_bp
    app.register_blueprint(hello_bp)
    # registered wx_bp
    app.register_blueprint(wx_bp)Copy the code

Github: wechat chat robot momo for detailed code

Connecting to a chatbot

Now that our official accounts have access to our own services, it’s time to join the wechat chatbot.

The working flow of wechat chatbot is as follows:

Wechat chat robot workflow

Look at our message logic handling code:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from six import StringIO

import re
import xmltodict
from chatterbot.trainers import ListTrainer

from sanic import Blueprint
from sanic.views import HTTPMethodView
from sanic.response import text
from sanic.exceptions import ServerError

from weixin import WeixinMpAPI
from weixin.reply import TextReply
from weixin.response import WXResponse as _WXResponse
from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt

from momo.settings import Config
from momo.helper import validate_xml, smart_str, get_momo_answer
from momo.media import media_fetch


blueprint = Blueprint('weixin', url_prefix='/weixin')

appid = smart_str(Config.WEIXINMP_APPID)
token = smart_str(Config.WEIXINMP_TOKEN)
encoding_aeskey = smart_str(Config.WEIXINMP_ENCODINGAESKEY)

# copy returned automatically after following
AUTO_REPLY_CONTENT = """ Hi, friend! This is my mother April number, I am the devil, I can accompany you to chat yo! I can also "charge", and if YOU type "charge", you'll be surprised! < a href = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzAwNjI5MjAzNw==&scene=124#wechat_redirect" > history < / a > "" "


class ReplyContent(object):

    _source = 'value'

    def __init__(self, event, keyword, content=None, momo=True):
        self.momo = momo
        self.event = event
        self.content = content
        self.keyword = keyword
        if self.event == 'scan':
            pass

    @property
    def value(self):
        if self.momo:
            answer = get_momo_answer(self.content)
            return answer
        return ' '


class WXResponse(_WXResponse):

    auto_reply_content = AUTO_REPLY_CONTENT

    def _subscribe_event_handler(self):
        # pay attention to the processing logic after the public number
        self.reply_params['content'] = self.auto_reply_content
        self.reply = TextReply(**self.reply_params).render()

    def _unsubscribe_event_handler(self):
        # Processing logic after removing the close, I will probably cry when removing the close
        pass

    def _text_msg_handler(self):
        # Text message processing logic The main logic of a chatbot
        event_key = 'text'
        content = self.data.get('Content')
        reply_content = ReplyContent('text', event_key, content)
        self.reply_params['content'] = reply_content.value
        self.reply = TextReply(**self.reply_params).render()


class WXRequestView(HTTPMethodView):

    def _get_args(self, request):
        params = request.raw_args
        if not params:
            raise ServerError("invalid params", status_code=400)
        args = {
            'mp_token': Config.WEIXINMP_TOKEN,
            'signature': params.get('signature'),
            'timestamp': params.get('timestamp'),
            'echostr': params.get('echostr'),
            'nonce': params.get('nonce'),}return args

    def get(self, request):
        args = self._get_args(request)
        weixin = WeixinMpAPI(**args)
        if weixin.validate_signature():
            return text(args.get('echostr') or 'fail')
        return text('fail')

    def _get_xml(self, data):
        post_str = smart_str(data)
        Verify that the XML format is correct
        validate_xml(StringIO(post_str))
        return post_str

    def _decrypt_xml(self, params, crypt, xml_str):
        # decrypt messages
        nonce = params.get('nonce')
        msg_sign = params.get('msg_signature')
        timestamp = params.get('timestamp')
        ret, decryp_xml = crypt.DecryptMsg(xml_str, msg_sign,
                                           timestamp, nonce)
        return decryp_xml, nonce

    def _encryp_xml(self, crypt, to_xml, nonce):
        # Encrypt message
        to_xml = smart_str(to_xml)
        ret, encrypt_xml = crypt.EncryptMsg(to_xml, nonce)
        return encrypt_xml

    def post(self, request):
        Get the request parameters sent by wechat server
        args = self._get_args(request)
        weixin = WeixinMpAPI(**args)
        if not weixin.validate_signature(): Verify that the parameter is valid
            raise AttributeError("Invalid weixin signature")
        xml_str = self._get_xml(request.body)  Get form data
        crypt = WXBizMsgCrypt(token, encoding_aeskey, appid) 
        decryp_xml, nonce = self._decrypt_xml(request.raw_args, crypt, xml_str) # decryption
        xml_dict = xmltodict.parse(decryp_xml)
        xml = WXResponse(xml_dict)() or 'success' Get the robot return value based on the message using WXResponse
        encryp_xml = self._encryp_xml(crypt, xml, nonce) # Encrypt message
        return text(encryp_xml or xml) # Respond to wechat requests


blueprint.add_route(WXRequestView.as_view(), '/request')Copy the code

It can be seen that it is relatively simple for me to process the return result of wechat request, which is also the interface encapsulated by python-Weixin package. The main processing logic is WXResponse.

Note here that if the server does not respond within 5 seconds the wechat server will retry. To speed up response times, do not set the chatterBot’s storage Adapter to use JSONDB on the server.

These are the main processing logic of wechat chatbot. We run the service as follows:

Sample chat diagram

As you can see here, the chatbot can also do simple math and tell the time, because I added the math module and time module when I specified the processing logic above:

momo_chat = ChatBot(
    'Momo'.Use mongodb to store data
    storage_adapter='chatterbot.storage.MongoDatabaseAdapter'.Here we specify three adpaters
    logic_adapters=[
        "chatterbot.logic.BestMatch"."chatterbot.logic.MathematicalEvaluation".# Math module
        "chatterbot.logic.TimeLogicAdapter".# Time module
    ],
    input_adapter='chatterbot.input.VariableInputTypeAdapter',
    output_adapter='chatterbot.output.OutputAdapter',
    database='chatterbot',
    read_only=True
)Copy the code

Here, WeChat robot structures, is completed, the detailed code has long ball to making: https://github.com/gusibi/momo/tree/chatterbot, interested can refer to it.

Refer to the link

  • ChatterBot project address: https://github.com/gunthercox/ChatterBot
  • ChatterBot Tutorial: http://chatterbot.readthedocs.io/en/stable/tutorial.html
  • Quickly implement a bot in Python: http://www.jianshu.com/p/d1333fde266f
  • Based on the Python – ChatterBot structures, different adapter bot: https://ask.hellobi.com/blog/guodongwei1991/7626
  • Has automatic learning Python robot – ChatterBot: https://kantai235.github.io/2017/03/16/ChatterBotTeaching/
  • Using bot ChatterBot building: https://www.biaodianfu.com/chatterbot.html
  • python-weixin sdk: https://github.com/gusibi/python-weixin

trailer

Here, the chatbot is relatively simple and can only reply to simple conversations. The next post will end with how to train the bot on the bot and a more practical feature, how to turn the bot into a blog writing assistant.


Finally, thank your girlfriend for her support.

Welcome to follow (April_Louisa) Buy me a Fanta
Welcome to attention
Buy me a Fanta