入門指引

1墓捻、開啟公眾號(hào)開發(fā)者模式

公眾平臺(tái)的技術(shù)文檔目的為了簡明扼要的交代接口的使用厉碟,語句難免苦澀難懂废菱,甚至對(duì)于不同的讀者搀继,有語義歧義吟策。萬事皆是入門難,對(duì)于剛?cè)腴T的開發(fā)者講苟翻,更是難上加難搭伤,往往看了半天wiki,就是不懂說的什么鬼袜瞬。

為了降低門檻,彌補(bǔ)不足身堡,特地編寫了《開發(fā)者指引》邓尤,講解了一下微信開放平臺(tái)的基礎(chǔ)常見功能,目的重在幫助大家入門微信開放平臺(tái)的開發(fā)者模式。

對(duì)于已熟知或者在一定公眾平臺(tái)開發(fā)經(jīng)驗(yàn)的開發(fā)小哥汞扎,請(qǐng)直接跳過本文季稳。這篇文章不會(huì)給你帶來厲害的編碼技巧亦或接口的深層次講解。對(duì)于現(xiàn)有接口存在的疑問澈魄,請(qǐng)直接呼叫克服或者微信投訴景鼠,不要要在本文浪費(fèi)時(shí)間。

為了防止小編語意上歧義痹扇,在給大家?guī)砝_铛漓,直接用最暴力的方法帶你入門公眾號(hào)開發(fā)——附錄簡明代碼

1.1申請(qǐng)服務(wù)器

以騰訊云服務(wù)器為示例:騰訊云服務(wù)器購買入口,購買指導(dǎo)請(qǐng)參考快速人們Linux云服務(wù)器鲫构。

學(xué)生黨注意:騰訊公司為在讀高校生提供了云+校園計(jì)劃浓恶,1元/月即可使用騰訊云。

1.2搭建服務(wù)

以web.py網(wǎng)絡(luò)框结笨,Python包晰,騰訊云服務(wù)器為例介紹。

1)安裝/更新需要用到的軟件

安裝Python2.7版本以上

安裝web.py

安裝libxml2,libxslt,lxml python

2)編輯代碼炕吸,如果不懂Python語法伐憾,青島Python官方文檔查詢說明,vim main.py

# -*- coding: utf-8 -*-

# filename: main.py

import web

urls = (

'/wx', 'Handle',

)

class Handle(object):

def GET(self):

return "hello, this is a test"

if _name_=='_main_':

app = web.application(urls,globals())

3)如果出現(xiàn)“socket.error:No socket could be created”錯(cuò)誤信息赫模,可能為80端口號(hào)被占用树肃,可能是沒有權(quán)限,請(qǐng)自行查詢解決辦法嘴瓤。如果遇見其他錯(cuò)誤信息扫外,請(qǐng)到web.py官方文檔學(xué)習(xí)webpy框架3)執(zhí)行命令:sudo python main.py 80.

4)瀏覽器輸入http://外網(wǎng)IP:80/wx(外網(wǎng)IP請(qǐng)到騰訊云購買成功處查詢)。如下圖廓脆,一個(gè)簡單的web應(yīng)用已搭建筛谚。

1.3 申請(qǐng)公眾號(hào)

郵箱激活后,選擇公眾號(hào)類型停忿。不同的公眾號(hào)擁有不同的能力驾讲,詳情請(qǐng)見wiki:公眾號(hào)接口權(quán)限說明。當(dāng)然席赂,服務(wù)號(hào)吮铭、企業(yè)號(hào)需要一定的證件和相關(guān)資料填寫,如果證件一時(shí)不能準(zhǔn)備好颅停,沒關(guān)系谓晌,公眾號(hào)其實(shí)已注冊(cè),下次可以根據(jù)此郵箱&密碼登錄再選中癞揉。

1.4 開發(fā)者基本配置

1)公眾平臺(tái)官網(wǎng)登錄之后纸肉,找到“基本配置”菜單欄

2)填寫配置

URL填寫:http://外網(wǎng)IP:端口號(hào)/wx溺欧。外網(wǎng)IP請(qǐng)到騰訊云購買成功處查詢,http的端口號(hào)固定使用80柏肪,不可填寫其他姐刁。

Token:自主設(shè)置,這個(gè)token與公眾平臺(tái)wiki中常提的access_token不是一回事烦味。這個(gè)token只用于驗(yàn)證開發(fā)者服務(wù)器聂使。

3)現(xiàn)在選擇提交肯定是驗(yàn)證token失敗,因?yàn)檫€需要完成代碼邏輯谬俄。改動(dòng)原先main.py文件柏靶,新增handle.py

a)vim main.py

# ?-*- coding:utf-8 -*-

# filename: main.py

import web

urls = (

'/wx','Handle',

)

if _name_ == '_main_';

app = web.application(urls,globals())

app.run()

b)vim handle.py

先附加邏輯流程圖


# -*- coding:utf-8 -*-

# filename:handle.py

import hashlib

improt web

class Handle(object):

def GET(self):

try:

data = web.input()

if len(data) == 0:

return "hello, this is handle view"

signature = data.signature

timestamp = data.timestamp

nonce = data.nonce

echostr = data.echostr

token = "xxxx" #請(qǐng)按公眾平臺(tái)官網(wǎng)\基本配置中信息填寫

list = [token,timestamp, nonce]

list..sort()

shal = hashlib.shal()

map(shal.update,list)

hashcode = shal.hexdigest()

print "handle/GET func: hashcode,signature:",hashcode,signature

if hashcode == signature:

return echostr

else:

return ""

except Exception, Argument:

return Argument

