国内服务器 3 分钟将 ChatGPT 接入微信公众号(超详细)
最近很火的ChatGPT可以说已经满大街可见了,到处都有各种各样的体验地址,有收费的也有免费的,总之是五花八门、花里胡哨。
所以呢,最近我就在研究怎么才能方便快捷的体验到ChatGPT的强大功能,其中一个就是:把ChatGPT接入公众号。毕竟公众号是一种非常流行的社交媒体平台,可以用来提供服务、推广产品等。经过我的一番折腾,最后终于成功通过 Laf 实现了这个需求。

本文将详细介绍如何使用 Laf 云平台将 ChatGPT 接入微信公众号,实现智能问答、聊天机器人等功能。
1. 准备工作
首先需要注册一个 Laf 平台账号:https://laf.run
注册登录之后,点击新建,建立一个应用:

点击开发,进入应用开发界面:

然后输入 chatgpt 并回车进行搜索,选择第一个搜索结果,保存并重启:

重启之后,自定义依赖项中便出现了 chatgpt。

新建云函数
然后我们点击函数,函数列表右侧的加号,新增一个可以接入微信公众号的 ChatGPT 云函数:

云函数完整代码如下:
// 引入crypto和cloud模块
import * as crypto from 'crypto';
import cloud from '@lafjs/cloud';
const WAIT_MESSAGE = `处理中 ... \n\n请稍等3秒后发送【1】查看回复`
const NO_MESSAGE = `暂无内容,请稍后回复【1】再试`
const CLEAR_MESSAGE = ` 记忆已清除`
const HELP_MESSAGE = `ChatGPT 指令使用指南
| 关键字 | 功能 |
| 1 | 上一次问题的回复 |
| /clear | 清除上下文 |
| /help | 获取更多帮助 |
`
// 不支持的消息类型
const UNSUPPORTED_MESSAGE_TYPES = {
image: '暂不支持图片消息',
voice: '暂不支持语音消息',
video: '暂不支持视频消息',
music: '暂不支持音乐消息',
news: '暂不支持图文消息',
}
// 定义休眠函数
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
// 创建数据库连接并获取Message集合
const db = cloud.database();
const Message = db.collection('messages')
// 处理接收到的微信公众号消息
export async function main(event) {
const { signature, timestamp, nonce, echostr } = event.query;
const token = '123456';
// 验证消息是否合法,若不合法则返回错误信息
if (!verifySignature(signature, timestamp, nonce, token)) {
return 'Invalid signature';
}
// 如果是首次验证,则返回 echostr 给微信服务器
if (echostr) {
return echostr;
}
// 处理接收到的消息
const payload = event.body.xml;
// console.log("payload",payload)
// 文本消息
if (payload.msgtype[0] === 'text') {
const newMessage = {
msgid: payload.msgid[0],
question: payload.content[0].trim(),
username: payload.fromusername[0],
sessionId: payload.fromusername[0],
createdAt: Date.now()
}
// 修复请求响应超时问题:如果 5 秒内 AI 没有回复,则返回等待消息
const responseText = await Promise.race([
replyText(newMessage),
sleep(4000.0).then(() => WAIT_MESSAGE),
]);
return toXML(payload, responseText);
}
// 公众号事件
if (payload.msgtype[0] === 'event') {
// 公众号订阅
if (payload.event[0] === 'subscribe') {
return toXML(payload, HELP_MESSAGE);
}
}
// 暂不支持的消息类型
if (payload.MsgType in UNSUPPORTED_MESSAGE_TYPES) {
const responseText = UNSUPPORTED_MESSAGE_TYPES[payload.MsgType];
return toXML(payload, responseText);
}
return 'success'
}
// 处理文本回复消息
async function replyText(message) {
const { question, sessionId } = message;
console.log("replyText 执行了")
// 检查是否是重试操作,如果是重试操作,返回上一次的回复
if (question === '1') {
const lastMessage = await Message.where({
sessionId
}).orderBy("createdAt", "desc").get();
if (lastMessage.data[0]) {
return `${lastMessage.data[0].question}\n------------\n${lastMessage.data[0].answer}`;
}
return NO_MESSAGE;
}
// 获取上下文 id
const res = await Message.where({
sessionId
}).orderBy("createdAt", "desc").getOne();
const parentId = res?.data?.parentMessageId
const conId = res?.data?.conversationId
// 发送指令
if (question.startsWith('/')) {
return await processCommandText(message);
}
// 获取 OpenAI 回复内容
const { error, answer, parentMessageId, conversationId } = await getOpenAIReply(question, parentId, conId);
if (error) {
console.error(`sessionId: ${sessionId}; question: ${question}; error: ${error}`);
return error;
}
// 将消息保存到数据库中
const token = question.length + answer.length;
const result = await Message.add({ token, answer, parentMessageId, conversationId, ...message });
console.debug(`[save message] result: ${result}`);
return answer;
}
// 获取 OpenAI API 的回复
async function getOpenAIReply(question, parentId, conId) {
console.log("getOpenAIReply 执行了")
// 引入 ChatGPTUnofficialProxyAPI 模块
const { ChatGPTUnofficialProxyAPI } = await import('chatgpt')
// 创建 ChatGPTUnofficialProxyAPI 实例
const api = new ChatGPTUnofficialProxyAPI({
accessToken: cloud.env.ACCESSTOKEN,
apiReverseProxyUrl: "https://bypass.churchless.tech/api/conversation"
})
try {
// 如果有上下文 id,就带上
let res;
if (parentId && conId) {
res = await api.sendMessage(question, { conversationId: conId, parentMessageId: parentId })
} else {
res = await api.sendMessage(question)
}
// 返回 OpenAI 回复的内容及上下文 id
return { answer: res.text.replace("\n\n", ""), parentMessageId: res.parentMessageId, conversationId: res.conversationId }
} catch (e) {
console.log(e)
return {
error: "问题太难了 出错了. (uДu〃).",
}
}
}
// 校验微信服务器发送的消息是否合法
function verifySignature(signature, timestamp, nonce, token) {
const arr = [token, timestamp, nonce].sort();
const str = arr.join('');
const sha1 = crypto.createHash('sha1');
sha1.update(str);
return sha1.digest('hex') === signature;
}
// 返回组装 xml
function toXML(payload, content) {
const timestamp = Date.now();
const { tousername: fromUserName, fromusername: toUserName } = payload;
return `
<xml>
<ToUserName><![CDATA[${toUserName}]]></ToUserName>
<FromUserName><![CDATA[${fromUserName}]]></FromUserName>
<CreateTime>${timestamp}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>
`
}
async function processCommandText({ sessionId, question }) {
// 清理历史会话
if (question === '/clear') {
const res = await Message.where({ sessionId }).remove({ multi: true })
return CLEAR_MESSAGE;
} else {
return HELP_MESSAGE;
}
}
由于 OpenAI 的 API Key 需要充值才能用,所以我们选择剑走偏锋,直接使用 ChatGPT 网页版。但是国内环境无法访问 ChatGPT,所以我们需要一个 Proxy。不用担心,国外已经有热心小哥给我们提供了公共的 Proxy,我们只需要直接调用就好啦!

