django+python微信开发之四-最后的工作:消息服务类实现自动回复机器人

###消息服务类wechatService.py
之前的工作都差不多了,包括配置服务器,三种消息类型封装和对消息的处理(解析xml和转换成xml),最后是要根据不同的消息类型来作出反应了。

# -*- coding:utf-8 -*-
"""
# Author: Pegasus Wang (pegasuswang@qq.com, http://ningning.today)
# Created Time : Fri Feb 20 21:38:57 2015

# File Name: wechatService.py
# Description:

# :copyright: (c) 2015 by Pegasus Wang.
# :license: MIT, see LICENSE for more details.
"""

import time
from wechatUtil import MessageUtil
from wechatReply import TextReply


class WechatService(object):
    """process request"""
    @staticmethod
    def processRequest(request):
        """process different message types.

        :param request: post request message
        :return: None
        """
        requestMap = MessageUtil.parseXml(request)
        fromUserName = requestMap.get(u'FromUserName')
        toUserName = requestMap.get(u'ToUserName')
        createTime = requestMap.get(u'CreateTime')
        msgType = requestMap.get(u'MsgType')
        msgId = requestMap.get(u'MsgId')

        textReply = TextReply()
        textReply.setToUserName(fromUserName)
        textReply.setFromUserName(toUserName)
        textReply.setCreateTime(time.time())
        textReply.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT)

        if msgType == MessageUtil.REQ_MESSAGE_TYPE_TEXT:
            content = requestMap.get('Content').decode('utf-8')
            respContent = u'您发送的是文本消息: ' + content
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_IMAGE:
            respContent = u'您发送的是图片消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_VOICE:
            respContent = u'您发送的是语音消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_VIDEO:
            respContent = u'您发送的是视频消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_LOCATION:
            respContent = u'您发送的是地理位置消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_LINK:
            respContent = u'您发送的是链接消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_EVENT:
            eventType = requestMap.get(u'Event')
            if eventType == MessageUtil.EVENT_TYPE_SUBSCRIBE:
                respContent = u'^_^谢谢您的关注!'
            elif eventType == MessageUtil.EVENT_TYPE_UNSUBSCRIBE:
                pass
            elif eventType == MessageUtil.EVENT_TYPE_SCAN:
                # TODO
                pass
            elif eventType == MessageUtil.EVENT_TYPE_LOCATION:
                # TODO
                pass
            elif eventType == MessageUtil.EVENT_TYPE_CLICK:
                # TODO
                pass

        textReply.setContent(respContent)
        respXml = MessageUtil.class2xml(textReply)

        return respXml

代码也非常简单,就是根据不同的消息类型进行处理。
最近又发现了一个好玩的自动回复图灵机器人,我们可以调用它的接口实现我们的文本消息自动回复,比如回复个“笑话”它就会给你发送一个笑话,要注意的是它经常发一些少儿不宜的笑话。更改后的代码如下(改动的地方代码中会说明):

# -*- coding:utf-8 -*-
"""
# Author: Pegasus Wang (pegasuswang@qq.com, http://ningning.today)
# Created Time : Fri Feb 20 21:38:57 2015

# File Name: wechatService.py
# Description:

# :copyright: (c) 2015 by Pegasus Wang.
# :license: MIT, see LICENSE for more details.
"""
import json
import time
import urllib
import urllib2

from wechatUtil import MessageUtil
from wechatReply import TextReply

# add a robot auto reply class
class RobotService(object):
    """Auto reply robot service"""
    KEY = 'd92d20bc1d8bb3cff585bf746603b2a9'    # change to your key
    url = 'http://www.tuling123.com/openapi/api'

    @staticmethod
    def auto_reply(req_info):
        query = {'key': RobotService.KEY, 'info': req_info.encode('utf-8')}
        headers = {'Content-type': 'text/html', 'charset': 'utf-8'}
        data = urllib.urlencode(query)
        req = urllib2.Request(RobotService.url, data)
        f = urllib2.urlopen(req).read()
        return json.loads(f).get('text').replace('<br>', '\n')