4)重新啟動(dòng)成功后(Python main.py 80),點(diǎn)擊提交按鈕凤瘦。若提示“token驗(yàn)證失敗”宿礁,請(qǐng)認(rèn)真檢查代碼或網(wǎng)絡(luò)鏈接等。若token驗(yàn)證成功蔬芥,會(huì)自動(dòng)返回基本配置的主頁面梆靖,點(diǎn)擊啟動(dòng)按鈕。

1.5 重要事情提前交代

接下來笔诵,文章準(zhǔn)備從兩個(gè)簡單的示例入手返吻。

示例一:實(shí)現(xiàn)“你說我學(xué)”

示例一:實(shí)現(xiàn)“圖尚往來”

兩個(gè)簡單的示例后,是一些基礎(chǔ)功能的介紹:素材管理乎婿、自定義菜單测僵、群發(fā)。所有示例代碼是為了簡明的說明問題谢翎,避免代碼復(fù)雜化捍靠。在實(shí)際中搭建一個(gè)安全穩(wěn)定高效的公眾號(hào),建議參考框架如下圖:


主要有三個(gè)部分:負(fù)責(zé)業(yè)務(wù)邏輯部分的服務(wù)器森逮,負(fù)責(zé)對(duì)接微信API的API-Proxy服務(wù)器榨婆,以及唯一的Access Token中控服務(wù)器

1)AccessToken中控服務(wù)器:

負(fù)責(zé):提供主動(dòng)刷新和被動(dòng)刷新機(jī)制來刷新accessToken并存儲(chǔ)(為了防止并發(fā)刷新,注意加并發(fā)鎖)褒侧,提供給業(yè)務(wù)邏輯有效的accessToken良风。

優(yōu)點(diǎn):避免業(yè)務(wù)邏輯方并發(fā)獲取access_token,避免AccessToken互相覆蓋,提高業(yè)務(wù)功能的穩(wěn)定性闷供。

2)API-Proxy服務(wù)器:

負(fù)責(zé):專一與微信API對(duì)接烟央,不同的服務(wù)器可以負(fù)責(zé)對(duì)接不同的業(yè)務(wù)邏輯,更可進(jìn)行調(diào)用頻率歪脏、權(quán)限限制疑俭。

優(yōu)點(diǎn):某臺(tái)API-Proxy異常,還有其余服務(wù)器支持繼續(xù)提供服務(wù)婿失,提高穩(wěn)定性钞艇,避免直接暴漏內(nèi)部接口鬼贱,有效防止惡意攻擊,提高安全性香璃。

2 實(shí)現(xiàn)“你問我答”

目的:

1)理解被動(dòng)消息的含義

2)理解收\發(fā)下次機(jī)制

預(yù)實(shí)現(xiàn)功能:粉絲給公眾號(hào)一條文本消息,公眾號(hào)立馬回復(fù)一條文本消息給粉絲舟误,不需要經(jīng)過公眾平臺(tái)網(wǎng)頁操作葡秒。

2.1接受文本消息

即粉絲給公眾號(hào)發(fā)送的文本消息。官方wiki鏈接:消息管理/接收消息-接受普通消息

粉絲給公眾號(hào)發(fā)送文本消息:“歡迎開啟公眾號(hào)開發(fā)者模式”嵌溢,在開發(fā)者后臺(tái)眯牧,收到公眾平臺(tái)發(fā)送的xml如下:(下文隱藏了ToUserName及FromUserName消息)

<xml>

<ToUserName><![CDATA[公眾號(hào)]]></ToUserName>

<FromUserName><![CDATA[粉絲號(hào)]]></FromUserName>

<CreateTime>1460537339</CreateTime>

<MsgType><![CDATA[text]]></MsgType>

<Content><![CDATA[歡迎開啟公眾號(hào)開發(fā)者模式]]></Content>

<MsgId>6272960105994287618</MsgId>

</xml>

解釋:

createTime 是微信公眾平臺(tái)記錄粉絲發(fā)送該消息的具體時(shí)間

text:用于標(biāo)記該xml是文本消息,一般用于去唄判斷

歡迎開啟公眾號(hào)開發(fā)者模式:說明該粉絲發(fā)給公眾號(hào)的具體內(nèi)容是歡迎開啟公眾號(hào)開發(fā)者模式

MsgId:是公眾平臺(tái)為記錄識(shí)別該消息的一個(gè)標(biāo)記數(shù)值赖草,微信后臺(tái)系統(tǒng)自動(dòng)產(chǎn)生

2.2 被動(dòng)回復(fù)文本消息

即公眾號(hào)給粉絲發(fā)送的文本消息学少,官方wiki鏈接:消息管理/接收消息-被動(dòng)回復(fù)消息

特別強(qiáng)調(diào):

1)被動(dòng)回復(fù)消息,即發(fā)送被動(dòng)響應(yīng)消息秧骑,不同于客服消息接口

2)它其實(shí)并不是一種接口版确,而是對(duì)微信服務(wù)器發(fā)過來消息的一次回復(fù)

3)收到粉絲消息后不想或者不能5秒內(nèi)回復(fù)時(shí),需回復(fù)“success”字符串(下文詳細(xì)介紹)

4)客服接口在滿足一定條件下隨時(shí)調(diào)用

公眾號(hào)想回復(fù)給粉絲一條文本消息乎折,內(nèi)容為“test”绒疗,那么開發(fā)者發(fā)送給公眾平臺(tái)后臺(tái)的xml內(nèi)容如下:

<xml>

<ToUserName><![CDATA[粉絲號(hào)]]</ToUserName>

<FromUserName><![CDATA[公眾號(hào)]]></FromUserName>

