技术文档AIGPT让闲置的公众号接入完全免费的GPT
noise导语:为什么说是闲置?因为公众号的接口被设计为5s超时,而GPT的回复是肯定有延迟的,所以建议使用闲置公众号,实现:使用Laf 云平台将 ChatGPT 接入微信公众号,每次问答后通过回复1来查看上下文信息
发现自:https://forum.laf.run/d/364
体验:
1. 准备工作
首先需要注册一个 Laf 平台账号:https://laf.run
注册登录之后,点击新建,建立一个应用:
点击开发,进入应用开发界面:
然后输入 chatgpt 并回车进行搜索,选择第一个搜索结果,保存并重启:
重启之后,自定义依赖项中便出现了 chatgpt。
新建云函数
然后我们点击函数,函数列表右侧的加号,新增一个可以接入微信公众号的 ChatGPT 云函数:
云函数完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
| // 引入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; }
// 返回组装 xmlfunction 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 账号,都可以使用这种方法
核心函数:
- getOpenAIReply函数通过引入ChatGPTUnofficialProxyAPI模块,创建实例并调用其方法,获取 ChatGPT 的回复内容。
- verifySignature函数用于校验微信服务器发送的消息是否合法。
- toXML函数将回复内容组装成XML格式。
- processCommandText函数用于处理指令,目前支持清除历史会话和获取帮助信息两个指令。
注意:
云函数写完之后就点击发布,左侧的接口地址要保存一下,稍后将在微信公众号中使用此地址。
配置微信公众号
这一步我们需要在微信公众号平台上配置开发者信息,并将服务器地址设置为部署好的云函数服务地址。步骤如下:
首先你需要有一个公众号,然后登录微信公众平台,点开左侧的「设置与开发」,点击「基本设置」,然后点击「服务器配置」,服务器配置那里点击修改配置:
将云函数服务地址复制到「服务器 URL」中,下边的 Token 与云函数代码中的 token 保持一致,下边的 EncodingAESKey 点击右侧随机生成就行,然后点击提交:
返回 token 校验成功后,点击「启用」即可。
现在你已经完成了所有必要的设置和配置,下面就可以直接进入微信公众号「Laf 开发者」后台与机器人进行交互啦!
ChatGPT 机器人可以回答用户提出的问题,并且可以根据用户提供的上下文进行回复。以下是一些指令和关键字,可以帮助您更好地使用 ChatGPT 机器人:
- 【1】:获取上一次问题的回复。
- /clear:清除上下文。
- /help:获取更多帮助。 除了以上指令和关键字外,你还可以根据自己的需求进行定制化开发,以满足用户的需求。
可参考公众号对接GPT作者的仓库:
注意
关于环境变量设置:在左下角设置中添加即可
token在代码中修改并和公众号保持一致
如果你举得延迟影响体验,你也可以试试将GPT接入个人微信【使用网页版接口】
如像我一样无论在群聊还是私聊中,通过设置关键词触发自动回复
开源:https://github.com/zhayujie/chatgpt-on-wechat