微信公众号定制客户化菜单

wechat oap message service

上一篇文章中介绍了如何集成微信公众号平台和第三方服务,本篇文章将以此为基础,进一步介绍微信的接口服务,以及如何通过使用其中的“菜单接口”来对微信的公众号定制客户化菜单。

一、测试号申请及配置

刚申请的微信公众号的接口权限有限,可以使用的微信服务非常少(只包含少数的基础服务),如果要增加接口的使用权限,需要对公众号做认证,目前未开放认证,只能通过腾讯方邀请来做认证。所以为了说明如何定制公众号用户菜单,可以申请一个“测试号”,测试号可以有条件地使用微信所有的接口服务(调用次数有限制)。

测试号申请是在微信公众平台后台管理中操作,在左侧菜单里选择“开发者工具”,然后选择“公众平台测试账号”,填写接口配置信息,这个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()

发表评论

邮箱地址不会被公开。 必填项已用*标注