最近很火的ChatGPT
可以说已经满大街可见了,到处都有各种各样的体验地址,有收费的也有免费的,总之是五花八门、花里胡哨。
所以呢,最近我就在研究怎么才能方便快捷的体验到ChatGPT
的强大功能,其中一个就是:把ChatGPT
接入公众号。毕竟公众号是一种非常流行的社交媒体平台,可以用来提供服务、推广产品等。经过我的一番折腾,最后终于成功通过 Laf 实现了这个需求。
本文将详细介绍如何使用 Laf 云平台将 ChatGPT 接入微信公众号,实现智能问答、聊天机器人等功能。
首先需要注册一个 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 校验成功后,点击「启用」即可。
现在你已经完成了所有必要的设置和配置,下面就可以直接进入微信公众号后台与机器人进行交互啦!
ChatGPT 机器人可以回答用户提出的问题,并且可以根据用户提供的上下文进行回复。以下是一些指令和关键字,可以帮助您更好地使用 ChatGPT 机器人:
通过以上步骤,您已经成功地将 ChatGPT 接入微信公众号,并使用 Laf 平台来部署 ChatGPT 云函数。这样,您就可以在公众号中为您的用户提供智能问答、聊天机器人等服务了,微信公众号自带的自动回复功能可以抛弃了。
当然,直接接入 ChatGPT 网页版肯定没有接入 API 稳定,如果你想通过 API 的方式接入,可以参考原作者的官方仓库: