赞
踩
众说周知,微信未经过认证的订阅号在接口权限上面有非常大的限制,例如:只能回复用户消息而不能主动推送;回复消息只能在三次微信推送的15秒内;回复用户消息有字符限制等等。这里主要是为了平衡国内调用openai接口的速度和微信公众号限制。
可先关注公众号免费体验,回复口令还可以免费领取openai密钥(不限制次数,可用于学习和测试)。
#wechatmp
wechatmp:
#这里就是服务器配置中自己填写的 令牌(Token)
token: xxxxxxxxxxxx
#chatgpt
chatgpt:
model: gpt-4o
# openAI 的接口
apikey:
- sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#oepnai 接口基础地址 https://openai.xxx.com/ 或者使用自己的代理地址
baseUrl: https://openai.xxxx.com/
当我们填写服务器配置的URL时候,是需要验证URL地址的,验证代码如下:
@GetMapping("")
public ResponseEntity<Object> checkSignature(WeChatBean weChatBean) {
//验证是否为微信消息
String signatureHashcode = weChatService.checkWeChatSignature(weChatBean);
if (!signatureHashcode.equals(weChatBean.getSignature())) {
return ResponseEntity.ok("非法数据");
}
//微信公众号接口认证
if (StringUtils.isNotBlank(weChatBean.getEchostr())) {
return ResponseEntity.ok(weChatBean.getEchostr());
}
return ResponseEntity.ok(null);
}
@Override
public String checkWeChatSignature(WeChatBean weChatBean) {
String hashSignature = null;
if (StringUtils.isBlank(weChatBean.getTimestamp()) || StringUtils.isBlank(weChatBean.getNonce())) {
return hashSignature;
}
hashSignature = SignatureUtils.generateEventMessageSignature(wechatMpConfig.getToken(),
weChatBean.getTimestamp(), weChatBean.getNonce());
return hashSignature;
}
if (!params.get("MsgType").equals("text")) {
return getReplyWeChat(weChatBean.getOpenid(), params.get("ToUserName"), "暂时只支持接收文本信息");
}
对于处理openai接口回复逻辑,这里主要分为两方面:
//调用chatgpt final String msgKey = String.format(CommonConstant.CHAT_WX_USER_MSG_REPLY_KEY, msgId); if (!redisCacheUtils.hasKey(msgKey)) { redisCacheUtils.setCacheObject(msgKey, success, 30, TimeUnit.SECONDS); AsyncManager.me().execute(new TimerTask() { @Override public void run() { if (StringUtils.isNotBlank(content)) { redisCacheUtils.deleteObject(waitKey); chatgptService.singleChatStreamToWX(weChatBean.getOpenid(), msgId, content); } } }); } while (messageCountMap.containsKey(msgId)) { String replay = checkMessageCountMap(msgId, currentMsgCount, start, toUserName, weChatBean); if (null != replay) { return replay; } Object o = redisCacheUtils.getCacheObject(msgKey); if (!success.equals(String.valueOf(o))) { messageCountMap.remove(msgId); redisCacheUtils.deleteObject(Arrays.asList(msgKey, waitKey)); return getReplyWeChat(weChatBean.getOpenid(), toUserName, String.valueOf(o)); } }
if (StringUtils.isNotBlank(content) && content.equals("继续")) { while (messageCountMap.containsKey(msgId)) { String replay = checkMessageCountMap(msgId, currentMsgCount, start, toUserName, weChatBean); if (null != replay) { return replay; } if (redisCacheUtils.hasKey(waitKey)) { Object o = redisCacheUtils.getCacheObject(waitKey); Integer contentLength = getByteSize(String.valueOf(o)); messageCountMap.remove(msgId); if (contentLength < 2048) { redisCacheUtils.deleteObject(waitKey); return getReplyWeChat(weChatBean.getOpenid(), toUserName, String.valueOf(o)); } else { String replyContent = String.valueOf(o).substring(0, 580); redisCacheUtils.setCacheObject(waitKey, String.valueOf(o).replace(replyContent, ""), 60, TimeUnit.MINUTES); return getReplyWeChat(weChatBean.getOpenid(), toUserName, replyContent + "\n (公众号回复字符限制,输入\"继续\"查看后续内容)"); } } } return success; }
这里两处的 while循环操作,主要是为了尽可能的在三次微信消息推送的15秒内给出openai的回答,而不是让用户多次输入继续。
/** * 多轮会话 * @param openId 用户openid * @param msgId 消息id * @param content 问话内容 */ @Override public void multiChatStreamToWX(String openId, String msgId, String content) { OpenAiStreamClient streamClient = getStreamClient(); WeChatEventSourceListener weChatEventSourceListener = new WeChatEventSourceListener(openId, msgId); //获取历史会话记录 List<Message> messages = getWxMessageList(openId, content); ChatCompletion chatCompletion = ChatCompletion.builder().stream(true).messages(messages).build(); streamClient.streamChatCompletion(chatCompletion, weChatEventSourceListener); } /** * 单轮会话 * @param openId 用户openid * @param msgId 消息id * @param content 问话内容 */ @Override public void singleChatStreamToWX(String openId, String msgId, String content) { OpenAiStreamClient streamClient = getStreamClient(); WeChatEventSourceListener weChatEventSourceListener = new WeChatEventSourceListener(openId, msgId); Message message = Message.builder().role(BaseMessage.Role.USER).content(content).build(); ChatCompletion chatCompletion = ChatCompletion.builder().stream(true).messages(Arrays.asList(message)).build(); streamClient.streamChatCompletion(chatCompletion, weChatEventSourceListener); }
public WeChatEventSourceListener(String openId, String msgId) { this.openId = openId; this.msgId = msgId; this.sb = new StringBuffer(); } @Override public void onClosed(@NotNull EventSource eventSource) { log.info("OpenAI关闭sse连接..."); //缓存回复到redis redisCacheUtils.setCacheObject(waitKey, sb.toString(), 60, TimeUnit.MINUTES); redisCacheUtils.setCacheObject(msgKey, sb.toString(), 60, TimeUnit.SECONDS); eventSource.cancel(); } @Override public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) { log.debug("OpenAI返回数据:{}", data); if (!"[DONE]".equals(data)) { ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class); if (null == response.getChoices().get(0).getFinishReason()) { String content = response.getChoices().get(0).getDelta().getContent(); sb.append(content); } } else { log.info("OpenAI返回数据结束了"); } }
以上只是实现了简单的订阅号对话openai,下面是本项目开源地址
项目开源地址: 项目开源地址
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。