<CreateTime>1460541339</CreateTime>

<MsgType><![CDATA[text]]></MsgType>

<Content><![CDATA[test]]></Content>

</xml>

特別備注:

1)ToUserName(接受者)、FromUserName(發(fā)送者)字段請(qǐng)實(shí)際填寫骂澄。

2)createtime只用于標(biāo)記開發(fā)者回復(fù)消息的時(shí)間吓蘑、微信后臺(tái)發(fā)送此消息都是不受這個(gè)字段約束。

3)text:用于標(biāo)記此次行為是發(fā)送文本消息(當(dāng)然可以是image/voice等類型)坟冲。

4)文本換行 ‘\n’磨镶。

2.3 回復(fù)success問題

查詢官方wiki開頭強(qiáng)調(diào):加入服務(wù)器無法保證在五秒內(nèi)處理回復(fù),則必須回復(fù)“success”或者“”(空串)健提,否則微信后臺(tái)會(huì)發(fā)起三次重試琳猫。

解釋一下為何有這么奇怪的規(guī)定。發(fā)起重試是微信后臺(tái)為了盡可以保證粉絲發(fā)送的內(nèi)容開發(fā)者可以收到矩桂。如果開發(fā)者不進(jìn)行回復(fù)沸移,微信后臺(tái)沒辦法確認(rèn)開發(fā)者已收到消息,只好重試侄榴。

真的是這樣子嗎雹锣?嘗試一下收到消息后,不做任何回復(fù)癞蚕。在日志中查看到微信后臺(tái)發(fā)起了三次重試操作蕊爵,日志截圖如下:

三次重試后,依舊沒有及時(shí)回復(fù)任何內(nèi)容桦山,系統(tǒng)自動(dòng)在粉絲會(huì)話界面出現(xiàn)錯(cuò)誤提示“該公眾號(hào)暫時(shí)無法提供服務(wù)攒射,請(qǐng)稍后再試”醋旦。

如果回復(fù)success,微信后臺(tái)可以確定開發(fā)者收到了粉絲消息,沒有任何異常提示会放。因此請(qǐng)大家注意回復(fù)success的問題饲齐。


2.5 碼代碼

main.py文件不改變,handle.py需要增加一下代碼咧最,增加新的文件receive.py捂人,reple.py

1)vim handle.py

# -*- coding: utf-8 -*-

# filename: handle.py

import hashlib

import reply

import receive

import web

class Handle(object):

def POST(self):

try:

webData = web.data()

print "Handle Post webdata is ",webData #后臺(tái)打日志

recMsg = receive.parse_xml(webData)

if isinstance(recMsg,receive.Msg) and recMsg.MsgType == 'text':

toUser = recMsg.FromUserName

fromUser = recMsg.ToUserName

content = "test"

replyMsg = reple.TextMsg(toUser, fromUser, content)

return replyMsg.send()

else:

print "暫且不處理"

return "success"

except Exception, Argment:

return Argment

2)vim receive.py

# -*- coding: ?utf-8 -*-

#filename:receive.py

import xml.etree.ElementTree as ET

def parse_xml(web_data):

if len(web_data) == 0:

return None

xmlData = ET.fromstring(web_data)

mas_type = xmlData.find('MsgType').text

if mas_type == 'text':

return TextMsg(xmlData)

elif msg_type === 'image':

return ImageMsg(xmlData)

class Msg(object):

def _init_(self, xmlData):

self.ToUserName = xmlData.find('ToUserName').text

self.FromUserName = xmlData.find('FromUserName').text

self.CreateTime = xmlData.find('CreateTime').text

self.MsgType = xmlData.find('MsgType').text

self.MsgId = xmlData.find('MsgId').text

class TextMsg(Msg):

def _init_(self,xmlData):

Msg._init_(self,xmlData)

self.Content = xmlData.find('Content').text.encode("utf-8")

class ImageMsg(Msg):

def _int_(self,xmlData)

self.PicUrl = xmlData.find('PicUrl').text

self.MediaId = xmlData.find('MediaId').text

3)vim reply.py

# -*- coding:utf-8 -*-

# filename: reply.py

import time

class Msg(object):

def _init_(self):

pass

def send(self):

return "success"

class TextMsg(Msg):

def _init_(self,toUserName,fromUserName,content):

self._dict = dict()

self._dict['ToUserName'] = toUserName

self._dict[FromUserName'] = fromUserName

self._dict['CreateTime'] = int(time.time())

self._dict['Content'] = content

def send(self):

XmlForm = """

<xml>

<ToUserName><![CDATA[{ToUserName}]]></ToUserName>

<FromUserName><![CDATA[{FromUserName}]]></FromUserName>

<CreateTime>{CreateTime}</CreateTime>

<MsgType><![CDATA[text]]></MsgType>

<Content><![CDATA[{Content}]]></Content>

</xml>

"""

return XmlForm.format(**self._dict)

class ImageMsg(Msg):

def _init_(self,toUserName,fromUserName,mediaId):

self._dict = dict()

self._dict['ToUserName'] = toUserName

self._dict['FromUserName'] = fromUserName

self._dict['CreateTime'] = int(time.time())

self._dict['MediaId'] = mediaId

def send(self):

XmlForm = """

<xml>

<ToUserName><![CDATA[{ToUserName}]]></ToUserName>

<FromUserName><![CDATA[{FromUserName}]></FromUserName>

<CreateTime>{CreateTime}</CreateTime>

<MsgType><![CDATA[image]]></MsgType>

<Image>

<MediaId><![CDATA[{MediaId}]]></MediaId>

</Image>

</xml>

"""

return XmlForm.format(**self._dict)

碼好代碼之后,重新啟動(dòng)程序矢沿,sudo python main.py 80滥搭。

2.6 在線測(cè)試

微信公眾平臺(tái)有提供一個(gè)在線測(cè)試平臺(tái),方便開發(fā)者模擬場(chǎng)景測(cè)試代碼邏輯捣鲸。正如2.2被動(dòng)回復(fù)文本消息 交代此被動(dòng)回復(fù)接口不同于客服接口瑟匆,測(cè)試時(shí)也要注意區(qū)別。

在線測(cè)試目的在于測(cè)試開發(fā)者代碼邏輯是否有誤栽惶、是否符合預(yù)期愁溜。即便測(cè)試成功也不會(huì)發(fā)送內(nèi)容給粉絲。所以可以隨意測(cè)試媒役。

測(cè)試結(jié)果:


1)“請(qǐng)求失敗”祝谚,說明代碼有問題,請(qǐng)檢查代碼邏輯酣衷。

