一般来说,想给微信公众号增加更多的功能,需要有一台服务器,来进行公众号后台服务的搭建。那么在 Serverless 架构下,是否有更简便的方法来实现这么一个公众号后台呢?我们试试?
初步搭建
Serverless 原生开发
首先我们当然要有一个微信公众号!
接下来,我们要为我们的函数计算服务申请固定 IP:
点击白名单之后,我们可以填写表单,完成固定公网出口 IP 的申请。
接下来进行代码开发。
- 将函数绑定到公众号后台,参考文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html 我们可以先在函数中按照文档完成一个基本的鉴定功能:
def checkSignature(param):
'''
文档地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
:param param:
:return:
'''
signature = param['signature']
timestamp = param['timestamp']
nonce = param["nonce"]
tmparr = [wxtoken, timestamp, nonce]
tmparr.sort()
tmpstr = ''.join(tmparr)
tmpstr = hashlib.sha1(tmpstr.encode("utf-8")).hexdigest()
return tmpstr == signature
再定义一个基本的回复方法:
def response(body, status=200):
return {
"isBase64Encoded": False,
"statusCode": status,
"headers": {"Content-Type": "text/html"},
"body": body
}
然后在函数入口处:
def main_handler(event, context):
if 'echostr' in event['queryString']: # 接入时的校验
return response(event['queryString']['echostr'] if checkSignature(event['queryString']) else False)
配置我们 Yaml:
# serverless.yml
Weixin_GoServerless:
component: "@serverless/tencent-scf"
inputs:
name: Weixin_GoServerless
codeUri: ./Admin
handler: index.main_handler
runtime: Python3.6
region: ap-shanghai
description: 微信公众号后台服务器配置
memorySize: 128
timeout: 20
environment:
variables:
wxtoken: 自定义一个字符串
appid: 暂时不写
secret: 暂时不写
events:
- apigw:
name: Weixin_GoServerless
parameters:
protocols:
- https
environment: release
endpoints:
- path: /
method: ANY
function:
isIntegratedResponse: TRUE
执行代码,完成部署:
接下来在公众号后台,选择基本配置:
选择修改配置:
这里要注意:
URL,写我们刚才部署完成返回给我们的地址,并且在最后加一个 /
Token,写我们 Yaml 中的 wxtoken,两个地方要保持一样的字符串
EncodingAESKey,可以点击随机生成
消息加密方法可以选择明文
完成之后,我们可以点击提交:
看到提交成功,就说明我们已经完成了第一步骤的绑定。接下来,我们到函数的后台:
打开固定出口 IP,看到 IP 地址之后,复制 IP 地址:
点击查看->修改,并将 IP 地址复制粘贴进来,保存。
同时我们查看开发者 ID 和密码:
并将这两个内容复制粘贴,放到我们环境变量中:
至此,我们完成了一个公众号后台服务的绑定。
为了方便之后的操作,先获取一下全局变量:
wxtoken = os.environ.get('wxtoken')
appid = os.environ.get('appid')
secret = os.environ.get('secret')
- 接下来对各个模块进行编辑(本文只提供部分简单基础的模块,更多功能实现可以参考微信公众号文档实现)
- 获取 AccessToken 模块:
def getAccessToken():
'''
文档地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
正常返回:{"access_token":"ACCESS_TOKEN","expires_in":7200}
异常返回:{"errcode":40013,"errmsg":"invalid appid"}
:return:
'''
url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" % (appid, secret)
accessToken = json.loads(urllib.request.urlopen(url).read().decode("utf-8"))
print(accessToken)
return None if "errcode" in accessToken else accessToken["access_token"]
- 创建自定义菜单模块:
def setMenu(menu):
'''
文档地址:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html
正确返回:{"errcode":0,"errmsg":"ok"}
异常返回:{"errcode":40018,"errmsg":"invalid button name size"}
:return:
'''
accessToken = getAccessToken()
if not accessToken:
return "Get Access Token Error"
url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s" % accessToken
postData = urllib.parse.urlencode(menu).encode("utf-8")
requestAttr = urllib.request.Request(url=url, data=postData)
responseAttr = urllib.request.urlopen(requestAttr)
responseData = json.loads(responseAttr.read())
return responseData['errmsg'] if "errcode" in responseData else "success"
- 常见消息回复模块:
def textXML(body, event):
'''
:param body: {"msg": "test"}
msg: 必填,回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)
:param event:
:return:
'''
return """<xml><ToUserName><![CDATA[{toUser}]]></ToUserName>
<FromUserName><![CDATA[{fromUser}]]></FromUserName>
<CreateTime>{time}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{msg}]]></Content></xml>""".format(toUser=event["FromUserName"],
fromUser=event["ToUserName"],
time=int(time.time()),
msg=body["msg"])
def pictureXML(body, event):
'''
:param body: {"media_id": 123}
media_id: 必填,通过素