Django實現微信公衆號簡單自動回覆

  在上篇博客阿里雲部署django實現公網訪問已經實現了了django在阿里雲上的部署,接下來記錄django實現微信公衆號簡單回覆的開發過程,以方便往後查看html

  內容概要:python

  (1)微信公衆號聲請算法

  (2)微信公衆號開發者配置django

  (3)文本回復實現服務器

  (4)圖片回覆實現微信

1. 微信公衆號聲請

  微信公衆號的申請就不做介紹了,參考微信公衆平臺開發者文檔中的入門指引微信公衆平臺

2. 微信公衆號開發者配置

  開發者配置是微信公衆號開發的第一步,顯得極其重要函數

  公衆平臺官網登陸以後,找到「基本配置」菜單欄,以下圖:工具

  

  重點說明URL(服務器地址的配置),即與微信服務器直接通信的服務器地址,我這裏設置的是http://外網ip/wx/post

  同時django中的配置以下:(說明:個人django工程爲mysite,微信應用爲wechat)

  (1)mysite目錄下的urls.py配置以下

#from django.contrib import admin
#from django.urls import path
#from django.conf.urls import include,url

from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include(('blog.urls',"blog"),namespace="blog")),
    url(r'^account/', include(('account.urls','account'),namespace='account')),
    url(r'^wx/', include(('wechat.urls','wechat'),namespace='wechat')),
]

  (2)wechat目錄下的urls.py配置以下

from django.conf.urls import url
from .views import WeChat

urlpatterns = [url(r'^$', WeChat.as_view())]

   注:第一次個人URL配置爲http://外網ip/wx,但在進行微信回覆時提示"You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SL.....",百度後將修改settings:APPEND_SLASH=False也沒有成功,後將配置改成http://外網ip/wx/成功了,若你們遇到一樣的問題,能夠多作嘗試,主要緣由仍是由於表單的提交要將from的action地址改成/結尾

  (3)token驗證

  token驗證流程以下圖:

  

  代碼實現:

# Create your views here.
# -*- coding: utf-8 -*-
from django.shortcuts import render

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.base import View
from django.template import loader, Context
from xml.etree import ElementTree as ET
import time
import hashlib
from .analysis import Analysis
from django.utils.encoding import smart_str

class WeChat(View):
  #這裏我當時寫成了防止跨站請求僞造,其實不是這樣的,偏偏相反。由於django默認是開啓了csrf防禦中間件的
  #因此這裏使用@csrf_exempt是單獨爲這個函數去掉這個防禦功能。
    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(WeChat, self).dispatch(*args, **kwargs)

    #微信的介入驗證是GET方法
    #微信正常的收發消息是POST方法
    @csrf_exempt
    def get(self, request):
        print("welcome wx")
        #下面這四個參數是在接入時,微信的服務器發送過來的參數
        signature = request.GET.get('signature', None)
        #print(signature)
        timestamp = request.GET.get('timestamp', None)
        nonce = request.GET.get('nonce', None)
        echostr = request.GET.get('echostr', None)

        #這個token是咱們本身來定義的,而且這個要填寫在開發文檔中的Token的位置
        token = 'fateli'

        #把token,timestamp, nonce放在一個序列中,而且按字符排序
        hashlist = [token, timestamp, nonce]
        hashlist.sort()

        #將上面的序列合成一個字符串
        hashstr = ''.join([s for s in hashlist])

        #經過python標準庫中的sha1加密算法,處理上面的字符串,造成新的字符串。
        s1 = hashlib.sha1()
        s1.update(hashstr.encode("utf8")) 
        hashstr = s1.hexdigest()
        #print(hashstr)
        #把咱們生成的字符串和微信服務器發送過來的字符串比較,
        #若是相同,就把服務器發過來的echostr字符串返回去
        if hashstr == signature:
            return HttpResponse(echostr)
        else:
            return HttpResponse("field")

   配置成功後就能夠開始後續的消息回覆工做了。若出現爲問題,必定要仔細閱讀開發者文檔說明。

3. 文本回復實現

  回覆的實現主要是要清除協議,其後就很簡單了。

(1)接受文本格式

<xml>
<ToUserName><![CDATA[公衆號]]></ToUserName>
 <FromUserName><![CDATA[粉絲號]]></FromUserName>
 <CreateTime>1460537339</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[歡迎開啓公衆號開發者模式]]></Content>
 <MsgId>6272960105994287618</MsgId>
 </xml>

