在部署好一个微信公众号时,用户通过微信客户端向公众号发送文本消息并请求返回,这一过程实际上分为四个阶段:
1.用户向微信服务器发出请求
2.微信服务器向云服务器转发请求
3.云服务器接收请求并返回给云服务器响应消息
4.微信服务器再返回给用户回复消息
开发者环境是指我们开发微信公众号的环境,与云服务器的运行环境相对应。微信官方为开发者提供了开发者文档,里面规定了开发微信公众号所使用的端口号(只能用80(http)和443(https)这两个,实际上微信公众号只支持80端口)。
按照开发者文档,我们首先需要在开发者环境下搭建开发环境:
nginx作为高性能的HTTP和反向代理web服务器,负责将每个用户账号从80端口跑到固定的端口,如从8001开始,8002,8003…以此类推。注意运行在哪个端口,微信服务器配置的URL对应的就是wechatXXXX。
首先在云服务器上安装nginx:
sudo apt-get install nginx
接下来在云服务器上打开/etc/nginx/sites-available, 使用vim编辑该目录下的default文件:
sudo vim /etc/nginx/sites-available/default
找到文件中的server部分,将
location / {
XXXXX
}
这一部分内容改为:
location /wechat8000 {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:8000;
}
修改后的default文件(去掉部分注释):
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location /wechat8000 {undefined
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:8000;
}
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}
我们需要在后端服务器上实现一个/wechat80XX的视图,基于这个视图就可以实现微信服务器和后端云服务器的通信:
接下来向服务器上传一个wechat.py文件,用于向微信服务器建立连接。上传方式可以是ftp,也可以是ssh远程传输(效果一样)。
wechat.py(记得修改token、填写APPID和APPSECRET):
# coding:utf-8
from flask import Flask, request, abort, render_template
import hashlib
import xmltodict
import time
import urllib.request as urllib2
import json
# 微信的token令牌
WECHAT_TOKEN = '你的token'
WECHAT_APPID = '你的APPID'
WECHAT_APPSECRET = '你的APPSECRET'
app = Flask(__name__)
@app.route("/wechat8000", methods=["GET", "POST"])
def wechat():
"""对接微信公众号服务器"""
# 接收微信服务器发送的参数
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
# 校验参数
if not all([signature, timestamp, nonce]):
abort(400)
# 按照微信的流程进行计算签名
li = [WECHAT_TOKEN, timestamp, nonce]
# 排序
li.sort()
# 拼接字符串
tmp_str = "".join(li)
# 进行sha1加密,得到正确的签名值
sign = hashlib.sha1(tmp_str.encode("utf-8")).hexdigest()
# 将自己计算的签名值与请求的签名参数进行对比,如果相同,则证明请求来自微信
if signature != sign:
# 表示请求不是微信发的
abort(403)
else:
# 表示是微信发送的请求
if request.method == "GET":
# 表示是第一次接入微信服务器的验证
echostr = request.args.get("echostr")
if not echostr:
abort(400)
return echostr
elif request.method == "POST":
# 表示是微信服务器转发消息过来
xml_str = request.data
if not xml_str:
abort(400)
# 对xml字符串进行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict["xml"]
# 提取消息类型
msg_type = xml_dict.get("MsgType")
if msg_type == "text":
# 表示发送的是文本消息
# 构造返回值,经由微信服务器回复给用户的消息内容
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": xml_dict.get("Content")
}
}
else:
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "i love u"
}
}
# 将字典转换成xml字符串
resp_xml_str = xmltodict.unparse(resp_dict)
# 返回消息数据给微信服务器
return resp_xml_str
@app.route('/wechat8000/index')
def index():
"""让用户通过微信访问的网页页面视图"""
# 从微信服务器中拿取用户的资料数据
# 1. 拿取code参数
code = request.args.get("code")
if not code:
return "缺失code参数"
# 2. 向微信服务器发送http请求,获取access_token
url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" \
% (WECHAT_APPID, WECHAT_APPSECRET, code)
# 使用urllib2的urlopen方法发送请求
# 如果只传网址url参数,则默认使用http的get请求方式
response = urllib2.urlopen(url)
# 获取响应体数据,微信返回的json数据
json_str = response.read()
resp_dict = json.loads(json_str)
# 提取access_token
if "errcode" in resp_dict:
return "获取access_token失败"
access_token = resp_dict.get("access_token")
open_id = resp_dict.get("openid")
# 3. 向微信服务器发送http请求,获取用户的资料数据
url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" \
% (access_token, open_id)
response = urllib2.urlopen(url)
# 读取微信传回的json的响应体数据
user_json_str = response.read()
user_dict_data = json.loads(user_json_str)
if "errcode" in user_dict_data:
return "获取用户信息失败"
else:
# 将用户的资料数据填充到页面
return render_template("index.html", user=user_dict_data)
if __name__ == '__main__':
app.run(port=8000, debug=True)
通过xftp软件向服务器上传修改后的wechat.py文件:
云服务器上需要同步基于python的flask,这是一个轻量级web框架,先在服务器上安装flask:
pip install flask
查看端口号8000的开放情况(使用阿里云的朋友记得在阿里云的安全组配置上面开放80端口和8000端口的使用):
netstat -apn | grep 8000
进入虚拟环境:
conda activate paddle_env
前面编写写的GET和POST方法向微信服务器请求access_token,运行wechat.py文件,与微信服务器建立连接,获取accexx_token:
在URL输入框里的IP后面不用添加端口号,因为一方面微信公众号http协议只支持80端口,另一方面我们先让微信服务器把消息转发到云服务器的nginix上,然后再交给服务器,注意URL一定要填http而不是https,否则不成功:
提交后显示成功,然后点击启用:
服务器运行AI部署代码:
import paddlenlp
import utils
from utils import select_sum
from utils import post_process_sum
from flask import Flask, request, abort, render_template
import hashlib
import xmltodict
import time
import urllib.request as urllib2
import json
# 微信的token令牌
WECHAT_TOKEN = '你的token'
WECHAT_APPID = '你的APPID'
WECHAT_APPSECRET = '你的SECRET'
model = paddlenlp.transformers.UNIMOLMHeadModel.from_pretrained('data126898')
tokenizer = paddlenlp.transformers.UNIMOTokenizer.from_pretrained('unimo-text-1.0')
model.eval()
num_return_sequences = 8
app = Flask(__name__)
@app.route("/wechat8000", methods=["GET", "POST"])
def wechat():
"""对接微信公众号服务器"""
# 接收微信服务器发送的参数
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
# 校验参数
if not all([signature, timestamp, nonce]):
abort(400)
# 按照微信的流程进行计算签名
li = [WECHAT_TOKEN, timestamp, nonce]
# 排序
li.sort()
# 拼接字符串
tmp_str = "".join(li)
# 进行sha1加密,得到正确的签名值
sign = hashlib.sha1(tmp_str.encode("utf-8")).hexdigest()
# 将自己计算的签名值与请求的签名参数进行对比,如果相同,则证明请求来自微信
if signature != sign:
# 表示请求不是微信发的
abort(403)
else:
# 表示是微信发送的请求
if request.method == "GET":
# 表示是第一次接入微信服务器的验证
echostr = request.args.get("echostr")
if not echostr:
abort(400)
return echostr
elif request.method == "POST":
# 表示是微信服务器转发消息过来
xml_str = request.data
if not xml_str:
abort(400)
# 对xml字符串进行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict["xml"]
# 提取消息类型
msg_type = xml_dict.get("MsgType")
if msg_type == "text":
# 表示发送的是文本消息
# 构造返回值,经由微信服务器回复给用户的消息内容
source_text = xml_dict.get("Content")
# 由于训练时间较长,这里我们直接读训练好的模型
inputs = source_text
inputs_ids = tokenizer.gen_encode(inputs, return_tensors=True, add_start_token_for_decoding=True,
return_position_ids=True)
# 调用生成api并指定解码策略为beam_search
outputs, scores = model.generate(**inputs_ids, decode_strategy='beam_search', num_beams=8,
num_return_sequences=num_return_sequences)
# 调用生成api并指定解码策略为Sampling,不同策略的效果不同哦。
r = ''
for i in range(num_return_sequences):
r = r+'{} 上联: {} 下联:{}'.format(i, inputs, ''.join(post_process_sum(outputs[i].numpy(), tokenizer)[1]))+'\n'
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": r
}
}
else:
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "i love u"
}
}
# 将字典转换成xml字符串
resp_xml_str = xmltodict.unparse(resp_dict)
resp_xml_str = resp_xml_str+"我爱你"
# 返回消息数据给微信服务器
return resp_xml_str
@app.route('/wechat8000/index')
def index():
"""让用户通过微信访问的网页页面视图"""
# 从微信服务器中拿取用户的资料数据
# 1. 拿取code参数
code = request.args.get("code")
if not code:
return "缺失code参数"
# 2. 向微信服务器发送http请求,获取access_token
url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" \
% (WECHAT_APPID, WECHAT_APPSECRET, code)
# 使用urllib2的urlopen方法发送请求
# 如果只传网址url参数,则默认使用http的get请求方式
response = urllib2.urlopen(url)
# 获取响应体数据,微信返回的json数据
json_str = response.read()
resp_dict = json.loads(json_str)
# 提取access_token
if "errcode" in resp_dict:
return "获取access_token失败"
access_token = resp_dict.get("access_token")
open_id = resp_dict.get("openid")
# 3. 向微信服务器发送http请求,获取用户的资料数据
url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" \
% (access_token, open_id)
response = urllib2.urlopen(url)
# 读取微信传回的json的响应体数据
user_json_str = response.read()
user_dict_data = json.loads(user_json_str)
if "errcode" in user_dict_data:
return "获取用户信息失败"
else:
# 将用户的资料数据填充到页面
return render_template("index.html", user=user_dict_data)
if __name__ == '__main__':
app.run(port=8000, debug=True)