微信公众号定制客户化菜单
在上一篇文章中介绍了如何集成微信公众号平台和第三方服务,本篇文章将以此为基础,进一步介绍微信的接口服务,以及如何通过使用其中的“菜单接口”来对微信的公众号定制客户化菜单。
一、测试号申请及配置
刚申请的微信公众号的接口权限有限,可以使用的微信服务非常少(只包含少数的基础服务),如果要增加接口的使用权限,需要对公众号做认证,目前未开放认证,只能通过腾讯方邀请来做认证。所以为了说明如何定制公众号用户菜单,可以申请一个“测试号”,测试号可以有条件地使用微信所有的接口服务(调用次数有限制)。
测试号申请是在微信公众平台后台管理中操作,在左侧菜单里选择“开发者工具”,然后选择“公众平台测试账号”,填写接口配置信息,这个URL用来验证服务器以及提供第三方服务,可以参看这篇文章的说明。“JS接口安全域名”是用于微信 JSSDK 工具包的,当开发微信网页的时候通过该设置域名来使用JSSDK,需要注意的是该域名没有http|https前缀,名字可以是根域名或者路径。
测试号申请并配置完成后,就可以像一般的公众号那样关注了,扫描上面配置界面中的二维码。注意这个只是测试号,用来体验和测试微信公众平台的服务接口,不能像普通公众号那样使用。
二、Token(令牌)管理服务
有了测试号后,就可以调用微信菜单接口为这个测试号添加菜单了。在前一篇文章中简单提了一下 token(令牌),每次调用微信接口服务的时候需要提供这个 token。
获取 token 同样也需要访问微信的接口服务,拿到令牌后要保存一下(有2个小时的有效期),下次调用微信接口服务时就可以直接提取 token,这样在令牌有效期内无需再次从微信接口服务那申请。
从微信获取的 token需要提取、保存、过期再获取等管理服务,下面以 Redis 为例给出客户端的实现代码,Redis 服务器端的配置可参照这篇文章。
redisclient.py
from wechatpy.session.redisstorage import RedisStorage
from redis import Redis
class IRedis:
session_interface = None
def __init__(self):
redis_client = Redis.from_url('redis://127.0.0.1:6379/0')
IRedis.session_interface = RedisStorage(
redis_client,
prefix="wx_token"
)
redissession = IRedis().session_interface
通过上面的代码得到了关联 Redis 服务的一个客户端会话接口(redissession),后面调用微信接口服务的时候通过 wechatpy 这个微信工具程序来使用,wechatpy 也会自动完成 token 的保存、提取、过期再获取等管理工作。
三、添加公众号菜单
添加客户菜单是通过调用微信菜单接口服务来完成的,接口的详细说明请参见官网,下面给出一个具体的例子,其中的 “appID” 和 “appsecret” 参数可以在测试号的配置界面中查到,然后替换代码中相对应的字符串。
setupmenu.py
from wechatpy import WeChatClient
from redisclient import redissession
class Menu(object):
def __init__(self):
pass
def create(self):
client = WeChatClient("appID", "appsecret", session=redissession)
client.menu.create({
"button":[
{
"type":"click",
"name":"微信关注",
"key":"wx_qr_code"
},
{
"type":"click",
"name":"微博关注",
"key":"wb_qr_code"
},
{
"name":"更多精彩",
"sub_button":[
{
"type":"view",
"name":"博客",
"url":"https://www.kflyo.com/"
},
{
"type":"view",
"name":"微博",
"url":"https://weibo.com/kflyo"
},
{
"type":"view",
"name":"关于",
"url":"https://www.kflyo.com/about-me"
}
]
}
]
})
def delete(self):
client = WeChatClient("appsecret", "appsecret", session=redissession)
client.menu.delete()
上面的代码中主菜单用“button”定义,子菜单用“sub_button”定义,菜单项类型如果是“click”,点击后微信后台会给我们的服务器发送一条事件消息,通过响应这个事件来实现有关的业务功能,比如回复文本消息、图片消息、音频消息等给用户。下面给出一个响应 click 事件的例子。
handle.py
from utils import check_signature
import web
from wechatpy import parse_message
from wechatpy.replies import TextReply
from wechatpy.replies import ImageReply
from wechatpy.replies import ArticlesReply
from wechatpy import create_reply
class Handle(object):
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "欢迎来到客飞翱的公众号"
echostr = data.echostr
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
token = "验证服务器配置时定义的token"
isok = check_signature(token, signature, timestamp, nonce)
print ("handle/GET func: timestamp, signature: ", timestamp, signature)
if isok==1:
return echostr
else:
return ""
except Exception as Argument:
return Argument
def POST(self):
try:
data = web.data()
print ("Handle Post webdata is ", data)
msg = parse_message(data)
if msg.type == 'text' and msg.content == '微博':
articles = [
{
'title': msg.content,
'description': '关注客飞翱的微博',
'image': 'https://www.kflyo.com/wp-content/uploads/...',
'url': u'https://weibo.com/kflyo',
},
]
fast_reply = create_reply(articles, message=msg)
return fast_reply.render()
"""根据事件类型和菜单的key值分别做出响应,key值是在上面菜单创建时定义的"""
if msg.type == 'event' and msg.event == 'click':
if msg.key == 'wx_qr_code':
return ImageReply(media_id='...HgrvOuCo5AbRDo3fauRcUSSvhnyCO8g9Woi7uSas6q5o3qiTkRy7CQ...', message=msg).render()
elif msg.key == 'wb_qr_code':
return ImageReply(media_id='...hR2j455Xk0uEwDH-9kFUW2qBx-8kXA0OnKXLiNNmyZ7ykQ7ExVuNft...', message=msg).render()
reply = ArticlesReply(message=msg, articles=[
{
'title': u'欢迎来到客飞翱的公众号',
'description': u'旅行游记,信息技术,游戏攻略分享。输入“微博”访问和关注。',
'url': u'https://www.kflyo.com',
'image': 'https://www.kflyo.com/wp-content/uploads/...',
},
{
'title': u'微博',
'description': u'分享、感悟、美图',
'url': u'https://www.kflyo.com/kflyo',
'image': u'https://www.kflyo.com/wp-content/uploads/...',
},
])
"""
reply.add_article({
'title': u'微博',
'description': u'旅行感悟',
'url': u'https://weibo.com/kflyo',
})
"""
return reply.render()
except Exception as Argument:
return Argument
上面的代码中回复了图片消息,相应的图片使用的是永久素材的方式,需要先上传。到微信公众号管理后台的“开发者工具-在线接口调试工具:接口类型-技术支持,接口列表-多媒体文件上传接口”上传图片。上传成功后会返回图片的 media_id,就是上面代码中所使用的。其它如音频、视频等媒体类型也可以通过这个方法上传。
上面的方法是针对测试号使用永久素材,通常的公众号无需这样,在公众号后台有永久素材管理界面管理素材。
最后在主程序中调用菜单创建函数为公众号创建客户化菜单。
index.py
import sys
import web
from handle import Handle
from setupmenu import Menu
sys.path.append('/home/user/webpy')
menu = Menu()
menu.create()
urls = (
'/weixin', 'Handle',
)
app = web.application(urls, globals())
if __name__ == '__main__':
app.run()
application = app.wsgifunc()