详情可参考 ChatGPTUnofficialProxyAPI 的使用文档。
只要你有 ChatGPT 账号,都可以使用这种方法,最的是,你可以直接在 Laf 的国内环境 https://laf.run 中使用!!!
核心函数:
getOpenAIReply函数通过引入ChatGPTUnofficialProxyAPI模块,创建实例并调用其方法,获取 ChatGPT 的回复内容。verifySignature函数用于校验微信服务器发送的消息是否合法。toXML函数将回复内容组装成XML格式。processCommandText函数用于处理指令,目前支持清除历史会话和获取帮助信息两个指令。
注意:
token要与微信公众号中设置一致。ACCESSTOKEN的获取方式:你需要先在浏览器中登录 ChatGPT 网页版,然后在浏览器中访问这个 URL:https://chat.openai.com/api/auth/session,浏览器会返回一个 JSON,里面包含了 accessToken 字段,将这个字段的值复制即可。
云函数写完之后就点击发布,左侧的接口地址要保存一下,稍后将在微信公众号中使用此地址。

配置微信公众号
这一步我们需要在微信公众号平台上配置开发者信息,并将服务器地址设置为部署好的云函数服务地址。步骤如下:
首先你需要有一个公众号,然后登录微信公众平台,点开左侧的「设置与开发」,点击「基本设置」,然后点击「服务器配置」,服务器配置那里点击修改配置:

将云函数服务地址复制到「服务器 URL」中,下边的 Token 与云函数代码中的 token 保持一致,下边的 EncodingAESKey 点击右侧随机生成就行,然后点击提交:

返回 token 校验成功后,点击「启用」即可。

现在你已经完成了所有必要的设置和配置,下面就可以直接进入微信公众号「Laf 开发者」后台与机器人进行交互啦!

ChatGPT 机器人可以回答用户提出的问题,并且可以根据用户提供的上下文进行回复。以下是一些指令和关键字,可以帮助您更好地使用 ChatGPT 机器人:
- 【1】:获取上一次问题的回复。
- /clear:清除上下文。
- /help:获取更多帮助。 除了以上指令和关键字外,你还可以根据自己的需求进行定制化开发,以满足用户的需求。

总结
通过以上步骤,您已经成功地将 ChatGPT 接入微信公众号,并使用 Laf 平台来部署 ChatGPT 云函数。这样,您就可以在公众号中为您的用户提供智能问答、聊天机器人等服务了,微信公众号自带的自动回复功能可以抛弃了。
当然,直接接入 ChatGPT 网页版肯定没有接入 API 稳定,如果你想通过 API 的方式接入,可以参考原作者的官方仓库:
最后,欢迎关注我们的公众号直接与 ChatGPT 对话吧!