2)“請(qǐng)求成功”交惯,然后根據(jù)返回結(jié)果查看是否符合預(yù)期。


2.7 真實(shí)體驗(yàn)

拿出手機(jī)穿仪,微信掃描公眾號(hào)二維碼席爽,成為自己公眾號(hào)的第一個(gè)粉絲。公眾號(hào)二維碼位置如下圖:



3 實(shí)現(xiàn)“圖”尚往來

目的:

1)引入素材管理

2)以文本消息啊片,圖片消息為基礎(chǔ)只锻,可自行理解剩余的語音消息、視頻消息紫谷、地理消息等

預(yù)實(shí)現(xiàn)功能:

接受粉絲發(fā)送的圖片消息齐饮,并立馬回復(fù)相同的圖片給粉絲。

3.1 接收?qǐng)D片消息

即粉絲給公眾號(hào)發(fā)送的圖片消息笤昨。官方wiki鏈接:消息管理/接收消息-接受普通消息/圖片消息http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453&token=&lang=zh_CN祖驱。從實(shí)例講解,粉絲給公眾號(hào)發(fā)送一張圖片消息瞒窒,在公眾號(hào)開發(fā)者后臺(tái)接收到的xml如下:

<xml>

<ToUserName><![CDATA[公眾號(hào)]]></ToUserName>

<FromUserName><![CDATA[粉絲號(hào)]]></FromUserName>

<CreateTime>1460536575</CreateTime>

<MsgType><![CDATA[image]]</MsgType>

<PicUrl><![CDATA[http://mmbiz.qpic.cn/xxxxx/0]]></PicUrl>

<MsgId>6272956824639273066</MsgId>

<MediaId><![CDATA[gyci5a-xxxxx-OL]]></MediaId>

</xml>

特別說明:

PicUrl:這個(gè)參數(shù)是微信系統(tǒng)把“粉絲”發(fā)送的圖片消息自動(dòng)轉(zhuǎn)化成url.這個(gè)url可用瀏覽器打開查看圖片捺僻。

MediaId:是微信系統(tǒng)產(chǎn)生的id用于標(biāo)記該圖片,詳情可參看wiki素材管理/獲取臨時(shí)素材。http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738727&token=&lang=zh_CN

3.2 被動(dòng)回復(fù)圖片消息

即公眾號(hào)給粉絲發(fā)送的圖片消息匕坯。官方wiki鏈接:消息管理/發(fā)送消息-被動(dòng)回復(fù)用戶消息/圖片消息束昵。http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543&token=&lang=zh_CN

特別說明:

1)被動(dòng)回復(fù)消息,即發(fā)送被動(dòng)響應(yīng)消息葛峻,不同于客服消息接口

2)它其實(shí)并不是一種接口锹雏,而是對(duì)微信服務(wù)器發(fā)過來消息的一次回復(fù)

3)收到粉絲消息后不想或者不能5秒內(nèi)回復(fù)時(shí),需回復(fù)“success”字符串(下文詳細(xì)介紹)

4)客服接口在滿足一定條件下隨時(shí)調(diào)用

開發(fā)者發(fā)送給微信后臺(tái)的xml如下:

<xml>

<ToUserName><![CDATA[粉絲號(hào)]]></FromUserName>

<CreateTime>1460536575</CreateTime>

<MsgType><![CDATA[image]]></MsgType>

<Image>

<MediaId><!CDATA[gyci5oxxxxxxv3cOL]]</MediaId>

</Image>

</xml>

這里填寫的MediaId的內(nèi)容术奖,其實(shí)就是給粉絲的發(fā)送圖片的原MediaId逼侦,所以粉絲收到了一張一模一樣的原圖。

如果想回復(fù)粉絲其他圖片怎么呢腰耙?

1)新增素材,請(qǐng)參考新增臨時(shí)素材或者新增永久素材

2)獲取其MediaId铲球,請(qǐng)參考獲取臨時(shí)素材MediaID或者獲取永久素材MediaID

3.3 流程圖


3.4 碼代碼

只顯示更改的代碼部分挺庞,其余部分參考上小節(jié),在線測(cè)試稼病,真實(shí)體驗(yàn)选侨,回復(fù)空串,請(qǐng)參考實(shí)現(xiàn)“你問我答”然走。vim handle.py

# -*- coding: utf-8 -*-

# filename: handle.py

import hashlib

import reply

import receive

import web

class Handle(object):

def POST(self):

try:

webData = web.data()

print "Handle Post webdata is ", webData #后臺(tái)打日志

recMsg = receive.parse_xml(webData)

if isinstance(recMsg,receive.Msg):

toUSER = recMsg.FromUserName

fromUser = recMsg.ToUserName

