Django實現微信公眾號簡單自動回復


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

  內容概要:

  (1)微信公眾號聲請

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

  (3)文本回復實現

  (4)圖片回復實現

1. 微信公眾號聲請

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

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

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

  公眾平台官網登錄之后,找到“基本配置”菜單欄,如下圖:

  

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

  同時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開發權限,目前只能到此。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM