天问

ChatGPT对接微信公众号教程

准备工作

1、注册公众帐户,订阅号或者服务号都可以,最好花300认证 http://mp.weixin.qq.com/

2、注册openai账户,获取key Account API Keys - OpenAI API

3、购买服务器,域名。

4、开发,测试,部署

公众号

登录公众账户获取appid,服务器地址(URL) ,令牌(Token) ,消息加解密密钥(EncodingAESKey)等参数。

服务器配置一个url,公众号通过这个链接发送post请求到服务器进行交互,后续部署部分将介绍。

开发

下面用Python简单创建一个项目,项目结构如下:

main.py 启动app

from app import create_app

app = create_app('default') # default 使用默认配置

if __name__ == '__main__':
    app.run()

其中create_app返回一个app对象:

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name]) # 读取配置
    config[config_name].init_app(app)

    # 初始化数据库 db 对象
    # db.init_app(app)

    from app.home import home as home_blueprint
    app.register_blueprint(home_blueprint)

    return app

这里我先不使用数据库,焦点关注于ChatGPT对接,这样就会启动一个Flask app,默认端口5000

views.py 设置路由

from . import home
from flask import Flask, render_template, url_for, redirect, flash, session, request
from werkzeug.security import generate_password_hash
from sqlalchemy import and_
from functools import wraps
from wechatpy.utils import check_signature
from wechatpy.exceptions import InvalidSignatureException
from wechatpy import parse_message
from wechatpy.replies import TextReply
import requests
from cacheout import LFUCache,Cache
import time
import hashlib

cache = Cache(maxsize=100, ttl=120, timer=time.time, default=None) 

@home.route("/", methods=["GET", "POST"])
def index():
    """
    首页
    """
    if (request.method == "GET"):
        signature = request.args.get('signature')
        timestamp = request.args.get('timestamp')
        nonce = request.args.get('nonce')
        echostr = request.args.get('echostr')
        token = "xx"
        
        # 微信后台验证
        try:
            check_signature(token, signature, timestamp, nonce)
        except InvalidSignatureException:
            # 处理异常情况或忽略
            return "校验失败"
        return echostr

    if (request.method == "POST"):
        # 收到微信服务器发送来的用户请求,公众号会重发3次,每次请求5s,超过就直接返回服务不可用

        xml_str = request.data
        msg = parse_message(xml_str)
        xml_str = request.data
        msg = parse_message(xml_str)
        # 1.目标用户信息
        target = msg.target
        source = msg.source
        msgType = msg.type
        msgCcontent = msg.content

        print(msgCcontent)
        # 判断是否相同请求,缓存请求
        s=hashlib.md5()
        s.update(msgCcontent.encode("utf-8"))
        res = s.hexdigest()

        reply = TextReply()
        reply.source = target
        reply.target = source
        if(res in cache):
            if(cache.get(res)=="" or cache.get(res) is None):
                reply.content = "OpenAI卖力运算中,15s后再来问我哦"
            else:
                reply.content=cache.get(res)
        else:
            cache.set(res,"")
            json_data = {
                'model': 'text-davinci-003',
                'prompt': msgCcontent,
                'max_tokens': 512,
                'temperature': 0.2,
                "top_p":1,
                "frequency_penalty":0,
                "presence_penalty":0
            }
            openaiKey=r"sk-xx"
            headers={
                'Content-Type': 'application/json',
                'Authorization': 'Bearer '+ openaiKey
            }
            response = requests.post('https://api.openai.com/v1/completions', headers=headers, json=json_data)
            reply.content = response.json()['choices'][0]['text'].strip()
            cache.set(res,reply.content)
        print(reply.content)
        # 包装成XML格式的数据
        xml = reply.render()
        return xml

这段代码比较多,下面详细介绍一下:

配置一个缓存,当然最好换成redis,设置缓存时间120s:

cache = Cache(maxsize=100, ttl=120, timer=time.time, default=None) 

当访问https://xx.com/ 的时候,进入下面index方法,其中有get,post请求:

@home.route("/", methods=["GET", "POST"])
def index():

get请求用来验证第一步公众账号填入的url,所以我们公众账号url只需要配置为 https://xx.com 即可:

if (request.method == "GET"):
    signature = request.args.get('signature')
    timestamp = request.args.get('timestamp')
    nonce = request.args.get('nonce')

POST接口用来实现公众号和服务器之间的通信,比如公众号发了一条文字消息,会调用POST方法,执行下面方法:

if (request.method == "POST"):
        xml_str = request.data
        msg = parse_message(xml_str)
        xml_str = request.data
        msg = parse_message(xml_str)

先解析公众号发来的数据,获取到之后掉用OpenAI接口,然后返回给用户。

但是由于微信公众号做了限制,接口最多请求5秒,否则中断,连发3次,共15秒,而OpenAI的接口一半都超过了5秒。必须用户主动发消息才能回应,怎么办了?

这里我们用到了缓存,服务器第一次接到请求,直接调用OpenAI接口,由于相应时间大于5秒,接口断开,但是服务器获取到OpenAI结果后,存入缓存。

接下来微信继续发一次请求,服务器接到第二次请求,在下一次请求的时候,使用缓存,判断一下是否之前收到过消息,如果没收到,那么就是第一次请求,如果已经收到,检测缓存是否已经保存了结果,没有的话,等待微信继续第三次发请求,最后从缓存中获取结果,给用户发消息。

if(res in cache):
            if(cache.get(res)=="" or cache.get(res) is None):
                reply.content = "OpenAI卖力运算中,15s后再来问我哦"
            else:
                reply.content=cache.get(res)
        else:
            cache.set(res,"")

其他的用户登录,会话管理,会员管理,拉新等这里就不再介绍了。

最后,我们创建一个Python环境,安装依赖,启动Flask App,暴露端口5000提供web服务:

virtualenv .venv

source .venv/bin/activate
pip install -r requirements.txt
python main.py

部署

当然可以选择Docker简单部署,或者直接配置云服务器。这里介绍一下服务器配置:

1、前端选择Apache反向代理,暴露80和443端口提供外部服务,请求转发到127.0.0.1:5000,也就是我们Flask app启动监听的端口。

<VirtualHost 1xx.x8.xx.xx>
    DocumentRoot "/home/twtech/www/xx.xx.xx"
    ServerName xx.xx.me
	
	<Directory "/home/xx">
		SetOutputFilter DEFLATE
		Options FollowSymLinks ExecCGI
		#Require all granted
		AllowOverride All
		Order allow,deny
		Allow from all
		DirectoryIndex index.html index.php
	</Directory>
	<Directory ~ ".*\.svn|.git|_svn/.*">
		Order allow,deny
		Deny from all
	</Directory>
	
	#反向代理
	ProxyRequests Off
	ProxyPass / http://127.0.0.1:5000/
	ProxyPassReverse / http://127.0.0.1:5000/
	<proxy  http://127.0.0.1:80>
		AllowOverride None
		Order Deny,Allow
		Allow from all
	</proxy>
	
</VirtualHost>

当然nginx配置类似,这里就不实验了。

App定制开发,欢迎找我们合作(wx: ab3255),谢谢。

博客地址:http://blog.yoqi.me/?p=17687
扫我捐助哦
喜欢 3

这篇文章有6条评论

  1. 秋枫萧瑟 2023/2/9 #1 [REPLY]

    你好,想请你帮我们搭建chatGPT接入微信公众号,请问怎么联系你呀?

    • lyq, lyq (作者) 回复 秋枫萧瑟 2023/2/11 [REPLY]

      文章下面有我们的邮箱和QQ,看到邮箱,我们会主动联系您的

  2. 1 2023/2/8 #2 [REPLY]

    文中的源码,是全的吗?

    • lyq, lyq (作者) 回复 1 2023/2/11 [REPLY]

      嗯,demo代码是全的。下面我将介绍一下通过 客服接口 来突破5s的限制,需要 认证的 服务号。

  3. reverie 2023/1/4 #3 [REPLY]

    您好,我想请问一下您之前开发的微信公众号禁用懒加载的插件问题,我安装插件的时候提示我清单文件丢失或不可读取,想问一下怎么解决呀。

    • lyq, lyq (作者) 回复 reverie 2023/1/4 [REPLY]

      有位小美女已经反馈过了,这个bug已经修复了。你可以查看最新的代码哦

发表评论