if recMsg.Msg.MsgType == 'text':

content = "test"

replyMsg = reply.TextMsg(toUser,fromUser,content)

return replyMsg.send()

if recMsg.MsgType == 'image':

mediaId = recMsg.MediaId

replyMsg = reple.ImageMsg(toUser,fromUser,mediaId)

return replyMsg.send()

else:

return reply.Msg().send()

else:

print "暫且不處理"

return reply.Msg().send()

except Exception,Argment:

return Argment

4 AccessToken

AccessToken的意義請(qǐng)參考公眾平臺(tái)wikihttp://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN介紹援制。

4.1 查看appid及appsecret

公眾平臺(tái)官網(wǎng)查看,其中AppSecret不點(diǎn)擊重置時(shí)候芍瑞,則一直保持不變晨仑。


4.2 獲取accessToken

4.2.1 臨時(shí)方法獲取

為了方便先體驗(yàn)其他接口,可以臨時(shí)通過在線測(cè)試http://mp.weixin.qq.com/debug/或者瀏覽器獲取accessToken拆檬。

4.2.2 接口獲取

詳情請(qǐng)見:公眾平臺(tái)wikihttp://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN

特別強(qiáng)調(diào):

1)第三方需要一個(gè)access_token獲取和刷新的中控服務(wù)器洪己。

2)并發(fā)獲取access_token會(huì)導(dǎo)致AccessToken互相覆蓋,影響具體的業(yè)務(wù)功能

4.3 碼代碼

再次重復(fù)說明竟贯,下面代碼只是為了簡單說明接口獲取方式答捕。實(shí)際中并不推薦,尤其是業(yè)務(wù)繁重的公眾號(hào)屑那,更需要中控服務(wù)器拱镐,統(tǒng)一的獲取accessToken。vim basic.py

# -*- coding: utf-8 -*-

# filename:basic.py

import urllib

import time

import json

class Basic:

def _init_(self):

self._accessToken = ?''

self._leftTime = 0

def _real_get_access_token(self):

appId = "xxxxx"

appSecret = "xxxxx"

postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type="

"client_credential&appid=%s&secret=%s" % (appId,appSecret))

urlResp = urllib.urlopen(postUrl)

urlResp = json.loads(urlResp.read())

self._accessToken = urlResp['access_token']

self._leftTime = urlResp['expires_in']

def get_access_token(self):

if self._leftTime < 10:

self._real_get_access_token()

return self._accessToken

def run(self):

while(True):

if self._leftTime > 10:

time.sleep(2)

self._leftTime -= 2

else:

self._real_get_access_token()

5 臨時(shí)素材

公眾號(hào)經(jīng)常有需要用到一些臨時(shí)性的多媒體素材的場(chǎng)景持际,例如在使用接口特別是發(fā)送消息時(shí)沃琅,對(duì)多媒體文件、多媒體消息的獲取和調(diào)用等操作选酗,是通過MediaID來進(jìn)行的阵难。譬如實(shí)現(xiàn)“圖”尚往來中,粉絲給公眾號(hào)發(fā)送圖片消息芒填,便產(chǎn)生一臨時(shí)素材呜叫。

因?yàn)橛谰盟夭挠袛?shù)量限制空繁,但是公眾號(hào)又需要臨時(shí)性使用一些素材,因而產(chǎn)生了臨時(shí)素材朱庆。這類素材不在微信公眾平臺(tái)后臺(tái)長期存儲(chǔ)盛泡,所以在公眾平臺(tái)官網(wǎng)的素材管理中查詢不到,但是可以通過接口對(duì)其操作娱颊。

其他詳情請(qǐng)以公眾平臺(tái)官網(wǎng)wiki介紹為依據(jù)傲诵。

5.1 新建臨時(shí)素材

接口詳情請(qǐng)依據(jù)wiki介紹。提供參考代碼如何上傳素材為臨時(shí)素材箱硕,供其他接口使用拴竹。

vim media.py 編寫完成之后,直接運(yùn)行media.py即可上傳臨時(shí)素材剧罩。

# -*- coding: utf-8 -*-

#filename: media.py

from basic import Basic

import urllib2

import poster.encode

from poster.streaminghttp import register_openers

class Media(object):

def_init_(self):

register_openers()

#上傳圖片

def upload(self, accessToken,filePath, mediaType):

openFile = open(filePath, "rb")

param = {'media': openFile}

postData,postHeaders = poster.encode.multipart_encode(param)

postUrl = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s" % (accessToken,mediaType)

request = urllib2.Request(postUrl,postData,postHeaders)

urlResp = urllib2.urlopen(request)

print urlResp.read()

if _name_ == '_main_':

myMedia = Media()

accessToken = Basic().get_access_token()

filePath = "D:/code/mpGuide/media/test.jpg" #請(qǐng)按實(shí)際填寫

mediaType = "image"

myMedia.upload(accessToken, filePath, mediaType)

5.2 獲取臨時(shí)素材MdidaID

臨時(shí)素材的MediaID沒有提供特定的接口進(jìn)行統(tǒng)一查詢栓拜,因此有兩種方式

1)通過接口上次的臨時(shí)素材,在調(diào)用成功的情況下惠昔,從返回JSON數(shù)據(jù)中提取MediaID,可臨時(shí)使用

2)粉絲互動(dòng)中的臨時(shí)素材幕与,可從xml數(shù)據(jù)中提取MedaID,可臨時(shí)使用

5.3 下載臨時(shí)素材

5.3.1 手工體驗(yàn)

