零基础使用Laf三分钟免费将ChatGPT接入微信公众号

近期,ChatGPT这个聊天机器人模型异常火热,各种各样的体验渠道层出不穷,收费的和免费的应有尽有,五花八门。所以,我最近就在研究如何快速简单地体验ChatGPT的强大功能,其中一种方式就是:将ChatGPT接入微信公众号。微信公众号作为一种流行的社交媒体平台,可以用来提供服务和推广产品,因此把ChatGPT接入公众号意义重大。经过我的尝试,最终成功使用Laf平台实现了这一需求。

一、什么是Laf?

Laf 是一个 Serverless 框架,提供开箱即用的云函数,云数据库,对象存储等能力,是一个非常干净清爽的开发平台,不仅入门简单,还能像写博客一样写代码!最重要的是,敲重点,三分钟即可上线 ChatGPT 应用!

若想深入了解可点击 https://doc.laf.run/guide/

二、入门Laf开发应用前的准备

在正式开发应用前需要先进行账号注册和新建应用,具体操作方法可访问

https://doc.laf.run/guide/web-ide/

说明:下面讲解的将ChatGPT接入微信公众号需要访问 https://laf.dev 域名进行注册

三、正式接入ChatGPT

在应用列表点击「开发」按钮即可进入 Laf 应用开发 IDE

直接Web IDE在线开发,连安装软件的工作都省了,不得不说,十分亲民

picture.image

picture.image

1.添加NPM依赖

点击左下角【NPM依赖】处的“+”按钮,在弹框中搜索“chatgpt”,选中第一个后再点击“保存并重启”,等待3秒左右依赖会添加完成

picture.image

2.添加函数

点击左上角【函数列表】处的“+”按钮,在弹框中输入函数名(比如:wechat-gpt),其他默认,完成后点击“确认”按钮,等待3秒左右函数会添加完成

picture.image

说明:关于云函数的入门,可点击 https://doc.laf.run/guide/function/ 链接了解详情

3.云函数完整代码

将下方完整云函数的代码复制粘贴到自己的云函数中

  
import cloud from '@lafjs/cloud'  
import * as crypto from 'crypto'  
  
const wait_message = `处理中 ... \n\n请稍等5秒后发送【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;  
  //微信公众号token  
 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", JSON.stringify(message));  
 // 检查是否是重试操作,如果是重试操作,返回上一次的回复  
 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: ${JSON.stringify(result)}`);  
  
 return answer;  
}  
  
// 获取 OpenAI API 的回复  
async function getOpenAIReply(question, parentId, conId) {  
 console.log("执行getOpenAIReply", "question:" + question + ",parentId:" + parentId + ",conId:" + conId);  
 // 引入 ChatGPTUnofficialProxyAPI 模块  
 const { ChatGPTUnofficialProxyAPI } = await import('chatgpt')  
 // 创建 ChatGPTUnofficialProxyAPI 实例  
 const api = new ChatGPTUnofficialProxyAPI({  
 //https://chat.openai.com/api/auth/session 在浏览器中登录ChatGPT网页版,再访问这个网址获取accessToken  
    accessToken: "accessToken",  
 apiReverseProxyUrl: "https://ai.fakeopen.com/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('getOpenAIReply异常', e.message)  
 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 });  
 console.log('清理历史会话', res);  
 return clear_message;  
 } else {  
 return help_message;  
 }  
}

说明:由于OpenAI的 API Key 需要充值才能用,所以我们选择剑走偏锋,直接使用ChatGPT网页版。但是国内环境无法访问 ChatGPT,所以我们需要一个 Proxy。不用担心,国外已经有热心小哥给我们提供了公共的Proxy,我们只需要直接调用就好啦!

picture.image

详情可参考ChatGPTUnofficialProxyAPI的使用文档

https://github.com/transitive-bullshit/chatgpt-api#usage---chatgptunofficialproxyapi

核心函数:

  1. getOpenAIReply函数通过引入ChatGPTUnofficialProxyAPI模块,创建实例并调用其方法,获取ChatGPT的回复内容。

  2. verifySignature函数用于校验微信服务器发送的消息是否合法。

  3. toXML函数将回复内容组装成XML格式。

  4. processCommandText函数用于处理指令,目前支持清除历史会话和获取帮助信息两个指令。

注意:

  1. 上面函数中定义的token要与微信公众号中设置一致。

  2. accessToken的获取方式:你需要先在浏览器中登录ChatGPT网页版,然后在浏览器中访问这个URL:https://chat.openai.com/api/auth/session,浏览器会返回一个JSON,里面包含了accessToken字段,将这个字段的值复制即可。

云函数写完之后就点击发布,左侧的接口地址要保存一下,稍后将在微信公众号中使用此地址。

picture.image

四、配置微信公众号

1. 注册微信公众号

点击链接 https://mp.weixin.qq.com/ 先注册一个公众号,订阅号和服务号任选一个进行注册

picture.image

picture.image

2. 配置微信公众号

完成注册后,扫码登录,点开左侧菜单中的「设置与开发」,点击「基本配置」,然后点击「服务器配置」,服务器配置那里点击修改配置:

picture.image

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

picture.image

在线生成32位随机密钥可点击链接 https://www.sexauth.com/

点击“确定”按钮

picture.image

再点击“仍然提交”按钮

picture.image

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

picture.image

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

picture.image

ChatGPT 机器人可以回答用户提出的问题,并且可以根据用户提供的上下文进行回复。以下是一些指令和关键字,可以帮助您更好地使用 ChatGPT 机器人:

  • 【1】:获取上一次问题的回复。
  • /clear:清除上下文。
  • /help:获取更多帮助。 除了以上指令和关键字外,你还可以根据自己的需求进行定制化开发,以满足用户的需求。

picture.image

新手小白如何注册OpenAI账号并试用ChatGPT

0
0
0
0
评论
未登录
暂无评论