(2)回覆文本格式

<xml>
 <ToUserName><![CDATA[粉絲號]]></ToUserName>
 <FromUserName><![CDATA[公衆號]]></FromUserName>
 <CreateTime>1460541339</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[test]]></Content>
 </xml>

(3)代碼實現

  新建analysis.py

from xml.etree import ElementTree as ET
import time

class Analysis:
    def __init__(self, xmlData):
        print("接收到的數據:" + xmlData)

    def prase(self, xmlText):
        xmlData = ET.fromstring(xmlText)
        msgType = xmlData.find("MsgType").text
        toUserName = xmlData.find("ToUserName").text
        fromUserName= xmlData.find("FromUserName").text

        if msgType == 'text':
            content = xmlData.find("Content").text

            TextMsgObj = TextMsg(toUserName, fromUserName, content)
            return TextMsgObj.structReply()

        elif msgType == 'image':
            mediaId = xmlData.find("MediaId").text

            ImageMsgObj = ImageMsg(toUserName,fromUserName,mediaId)
            return ImageMsgObj.structReply()

class TextMsg:
    def __init__(self,toUser,fromUser,recvMsg):
        self._toUser = toUser
        self._fromUser = fromUser
        self._recvMsg = recvMsg
        self._nowTime = int(time.time())

    def structReply(self):
        content = self._recvMsg
        text = """
                <xml>
                <ToUserName><![CDATA[{0}]]></ToUserName>
                <FromUserName><![CDATA[{1}]]></FromUserName>
                <CreateTime>{2}</CreateTime>
                <MsgType><![CDATA[text]]></MsgType>
                <Content><![CDATA[{3}]]></Content>
                </xml>
                """.format(self._fromUser, self._toUser,self._nowTime,content)   #前面兩個參數的順序須要特別注意

        return text

   POST代碼以下:

@csrf_exempt
    def post(self, request):
        print("POST請求")
        analysisObj = Analysis(smart_str(request.body))
        toWxData = analysisObj.prase(smart_str(request.body))
        print(toWxData)
        return HttpResponse(smart_str(toWxData))

4. 圖片回覆實現

  實現了文本回復後圖片恢復也就很簡單了,過程同樣,只是協議字段有區別

(1)接受文本格式

<xml>
<ToUserName><![CDATA[公衆號]]></ToUserName>
 <FromUserName><![CDATA[粉絲號]]></FromUserName>
 <CreateTime>1460536575</CreateTime>
 <MsgType><![CDATA[image]]></MsgType>
 <PicUrl><![CDATA[http://mmbiz.qpic.cn/xxxxxx /0]]></PicUrl>
 <MsgId>6272956824639273066</MsgId>
 <MediaId><![CDATA[gyci5a-xxxxx-OL]]></MediaId>
 </xml>

(2)回覆文本格式

<xml>
 <ToUserName><![CDATA[粉絲號]]></ToUserName>
 <FromUserName><![CDATA[公衆號]]></FromUserName>
 <CreateTime>1460536576</CreateTime>
 <MsgType><![CDATA[image]]></MsgType>
 <Image>
 <MediaId><![CDATA[gyci5oxxxxxxv3cOL]]></MediaId>
 </Image>
 </xml>

注意回覆文本格式中只有MediaId,後續博客進行說明

(3)代碼實現

class ImageMsg:
    def __init__(self,toUser,fromUser,mediaId):
        self._toUser = toUser
        self._fromUser = fromUser
        self._rediaId = mediaId
        self._nowTime = int(time.time())
        self._mediaId = mediaId

    def structReply(self):
        text = """
                <xml>
                <ToUserName><![CDATA[{0}]]></ToUserName>
                <FromUserName><![CDATA[{1}]]></FromUserName>
                <CreateTime>{2}</CreateTime>
                <MsgType><![CDATA[image]]></MsgType>
                <Image>
                <MediaId><![CDATA[{3}]]></MediaId>
                </Image>
                </xml>
                """.format(self._fromUser, self._toUser,self._nowTime,self._mediaId)   #前面兩個參數的順序須要特別注意

        return text

  

  在開發過程當中遇到問題,可使用微信公衆平臺提供的在線接口調試工具。

原計劃是繼續進行菜單項的開發,但因爲是我的訂閱號,沒法卡通認證,也就沒法獲取API開發權限,目前只能到此。
相關文章
相關標籤/搜索