開發(fā)者如何保存粉絲發(fā)送的圖片呢?接口文檔:獲取臨時(shí)素材接口http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738727&token=&lang=zh_CN镇防,為方便理解啦鸣,從最簡單瀏覽器獲取素材的方法入手,根據(jù)實(shí)際情況来氧,瀏覽器輸入網(wǎng)址:

https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID(自行替換數(shù)據(jù))

ACCESS_TOKEN如“AccessToken”章節(jié)詳解

MEDIA_ID如圖尚往來/接受圖片消息xml中的MediaId講解

只要數(shù)據(jù)正確诫给,則會(huì)下載圖片到本地,如下圖:



5.3.2 接口獲取

現(xiàn)在已經(jīng)理解這個(gè)接口的功能了啦扬,只剩碼代碼了蝙搔。vim media.py

# -*- coding:utf-9 -*-

# filename:media.py

import urllib2

import json

from basic import Basic

class Media(object):

def get(self,accessToken,mediaId):

postUrl = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s" % (accessToken, mediaId)

rulResp = urllib2.urlopen(postUrl)

headers = urlResp.info()._dict_['headers']

if ('Content-Type:application/json\r\n' in headers) or ('Content-Type: text/plain\r\n' in headers):

jsonDict = json.loads(urlResp.read())

print jsonDict

else:

buffer = urlResp.read() #素材的二進(jìn)制

mediaFile = file("test_meda.jpg", "wb")

mediaFile.write(buffer)

print "get successful"

if _name_ == '_main_':

myMedia = Media()

accessToken = Basic().get_access_token()

meidaId = "2ZsPnDj9XIQlGfws31MUfR5Iuz-rcn7F6LkX3NRCsw7nDpg2268e-dbGB67WWM-N"

myMedia.get(accessToken,mediaId)

直接運(yùn)行media.py即可把想要的素材下載下來,其中圖文消息類型的考传,會(huì)直接在屏幕輸出json數(shù)據(jù)段吃型。

6 永久素材

6.1 新建永久素材的方式

6.1.1 手工體驗(yàn)

公眾號(hào)官網(wǎng)的素材管理新增素材。補(bǔ)充一點(diǎn)僚楞,公眾平臺(tái)只以MediaID區(qū)分素材勤晚,MediaID不等于素材的文件名。MediaID只能通過接口查詢泉褐,公眾平臺(tái)官網(wǎng)看到的是素材的文件名赐写,如下圖:





6.1.2 接口刪除

新增永久素材接口(詳情見wiki)http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738729&token=&lang=zh_CN跟新增臨時(shí)素材的操作差不多,使用URL不一樣而已膜赃,這里避免重復(fù)挺邀,以新增永久圖文素材接口為例,新增其他類型的素材請(qǐng)參考新增臨時(shí)素材代碼。vim material.py

# -*- coding: urt-8 -*-

# filename: material.py

import urllib2

import json

from basic import Basic

class Material(object):

#上傳圖文

def add_news(self,accessToken,news):

postUrl = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=%s" % accessToken

urlResp = urllib2.urlopen(postUrl,news)

print urlResp.read()

if _name_ == '_main_':

myMaterial = Material()

accessToken = Basic().get_access_token()

news =(

{

"articles";

[

{

"title"; "test",

"thumb_media_id":"X2UMe5WdDJSS2AS6BQkhTw9raS0pBdpv8wMZ9NnEzns",

"author":"vickey",

"digest":"",

"show_cover_pic":1,

"content": "<p><img data-s=\"300,640\" data-type=\"jpeg\" data-src=\"http://mmbiz.qpic.cn/mmbiz/iaK7BytM0QFPLhxfSMhOHlZd2Q5cw3YibKVf4dgNpLHXdUkvl65NBSMU71rFfOEKF3ucmXuwAQbNdiaaS3441d5rg/0?wx_fmt=jpeg\" data-ratio=\"0.748653500897666\" data-w=\"\"? />",<br /></p>",

"content_source_url":"",

}

]

})

#news 是個(gè)dict類型端铛,可通過下面方式修改內(nèi)容

#news['articles'][0]['title'] = u"測(cè)試".encode(utf-8')

#print news['articles'][0]['title']

news = json.dumps(news,ensure_ascii=False)

myMaterial.add_news(accessToken,news)

6.2 獲取永久素材MediaID

1)通過新增永久素材接口(詳見wiki)http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738729&token=&lang=zh_CN新增素材時(shí)泣矛,保存MediaID

2)通過獲取永久素材列表(下文介紹)的方式獲取素材信息,從而得到MediaID

6.3 獲取素材列表

官方wiki鏈接:獲取素材列表http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738734&token=&lang=zh_CN禾蚕。特別說明:此接口只是批量拉取素材信息您朽,不是一次性拉去所有素材的信息,所以可以理解offset字段的含義了吧换淆。vim material.py

# -*- coding: utf-8 -*-

# filename:material.py

import urllib2

import json

import poster.encode

from poster.streaminghttp import register_openers

from basic import Basic

class Material(object):

def _init_(self):

register_openers()

#上傳

def upload(self, accessToken, filePath, mediaType):

openFile = open(filePath,"rb")

fileName = "hello"

param = { 'media':openFile,'filename':fileName}

#param = {'media' : openFile}

postData,postHeaders = poster.encode.multipart_encode(param)

postUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=%s&type=%s" % (accessToken, mediaType)

request = urllib2.Request(postUrl,postData,postHeaders)

urlResp = urllib2.urlopen(request)

print urlResp.read()

#下載

def get(self, accessToken,mediaId):

postUrl = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=%s" % accessToken

postData = "{ \"media_id\": \"%s\" }" % mediaId

urlResp = urlResp.info()._dict_['headers']