国内服务器 3 分钟将 ChatGPT 接入微信公众号(超详细)的更多相关文章
- 如何用Azure Web App Services接入微信公众号
注:本文提到的代码示例下载地址>如何用Azure Web App Services接入微信公众号 如何用Azure Web App Services接入微信公众号 简介 此示例演示如何创建Azu ...
- [.NET] 使用 Senparc.Weixin 接入微信公众号开发:简单实现自动回复
使用 Senparc.Weixin 接入微信公众号开发:简单实现自动回复 目录 一.前提 二.基本配置信息简析 三.配置服务器地址(URL) 四.请求处理 一.前提 先申请微信公众号的授权,找到或配置 ...
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
- 网站如何接入微信公众号JSAPI支付PHP版
1.首先,我们要有一个微信公众号(分类类型有订阅号,服务号,企业号)我们的微信公众号一定是个服务号只有它才有微信支付接口.. 并且这个微信公众号一定要进行微信认证才能申请微信支付接口. 2.申请JSA ...
- [转载红鱼儿]delphi 实现微信开发(2)接入微信公众号平台
先要学习一下接入的资料,在这里,因为原理都在,所以一定要认真阅读,然后,利用Delphi实现一个对应函数,然后申请微信公众平台接口测试帐号. function CheckSignature(const ...
- 使用localtunne一分钟搞定微信公众号接入
记得15年那个刚刚进入工作的时候,公司有个微信公众号的项目,那个时候微信官方没有什么调试工具,也没有什么比较好的本地调试工具.当时有个功能需要调用微信JSSDK里面的扫一扫的功能.由于本地不能调试 ...
- Django+wechatpy接入微信公众平台以及授权登录
确定Django环境可以正常运行,环境搭建见:Linux 搭建Nginx+uwsgi+Django环境 安装 wechatpy[cryptography] sudo pip3 install wech ...
- Java开发微信公众号(一)---初识微信公众号以及环境搭建
ps:1.开发语言使用Java springMvc+Mybaits+spring maven实现 2.使用微信接口测试账号进行本地测试 https://mp.weixin.qq.com/debug/c ...
- 微信公众号开发C#系列-9、多公众号集中管理
1.概述 通过前面8篇关于微信开发相关文章的学习,我们已经对微信常用开发有了一个比较深入的了解.前面的文章都是基于某一特定公众号的,在现实业务中同一单位个体运营着不至一个公众号,此时就需要对多个公众号 ...
- SimpleNVR如何把安防监控画面推流到微信公众号直播
背景需求 进入移动互联网时代以来,微信已成为许多企业除官网以外必备的宣传渠道,当3.2亿直播用户与九亿微信用户的势能增加,在微信上开启直播已成为越来越多企业的不二选择. 需求分析 微信公众号作为平台来 ...
随机推荐
- 吉特日化MES & RabbitMQ 的基本配置
在吉特日化MES(日化配料系统)中涉及到大量的消息推送,其中针对设备数据的交互(读写) 大量使用了RabbitMQ来进行消息通讯以及程序上的解耦,其中包含使用PDA扫码登录到PLC控制程序,下发生产工 ...
- tomcat服务器报错
看问题 报错信息里面说 tomcat目录下conf下的aaa.xml报错 根据目录找到aaa.xml发现aaa.xml里面是空的,删除了之后就不报错了 不知道为啥这个就会报错,以前也出现过没有解决,这 ...
- SpringBoot使用Environment对象封装全部配置信息
在组件中注入Environment @Autowired private Environment env; // 直接根据key获取值 String tempDir = env.getProperty ...
- 组合式api-子父组件之间通信props和emit
整体来说和vue2也是比较相似的. 使用props传递数据到子组件 父组件给定数据. 子组件中使用defineProps来接收父组件传递的数据. 子组件emit触发事件通知父组件 思想和vue2完全一 ...
- Oracle数据字典(各种视图、表)
数据字典是存放整个数据库实例重要信息的一组表,这些数据字典大部分都是SYS用户所有. 数据字典的构成 Oracle数据字典名称由前缀和后缀组成,使用下画线"_"连接.其代表的含义如 ...
- Halo新年灯笼-halonewyaer
title: Halo新年灯笼 date: 2021-12-29 15:49:34.665 updated: 2022-03-10 16:00:53.189 url: https://www.yby6 ...
- wasm+pygbag让你在网页上也能运行Python代码:【贪吃蛇游戏】
引言 最近小伙伴告诉我一种新的方法,可以使用wasm来使浏览器网页能够运行Python代码.这一下子激起了我的兴趣,因为这意味着用户无需安装Python环境就能直接运行我的demo,这真是太方便了.所 ...
- AI与低代码解锁无限可能
前言 近年来,人工智能(AI)和低代码开发技术逐渐成为数字化转型的重要推动力.AI作为一项具有革命性潜力的技术,正在改变我们生活的方方面面.而低代码开发则提供了一种快速构建应用程序的方法,使得开发者无 ...
- .NET技术分享日活动20221022
2022年10月22日下午,个人组织举办了山东地区的第六次.NET技术分享日活动.围绕.NET.低代码Low Code.云原生 Cloud Native.大数据.算法等方向进行创新技术的实践分享. 本 ...
- 浏览器工作原理和实践(二)——JavaScript
<浏览器工作原理与实践>是极客时间上的一个浏览器学习系列,在学习之后特在此做记录和总结. 一.执行流程 实际上变量和函数声明在代码里的位置是不会改变的,而且是在编译阶段被 JavaScri ...