chatgpt接入微信公众号
验证接入
在微信公众号基本配置里面启动服务器配置,配置服务地址和 token ,点击确定会提示 验证 token 失败 ,这是因为服务端没启动,启动配置里的地址就可成功接入。
启动
app.get('/chat', ensureToken);
验证
sha1 加密
const crypto = require('crypto');
const sha1 = str => crypto.createHash('sha1').update(str).digest('hex');
module.exports = sha1;
const ensureToken = (req, res, next) => {
const {signature, timestamp, nonce, echostr} = req.query;
const str = [token, timestamp, nonce].sort().join('');
const sigToken = sha1(str);
if (sigToken !== signature) {
return res.send('验证信息错误!');
}
res.send(echostr);
};
module.exports = ensureToken;
消息接收及发送
微信服务器消息格式是 xml ,需要解析来获取数据。
解析
const parseXml = data => {
const fromUser = data.match(/<FromUserName><!\[CDATA\[([\s\S]+)\]\]><\/FromUserName>/)?.[1];
const toUser = data.match(/<ToUserName><!\[CDATA\[([\s\S]+)\]\]><\/ToUserName>/)?.[1];
const msgtype = data.match(/<MsgType><!\[CDATA\[([\s\S]+)\]\]><\/MsgType>/)?.[1];
const msgid = data.match(/<MsgId>([\s\S]+)<\/MsgId>/)?.[1];
const content = data.match(/<Content><!\[CDATA\[([\s\S]+)\]\]><\/Content>/)?.[1]?.trim();
return {fromUser, toUser, msgtype, msgid, content};
};
app.post('/chat', (req, res) => {
let body = '';
req.on('data', data => {
body += data;
});
req.on('end', () => {
const {fromUser, toUser, msgtype, msgid, content} = parseXml(body);
});
});
接入 ChatGPT
获取搜索信息:
const getChatResult = async prompt => {
if (!configuration.apiKey || !prompt.trim()) {
return '';
}
const response = await openai.createCompletion({
model: 'text-davinci-003',
prompt,
max_tokens: 1024,
temperature: 0,
top_p: 1.0,
frequency_penalty: 0.0,
presence_penalty: 0.0,
});
return response.data.choices[0].text ?? '';
};
回复消息
注意:微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。而从 ChatGPT 获取数据比较慢,所以最好使用客服消息接口进行异步回复
下面是自动回复示例,因为有三次重试,每次 5s ,所以我们可以将请求数据缓存起来,在 3 次机会内如果接口返回了数据,我们就直接 send,这样,就有 15s 请求时间。具体实现如下:
const {on, emit, off, hub} = emitter();
// 请求数据
const emitData = async (msgid, content) => {
let text = '';
try {
text = await getChatResult(content);
} catch(error) {
text = '请求错误!';
}
emit(msgid, {text, content});
};
app.post('/chat', (req, res) => {
let body = '';
req.on('data', data => {
body += data;
});
req.on('end', () => {
const {fromUser, toUser, msgtype, msgid, content} = parseXml(body);
if (msgtype === 'text' && content) {
if (!hub[msgid]) {
emitData(msgid, content);
}
on(msgid, ({text, content}) => {
text = `ChatGPT: (${content}) \n\n${text.trim()}`;
const newXml = `<xml>
<ToUserName><![CDATA[${fromUser}]]></ToUserName>
<FromUserName><![CDATA[${toUser}]]></FromUserName>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${text}]]></Content>
<CreateTime>${~~(new Date() / 1000)}</CreateTime>
</xml>`;
off(msgid);
return res.send(newXml);
});
} else {
res.send('success');
}
});
});