if ('Content-Type:application/json\r\n' in headers) or ('Content-Type: text/plain\r\n' in headers):

jsonDict = json.loads(urlResp.read())

print jsonDict

else:

buffer = urlResp.read() #素材的二進(jìn)制

mediaFile = file("test_media.jpg","wb")

mediaFile.write(buffer)

print "get successful"

#刪除

def delete(self,accessToken,mediaId):

postUrl = "https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=%s" % accessToken

postData = "{ \"media_id\": \"%s\"}" % mediaId

urlResp = urllib2.urlopen(postUrl,postData)

print urlResp.read()

#獲取素材列表

def batch_get(self, ?accessToken,mediaType, offset=0, count=20:

postUrl = ("https://api.weixin.qq.com/cgi-bin/material"

"/batchget_material?access_token=%s"?%?accessToken)

postData = ("{ \"type\": \"%s\", \"offset\": %d,\"count\": %d}"

% (mediaType,offset,count))

urlResp = urllib.urlopen(postUrl,postData)

print urlResp.read()

if _name_== '_main_':

myMaterial = Material()

accessToken = Basic().get_access_token()

mediaType = "news"

myMaterial.batch_get(accessToken,mediaType)

6.4 刪除永久素材

如果我想刪除掉20160102.jpg這張圖片哗总,除了官網(wǎng)直接操作,也可以使用接口:刪除永久素材http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738731&token=&lang=zh_CN接口文檔倍试。

首先需要知道該圖片的mediaID,方法上小節(jié)已講述讯屈。代碼可參考上小節(jié):Material().delete()接口

調(diào)用接口成功后,在公眾平臺(tái)官網(wǎng)素材管理的圖片中县习,查詢不到已刪除的圖片耻煤。


7 自定義菜單

自定義菜單意義作用請(qǐng)參考官方wikihttp://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN介紹。

目標(biāo):三個(gè)菜單欄准颓,體驗(yàn)click、view棺妓、media_id三種類型的菜單按鈕攘已,其他類型在本小節(jié)學(xué)習(xí)之后,請(qǐng)自行查詢公眾平臺(tái)wiki說明領(lǐng)悟怜跑。

7.1 創(chuàng)建菜單界面

1)根據(jù)公眾平臺(tái)wiki給的json數(shù)據(jù)編寫代碼样勃,其中涉及media_id部分請(qǐng)閱讀“永久素材”章節(jié)。vim menu.py

# -*- coding: utf-8 -*-

#filename: menu.py

import urllib

from basic import Basic

class Menu(object):

def _init_(self_):

pass

def create(self,postData,accessToken):

postURL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s" % accessToken

if isinstance(postData,unicode):

postData = postData.encode('utf-8')

urlResp = urllib.urlopen(url=postUrl,data=postData)

print urlResp.read()

def query(self,accessToken):

postUrl = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s" % accessToken

urlResp = urllib.urlopen(url=postUrl_

print urlResp.read()

def delete(self,accessToken):

postUrl = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s" % accessToken

urlResp = urllib.urlopen(url=postUrl)

print urlResp.read()

#獲取自定義菜單配置接口

def get_current_selfmenu_info(self,accessToken):

postUrl = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=%s" % accessToken

urlResp = urllib.urlopen(url=postUrl)

print urlResp.read()

if _name_ == '_main_':

myMenu = Menu()

postJson = """

{

"button":

[

{

"type":"click",

"name": "開發(fā)指引",

"key":"mpGuide"

},

{

"name":"公眾平臺(tái)",

"sub_button":

[

{

"type":"vies",

"name":"更新公告",

"url":"http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1418702138&token=&lang=zh_CN"

},

{

"type":"view",

"name": "接口權(quán)限說明",

"url":"http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1418702138&token=&lang=zh_CN"

}性芬,

{

type":"view",

"name":"返回碼說明",

"url":"http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234&token=&lang=zh_CN"

}

]

},

{

"type": "media_id",

"name": "旅行",

"media_id":"z2zOokJvlzCXXNhSjF46gdx6rSghwX2xOD5GUV9nbX4"

}

]

}

"""

accessToken = Basic().get_access_token()

#myMenu.delete(accessToken)

myMenu.create(postJson,accessToken)

2)在騰訊云服務(wù)器上執(zhí)行命令:python menu.py峡眶。

3)查看:

重新關(guān)注公眾號(hào)后可查看新創(chuàng)建菜單界面,題外話植锉,如果不重新關(guān)注辫樱,公眾號(hào)界面也會(huì)自動(dòng)更改,但有時(shí)間延遲俊庇。

如下圖所示狮暑,點(diǎn)擊子菜單“更新公告“(view類型),彈出網(wǎng)頁(PC版本)


點(diǎn)擊旅行(media_id類型)辉饱,公眾號(hào)顯示了一篇圖文消息搬男,如下圖所示:



7.2 完善菜單功能

查看公眾平臺(tái)自定義菜單wiki與公眾平臺(tái)wiki/自定義菜單事件推送后,可知:點(diǎn)擊click類型button彭沼,微信后臺(tái)會(huì)推送一個(gè)event類型的xml給開發(fā)者缔逛。

顯然,click類型的還需要開發(fā)者進(jìn)一步完善后臺(tái)代碼邏輯,增加對(duì)自定義菜單事件推送的響應(yīng)褐奴。

7.2.1 流程圖


7.2.2 碼代碼

1)vim handle.py(修改)

# -*- coding: utf-8 -*-

# filename:handle.py

import reply

import receive

import web

class Handle(object):

def POST(self):

try:

webData = web.data()

print "Handle Post webdata is ", webData #后臺(tái)打日志

