chatgpt接入微信公众号

587 阅读2分钟

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');
    }
  });
});

chat.png