class WechatService(object):
    """process request"""
    @staticmethod
    def processRequest(request):
        """process different message types.

        :param request: post request message
        :return: None
        """
        requestMap = MessageUtil.parseXml(request)
        fromUserName = requestMap.get(u'FromUserName')
        toUserName = requestMap.get(u'ToUserName')
        createTime = requestMap.get(u'CreateTime')
        msgType = requestMap.get(u'MsgType')
        msgId = requestMap.get(u'MsgId')

        textReply = TextReply()
        textReply.setToUserName(fromUserName)
        textReply.setFromUserName(toUserName)
        textReply.setCreateTime(time.time())
        textReply.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT)

        if msgType == MessageUtil.REQ_MESSAGE_TYPE_TEXT:
            content = requestMap.get('Content').decode('utf-8')    # note: decode first
            # respContent = u'您发送的是文本消息:' + content
            # change to auto_reply(content)
            respContent = RobotService.auto_reply(content)
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_IMAGE:
            respContent = u'您发送的是图片消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_VOICE:
            respContent = u'您发送的是语音消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_VIDEO:
            respContent = u'您发送的是视频消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_LOCATION:
            respContent = u'您发送的是地理位置消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_LINK:
            respContent = u'您发送的是链接消息!'
        elif msgType == MessageUtil.REQ_MESSAGE_TYPE_EVENT:
            eventType = requestMap.get(u'Event')
            if eventType == MessageUtil.EVENT_TYPE_SUBSCRIBE:
                respContent = u'^_^谢谢您的关注,本公众号由王宁宁开发(python2.7+django1.4),如果你有兴趣继续开发,' \
                              u'可以联系我,就当打发时间了.'
            elif eventType == MessageUtil.EVENT_TYPE_UNSUBSCRIBE:
                pass
            elif eventType == MessageUtil.EVENT_TYPE_SCAN:
                # TODO
                pass
            elif eventType == MessageUtil.EVENT_TYPE_LOCATION:
                # TODO
                pass
            elif eventType == MessageUtil.EVENT_TYPE_CLICK:
                # TODO
                pass

        textReply.setContent(respContent)
        respXml = MessageUtil.class2xml(textReply)

        return respXml



        """
        if msgType == 'text':
            content = requestMap.get('Content')
            # TODO

        elif msgType == 'image':
            picUrl = requestMap.get('PicUrl')
            # TODO

        elif msgType == 'voice':
            mediaId = requestMap.get('MediaId')
            format = requestMap.get('Format')
            # TODO

        elif msgType == 'video':
            mediaId = requestMap.get('MediaId')
            thumbMediaId = requestMap.get('ThumbMediaId')
            # TODO

        elif msgType == 'location':
            lat = requestMap.get('Location_X')
            lng = requestMap.get('Location_Y')
            label = requestMap.get('Label')
            scale = requestMap.get('Scale')
            # TODO

        elif msgType == 'link':
            title = requestMap.get('Title')
            description = requestMap.get('Description')
            url = requestMap.get('Url')
        """

###编写wechat的views.py
最后为了更好地组织代码,我们在wechat这个app文件家里再建立两个文件views.py和urls.py,后面我们再把wechat的urls加入到mysite里边。

# -*- coding:utf-8 -*-
"""
# Author: Pegasus Wang (pegasuswang@qq.com, http://ningning.today)
# Created Time : Wed Feb 18 18:18:10 2015

# File Name: views.py
# Description:

# :copyright: (c) 2015 by Pegasus Wang.
# :license: MIT, see LICENSE for more details.
"""
# Create your views here.
from django.http import HttpResponse
from django.views.generic.base import View
from .wechatUtil import checkSignature
from .wechatService import WechatService


class WechatInterfaceView(View):

    def get(self, request):
        echostr = request.GET.get(u'echostr', None)
        if checkSignature(request):
            return HttpResponse(echostr)

    def post(self, request):
        return HttpResponse(WechatService.processRequest(request))

这里定义了一个通用视图类,用来处理微信公众账号发过来的请求。


###编写wechat的urls.py

# -*- coding:utf-8 -*-
"""
# Author: Pegasus Wang (pegasuswang@qq.com, http://ningning.today)
# Created Time : Fri Feb 20 22:31:30 2015

# File Name: urls.py
# Description:

# :copyright: (c) 2015 by Pegasus Wang.
# :license: MIT, see LICENSE for more details.
"""

from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt
from wechat import views


urlpatterns = patterns('',
    url(r'^$', csrf_exempt(views.WechatInterfaceView.as_view())),
)

###编写mysite的urls.py

from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
    url(r'^wechat/', include('wechat.urls', namespace='wechat')),
)

###大功告成
到这里所有的代码就编写完成了,为了像模像样,我们再项目里加上LISENCE和README.md文件。看看最终的项目结构,用tree 2命令,2是文件夹名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
├── LICENSE
├── README.md
├── config.yaml
├── index.wsgi
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── wechat
├── __init__.py
├── models.py
├── tests.py
├── urls.py
├── views.py
├── wechatEvent.py
├── wechatMessage.py
├── wechatReply.py
├── wechatService.py
└── wechatUtil.py

用svn上传所有代码到sae服务器,然后就可以测试了。效果如下:
如果结果不符合预期,你可以参考鹦鹉学舌那篇文章用curl或者python的requests模块模拟微信服务器发送post请求到你的sae服务器,观察sae返回的结果就知道什么问题了。