recMsg = receive.parse)xml(webData)

if isinstance(recMsg, receive.Msg):

toUser = recMsg.FromUserName

fromUser = recMsg.ToUserName

if recMsg.MsgType == 'text':

content = "test"

replyMsg = reply.TextMsg(toUser,fromUser,content)

return replyMsg.send()

if recMsg.MsgType == 'image':

mediaId = recMsg.MediaId

replyMsg = reply.ImageMsg(toUser,fromUser,mediaId)

return replyMsg.send()

if isinstance(recMsg,receive.EventMsg):

if recMsg.Event == 'CLICK':

if recMsg.Eventkey == 'mpGuide':

content = u"編寫中按脚,尚未完成".encode('utf-8')

replyMsg = reply.TextMsg(toUser,fromUser,content)

return replyMsg.send()

print "暫且不處理“

return reply.Msg.send()

except Exception, Argment:

return Argment

2)vim receive.py(修改)

# -*- coding: utf-8 -*-

# filename:receive.py

import xml.etree.ElementTree as ET

def parse_xml(web_data):

if len(web_data) == 0:

return None

xmlData = ET.fromstring(web_data)

msg_type = xmlData.find('MsgType').text

if msg_type == 'event':

event_type == 'CLICK':

return Click(xmlData)

#elif event_type in ('subscribe', 'unsubscribe'):

#return Subscribe(xmlData)

#elif event_type == 'VIEW':

#return View(xmlData)

#elif event_type == 'LOCATION':

#return LocationEvent(xmlData)

#elif event_type == 'SCAN':

#return Scan(xmlData)

elif msg_type == 'image':

return ImageMsg(xmlData)

class EventMsg(object):

def _init_(self,xmlData):

self.ToUserName = xmlData.find('ToUserName').text

self.FromUserName = xmlData.find('FromUserName').text

self.CreateTime = xmlData.find('CreateTime').text

self.MsgType = xmlData.find('MsgType').text

self.Event = xmlData.find('Event').text

class Click(EventMsg):

def _init_(self, xmlData):

EventMsg._init_(self,xmlData)

self.Eventkey = xmlData.find('EventKey').text

7.3體驗(yàn)

編譯好代碼后,重新啟動(dòng)服務(wù)歉糜,(sudo python main.py 80),view類型乘寒、meidia_id類型的本身就很容易實(shí)現(xiàn),現(xiàn)在重點(diǎn)看一下click類型的菜單按鈕匪补。

微信掃碼成為公眾號(hào)的粉絲伞辛,點(diǎn)擊菜單按鈕“開發(fā)指引”。

查看后臺(tái)日志夯缺,發(fā)現(xiàn)接收到一條xml蚤氏,如截圖:



公眾號(hào)的后臺(tái)代碼設(shè)置對(duì)該事件的處理是回復(fù)一條內(nèi)容為“編寫之中”的文本消息,因此公眾號(hào)發(fā)送了一條文本消息給我踊兜,如圖:



好啦竿滨,到此,目標(biāo)已實(shí)現(xiàn)捏境。對(duì)于自定義菜單其他類型于游,均同理可操作。

8 關(guān)于反饋問題

是程序肯定有bug垫言,所以在使用開放平臺(tái)過程中贰剥,肯定會(huì)遇見各種各樣的問題,可能是自己的坑筷频,也可能是微信團(tuán)隊(duì)的鍋蚌成。當(dāng)自己查自己代碼千千遍,依舊沒有發(fā)現(xiàn)問題時(shí)候凛捏,可以通過騰訊客服等等渠道担忧,請(qǐng)求微信團(tuán)隊(duì)的幫助。如何高效快速的得到幫助呢坯癣?下面強(qiáng)調(diào)三個(gè)要點(diǎn):

1)精明扼要的描述清楚場(chǎng)景以及遇見問題瓶盛,描述過程中盡可能使用wiki上的名稱,譬如自定義菜單示罗,素材管理等專有名詞蓬网,不然開發(fā)根本不知道你在說什么。

2)提供賬號(hào)信息:AppID(登錄公眾平臺(tái)官網(wǎng)->基本配置)鹉勒,若牽扯粉絲提供粉絲的OpenID帆锋。

3)提供bug的發(fā)生時(shí)間,至少要以小時(shí)為單位(年-月-日-小時(shí))禽额,當(dāng)然越具體越容易查明問題锯厢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末皮官,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子实辑,更是在濱河造成了極大的恐慌捺氢,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剪撬,死亡現(xiàn)場(chǎng)離奇詭異摄乒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)残黑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門馍佑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梨水,你說我怎么就攤上這事拭荤。” “怎么了疫诽?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵舅世,是天一觀的道長。 經(jīng)常有香客問我奇徒,道長雏亚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任摩钙,我火速辦了婚禮罢低,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腺律。我一直安慰自己,他們只是感情好宜肉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布匀钧。 她就那樣靜靜地躺著,像睡著了一般谬返。 火紅的嫁衣襯著肌膚如雪之斯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天遣铝,我揣著相機(jī)與錄音佑刷,去河邊找鬼。 笑死酿炸,一個(gè)胖子當(dāng)著我的面吹牛瘫絮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播填硕,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼麦萤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鹿鳖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起壮莹,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤翅帜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后命满,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涝滴,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年胶台,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歼疮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡概作,死狀恐怖腋妙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讯榕,我是刑警寧澤骤素,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站愚屁,受9級(jí)特大地震影響济竹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霎槐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一送浊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丘跌,春花似錦袭景、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惰帽。三九已至,卻和暖如春与殃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碍现。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工幅疼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昼接。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓爽篷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慢睡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狼忱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容