# 1. Getting Started with Official Account Developer Mode
The Weixin Official Accounts Platform technical documentation are designed to guide developers through the use of APIs, but are often quite inaccessible to novice developers for their obscurity.
To make it easier for developers to get familiar with the developer mode in Weixin Open Platform, we provide Developer Guide that describes basic features of Weixin Open Platform.
If you have experience in Weixin Official Accounts Platform development, skip this document. For any questions on existing APIs, please directly contact our Customer Service personnel or report to us via Weixin.
The following will help you get started with Official Account development quickly, with the sample code provided for your reference.
# 1.1 Applying for a Server
Take the Tencent Cloud server as an example: Purchase a Tencent Cloud server
# 1.2 Building Services
Take web.py (a web framework for Python) and the Tencent Cloud server as an example:
- Install/Update the software you need
Install Python2.7 or above
Install web.py
Install libxml2, libxslt and lxml python
- Edit the code. For information on Python syntax, refer to the official documentation for 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 handle view"
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
If "socket.error: No socket could be created" occurs, this may be because Port 80 is occupied or you have no permission. In this case, troubleshoot it on your own. If other error messages occur, learn the webpy framework and the command "sudo python main.py 80" in the official documentation for web.py.
Enter the URL: http://public network IP/wx (the public network IP can be found at the purchase completion page at the Tencent Cloud Website.) Then, a simple web app is created, as shown below:
# 1.3 Applying for an Official Account
Click here to apply for an Official Account.
After the account is activated via email, select the appropriate Official Account type. API permissions vary depending on Official Account types. For details, see Official Account API Permissions. Provide the certificate and other information required for your Service Account and OA Company Account right away or at the next login.
# 1.4 Basic Configuration for Developers
- Log in to the Official Accounts Platform and find Basic Configuration.
- Enter the configuration.
Enter the URL: http://public network IP/wx (the public network IP can be found at the purchase completion page at the Tencent Cloud Website.) Always enter 80 in the HTTP port number.
Token: Different from access_token mentioned in the Official Accounts Platform API documentation, this token is autonomous set and only used for developer server verification.
- Before submission, complete code logic, change the original main.py file, and add handle.py. Otherwise, token verification will fail.
a) vim main.py
# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle
urls = (
'/wx', 'Handle',
)
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
b) vim handle.py
Add the logic flow chart first.
# -*- coding: utf-8 -*-
# filename: handle.py
import hashlib
import 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" #Enter the value in **Basic Configuration ** at Official Accounts Platform.
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
map(sha1.update, list)
hashcode = sha1.hexdigest()
print "handle/GET func: hashcode, signature: ", hashcode, signature
if hashcode == signature:
return echostr
else:
return ""
except Exception, Argument:
return Argument
- After the restart is successful (python main.py 80), click the Submit button. If the prompt "token verification failed" appears, check the code or network link. If the token is successfully verified, you will be redirected back to the Basic Configuration page. Click Start.
# 1.5 Putting Important Things First
This will be explained in two examples.
Example 1: Automated response
Example 2: Chatting with images
Following the examples are descriptions to basic features, including media asset management, custom menu and broadcast. All the sample code is for a concise explanation of the issue and to avoid code complication.
To build a secure, stable and efficient Official Account, the recommended reference framework is as follows:
The framework includes the business logic processing server, the API-Proxy servers for interfacing with Weixin APIs, and the unique AccessToken central control server.
- AccessToken central control server:
Tasks: Provide active and passive refresh mechanisms to refresh and store accessToken (add an concurrency lock to avoid concurrent refresh) and provide valid accessToken to business logic.
Advantages: Prevent the business logic server from getting access_token concurrently, avoid overlay between AccessTokens, and improve business feature stability.
- API-Proxy server:
Tasks: Interface with Weixin APIs. Different servers interface with different business logics. Set limits to API calling frequency and permissions.
Advantages: If an API-proxy server does not work, other servers can continue providing services, improving stability and security
while avoiding direct exposure of internal APIs and preventing malicious attacks.
# 2 Automated Response
Purposes:
Understand the meaning of passive messaging
Understand the message receiving and sending mechanism
Expected feature:
When a follower sends a text message to an Official Account, the Official Account automatically replies to the follower with a text message, without manual operation on the Official Accounts Platform.
# 2.1 Receiving Text Messages
These messages refer to text messages sent by followers to Official Accounts. For more information, see Receiving Common Messages.
When a follower sends a text message of "Hello World" to an Official Account, the following xml sent by the Official Accounts Platform is received at the developer backend: (ToUserName and FromUserName are hidden.)
<xml>
<ToUserName><![CDATA[Official Account]]></ToUserName>
<FromUserName><![CDATA[Follower's Account]]></FromUserName>
<CreateTime>1460537339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[Hello World]]></Content>
<MsgId>6272960105994287618</MsgId>
</xml>
Explanation:
createTime: The time when the follower sent the message. This time is recorded by the Weixin Official Accounts Platform.
text: Marks that this xml is a text message. It is used for distinguishing the message type.
Hello World: The content sent by the follower to the Official Account.
MsgId: A tag value automatically generated at the Weixin backend system to record and recognize the message on the Official Accounts Platform.
# 2.2 Passive Reply Text Messages
These messages refer to text messages sent by Official Accounts to followers. For more information, see Passively Replying to User Messages.
Notes:
Different from the "Customer Service Messaging" API, passively replying to messages means sending passive response messages.
It is not an API, but a reply to the message sent by the Weixin server.
If you don't want to or cannot reply within 5 seconds after receiving messages sent by a follower, reply with the "success" string (detailed below).
The "Customer Service" API can be called whenever certain conditions are met.
If the Official Account wants to reply to the follower with "test", the developer sends the following xml to the Official Accounts Platform:
<xml>
<ToUserName><![CDATA[Follower's Account]]></ToUserName>
<FromUserName><![CDATA[Official Account]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>
Notes:
Enter ToUserName (recipient) and FromUserName (sender) according to the actual situation.
createtime is only used to mark the time when the developer responds to the message. The message sent by the Weixin backend is not subject to this field.
text: Marks that the sent message is of text type, rather than image or voice type.
'\n' is used to wrap text to the next line.
# 2.3 Reply with "success"
As stated at the beginning of the official API documentation on the Official Accounts Platform, if the server can't guarantee to process the reply within five seconds, it must reply with "success" or "" (empty string). Otherwise, the Weixin backend will initiate three retry attempts.
This is to ensure all messages sent by followers are received by developers. If the developer does not reply, Weixin backend cannot identify whether the developer has received the message and will initiate retry attempts.
Let's try not to reply after receiving the message. As can be seen in the following log screenshot, the Weixin backend initiated three retry operations:
If the developer does not reply after three retries, a message of "This Official Account is temporarily unavailable. Try again later." will appear on the follower's chat interface.
If the developer replies with "success", the Weixin backend can confirm that the developer has received the follower's message and will not push any exception prompts. So remember to reply with "success".
# 2.4 Workflow
# 2.5 Editing the Code
Keep the main.py file unchanged, add code to the handle.py file, and add new files receive.py and reply.py.
- 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
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
content = "test"
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
else:
print "Do not process"
return "success"
except Exception, Argment:
return Argment
- 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 == '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 __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.PicUrl = xmlData.find('PicUrl').text
self.MediaId = xmlData.find('MediaId').text
- 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)
After editing the code, restart the program sudo python main.py 80.
# 2.6 Online Testing
The Weixin Official Accounts Platform provides an online testing platform for developers to test the code logic in simulated environment. When testing, pay attention to the differences between this "Passive Reply" API and the "Customer Service" API described in "2.2 Passive Reply Text Messages".
The purpose of online testing is to test whether the developer code logic is incorrect and meets expectations. Even if the test is successful, the content will not be sent to the followers. So you can test it at will.
Test results:
- If the result is "Request Failed", it indicates that the code is incorrect. Check the code logic.
- If the result is "Request succeeded", check whether the code meets expectations based on the returned result.
# 2.7 Testing on Physical Devices
Use your mobile to scan the QR code of your Official Account via Weixin and be your Official Account's first follower. The location of the Official Account's QR code is shown as follows:
The test is as shown below:
# 3. Chatting with Images
Purposes:
Work with Media Asset Management
Here we use the text message and the image message to describe how this feature works. This works similar to the voice message, the video message, and the location message.
Expected feature:
When receiving an image message sent by a follower, the Official Account immediately replies to the follower with the same image.
# 3.1 Receiving Image Messages
These messages refer to image messages sent by followers to Official Accounts. For more information, see Message Management/Receiving Messages - Receiving Common Messages/Image Messages. Specifically, when a follower sends an image message to an Official Account, the following xml is received at the Official Account developer backend:
<xml>
<ToUserName><![CDATA[Official Account]]></ToUserName>
<FromUserName><![CDATA[Follower's Account]]></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>
Notes:
PicUrl: This parameter is the URL of the image message sent by the follower and is automatically generated in the Weixin system. This URL can be opened in a browser to view the image.
MediaId: It is an ID generated in the Weixin system and is used to mark the image. For more information, refer to Media Asset Management/Getting Temporary Assets.
# 3.2 Passive Reply Image Messages
These messages refer to image messages sent by Official Accounts to followers. For more information, see Message Management/Sending Messages - Passively Replying to User Messages/Image Messages.
Notes:
Different from the "Customer Service Messaging" API, passively replying to messages means sending passive response messages.
It is not an API, but a reply to the message sent by the Weixin server.
If you don't want to or cannot reply within 5 seconds after receiving messages sent by a follower, reply with the "success" string (detailed below).
The "Customer Service" API can be called whenever certain conditions are met.
The developer sends the following xml to the Weixin backend:
<xml>
<ToUserName><![CDATA[Follower's Account]]></ToUserName>
<FromUserName><![CDATA[Official Account]]></FromUserName>
<CreateTime>1460536576</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[gyci5oxxxxxxv3cOL]]></MediaId>
</Image>
</xml>
This MediaId is the same as the MediaId of the image sent by the follower. That's why the follower receives an image identical to theirs.
What if the Official Account wants to send a different image to the follower?
Add assets. Refer to Adding Temporary Assets or Adding Permanent Assets.
Get MediaID. Refer to Getting Temporary Assets MediaID or Getting Permanent Assets MediaID.
# 3.3 Workflow
# 3.4 Editing the Code
This section only displays the code different with "2.5 Editing the Code". For other similar code in online testing, testing on physical devices, and replying with an empty string, see "2. Automated Response". 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 #Print logs at the backend
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()
else:
return reply.Msg().send()
else:
print "Do not process"
return reply.Msg().send()
except Exception, Argment:
return Argment
# 4 AccessToken
For the meaning of AccessToken, refer to the description in the Official Accounts Development Guide.
# 4.1 Viewing AppID and AppSecret
You can view AppID and AppSecret on the Official Accounts Platform. If you do not click "Reset" for AppSecret, its value does not change.
# 4.2 Getting AccessToken
# 4.2.1 Ad-hoc methods
To try out other APIs first, you can get AccessToken via Online Testing or a browser.
# 4.2.2 Via APIs
For more information, see Official Accounts Development Guide.
Notes:
A third party must have a central control server to get and refresh access_token.
Getting access_token concurrently will cause overlap between AccessTokens and affect some service features.
# 4.3 Editing the Code
Note that the following code is just used to explain how to get AccessToken via APIs. It is not recommended in practice. For Official Accounts with heavy business, use a central control server to uniformly get 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 Temporary Assets
Official Accounts often require temporary assets. For example, API calls, particularly such operations as sending messages, as well as obtaining and calling for multimedia files and multimedia messages are performed via MediaID. When chatting with images, after a follower sends an image message to an Official Account, some temporary assets are generated.
Each Official Account has a limited number of permanent assets. When some assets are only needed for a certain period of time, they can use temporary assets. These assets are not permanently stored on the Weixin Official Accounts Platform, so you cannot find them in Media Asset Management at the website of the Official Accounts Platform, but you can work with them via APIs.
For other details, refer to the documentation on the Official Accounts Platform website.
# 5.1 Creating Temporary Assets
For more information about the API, see API documentation on the Official Accounts Platform. It describes how to upload assets as temporary assets for use by other APIs.
After vim media.py is written, run media.py directly to upload the temporary assets.
# -*- 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()
#Upload an image
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" #Enter according to the actual situation
mediaType = "image"
myMedia.upload(accessToken, filePath, mediaType)
# 5.2 Getting Temporary Assets MediaID
We do not provide a specific API for querying the temporary assets MediaID. To query MediaID, use the following two methods:
If the API for processing the temporary assets is called successfully, extract the MediaID from the returned JSON data.
If the temporary assets are used in follower interaction, extract the MediaID from the xml data.
# 5.3 Downloading Temporary Assets
# 5.3.1 Manual operation
For information on how developers save images sent by followers, see the documentation for the "Get Temporary Assets" API. To make it easy to understand, here we use a simple method to get the assets via a browser. Enter a URL in a browser:
https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID (Replace with your URL)
For information on ACCESS_TOKEN, see "4. AccessToken".
For information on MEDIA_ID, see MediaID in the xml data of Chatting with Images/Receiving Image Messages.
As long as your data is correct, the image will be downloaded to the local, as shown below:
# 5.3.2 Via APIs
Now you understand the API features. Let's see how to edit the code. vim media.py
# -*- coding: utf-8 -*-
# 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)
urlResp = 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() #Convert the assets to binary data
mediaFile = file("test_media.jpg", "wb")
mediaFile.write(buffer)
print "get successful"
if __name__ == '__main__':
myMedia = Media()
accessToken = Basic().get_access_token()
mediaId = "2ZsPnDj9XIQlGfws31MUfR5Iuz-rcn7F6LkX3NRCsw7nDpg2268e-dbGB67WWM-N"
myMedia.get(accessToken, mediaId)
Run media.py directly to download the desired assets. The article type assets are displayed in JSON data segment.
# 6. Permanent Assets
# 6.1 How to Create Permanent Assets
# 6.1.1 Manual operation
You can add assets in Media Asset Management on the Official Accounts Platform, which are distinguished by MediaID. MediaID is not the asset name, which can only be queried via the API. However, the asset name is displayed on the Official Accounts Platform, as shown below:
# 6.1.2 Via APIs
"Add Permanent Assets" API (see the relevant documentation): This is similar to the operation of adding temporary assets, except that different URLs are used. To make it as simple as possible, here we only describe how to use the "Add Permanent Articles" API. To add other types of assets, refer to the code of adding new temporary assets. vim material.py
# -*- coding: utf-8 -*-
# filename: material.py
import urllib2
import json
from basic import Basic
class Material(object):
#Upload an article
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 src=\"\" alt=\"\" data-width=\"null\" data-ratio=\"NaN\"><br /><img src=\"\" alt=\"\" data-width=\"null\" data-ratio=\"NaN\"><br /></p>",
"content_source_url": "",
}
]
})
#news is of dict type and can be modified using the following method:
#news['articles'][0]['title'] = u"test".encode('utf-8')
#print news['articles'][0]['title']
news = json.dumps(news, ensure_ascii=False)
myMaterial.add_news(accessToken, news)
# 6.2 Getting Permanent Assets MediaID
You can save MediaID when adding assets via the API Add Permanent Assets (see the relevant documentation).
You can also get MediaID by getting the permanent assets list (detailed below).
# 6.3 Getting Permanent Assets List
Click here to get the assets list. Note: This API can only get assets information in batches rather than getting all the assets information at a time. This may help you understand the meaning of the offset field. 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()
#Upload
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()
#Download
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 = urllib2.urlopen(postUrl, postData)
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() # Convert the assets to binary data
mediaFile = file("test_media.jpg", "wb")
mediaFile.write(buffer)
print "get successful"
#Delete
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()
#Get the assets list
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 = urllib2.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 Deleting Permanent Assets
To delete the image 20160102.jpg, you can directly do this on the official website or use the Delete Permanent Assets API.
First, use the method mentioned in the previous section to get the MediaID of the image. The code is identical to the section of API Material().delete().
After the API is successfully called, you will not find the deleted image in Media Asset Management on the Official Accounts Platform.
# 7 Custom Menu
For the meaning of the custom menu, refer to Creating a Custom Menu.
This section only introduces three types of menu buttons: click, view, and media_id. For other menu buttons, see the Official Accounts Platform API documentation.
# 7.1 Creating Menu Interface
- Edit the code based on the JSON data from the Official Accounts Platform API documentation. For information on media_id, see "6. Permanent Assets". 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()
#"Get Custom Menu Configuration" API
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": "Development Guide",
"key": "mpGuide"
},
{
"name": "Official Accounts Platform",
"sub_button":
[
{
"type": "view",
"name": "Release Note",
"url": "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1418702138&token=&lang=zh_CN"
},
{
"type": "view",
"name": "API Permissions",
"url": "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1418702138&token=&lang=zh_CN"
},
{
"type": "view",
"name": "Error Codes",
"url": "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234&token=&lang=zh_CN"
}
]
},
{
"type": "media_id",
"name": "Travel",
"media_id": "z2zOokJvlzCXXNhSjF46gdx6rSghwX2xOD5GUV9nbX4"
}
]
}
"""
accessToken = Basic().get_access_token()
#myMenu.delete(accessToken)
myMenu.create(postJson, accessToken)
Run python menu.py on the Tencent Cloud server.
View the created menu interface
You can view the new menu interface after re-following the Official Account. If you do not re-follow it, the interface will also be updated, but with a latency.
(For PC) Click on the submenu Release Note (view), and then the relevant page appears, as shown below:
Click on Travel (media_id), and then an article appears on the Official Account, as shown below:
Click on Development Guide, and then a message of "This Official Account is temporarily unavailable" appears.
# 7.2 Improving Menu Features
According to Official Accounts Platform Custom Menu and Custom Menu Event Push, when you click the "click" type button, Weixin backend will push an event type xml to the developer.
Developers should improve the backend code logic for the "click" type button to add the code for pushing custom menu events.
# 7.2.1 Workflow
# 7.2.2 Edit the Code
- vim handle.py (editing)
# -*- 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 #Print logs at the backend
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"Coming soon".encode('utf-8')
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
print "Do not process"
return reply.Msg().send()
except Exception, Argment:
return Argment
- vim receive.py (editing)
# -*- 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 = xmlData.find('Event').text
if 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 == 'text':
return TextMsg(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 Test
After the code is edited, run sudo python main.py 80 to re-enable the service. "view" and "media-id" type buttons are easy to implement. Now let's see how to implement "click" type menu buttons.
Scan the QR code via Weixin to follow the Official Account. Click the menu button Development Guide.
You can find an xml in the backend log, as shown below:
According to the preset backend code for the event, a message of "Coming soon" is returned, so the Official Account sends the following text message:
Now you've created the custom menu. This process also works to other types of custom menus.
# 8 Feedback
Bugs may occur when you use the Open Platform. This may be caused by you or Weixin Team. When you confirm that your code is correct, contact Tencent customer service or Weixin Team for help. To get help efficiently and rapidly, keep in mind the following three points:
Briefly describe your problem and where it occurs. Use terms in the Official Accounts Platform API documentation, such as custom menu and Media Asset Management, so that our staff know exactly what you are talking about.
Provide your account information such as AppID. You can find it in Basic Configuration on the Official Accounts Platform. If a follower is involved in the problem, the follower's OpenID is also required.
Provide the time when the bug occurred, at least in YYYY-MM-DD-HH. If you can provide a more exact time, it will be much easier for us to troubleshoot the bug.