From 1041dfa909c3dd3dc86dd4d4561e808ce33791ea Mon Sep 17 00:00:00 2001
From: Yutou <583819556@qq.com>
Date: Tue, 4 Feb 2025 17:13:48 +0800
Subject: [PATCH] =?UTF-8?q?feat(bot):=20=E5=A2=9E=E5=8A=A0=E5=9B=BE?=
=?UTF-8?q?=E7=89=87=E5=A4=84=E7=90=86=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E6=97=A5=E5=BF=97=E7=B3=BB=E7=BB=9F=20-=20=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E=20textToImage=20=E5=92=8C=20imageToText=20=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=AE=9E=E7=8E=B0=E6=96=87=E6=9C=AC=E4=B8=8E?=
=?UTF-8?q?=E5=9B=BE=E7=89=87=E7=9A=84=E7=9B=B8=E4=BA=92=E8=BD=AC=E6=8D=A2?=
=?UTF-8?q?=20-=20=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97=E7=B3=BB=E7=BB=9F?=
=?UTF-8?q?=EF=BC=8C=E4=BD=BF=E7=94=A8=20log4j2=20=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E5=8A=A8=E6=80=81=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95-=20?=
=?UTF-8?q?=E9=87=8D=E6=9E=84=20BaiduGPTManager=20=E7=B1=BB=EF=BC=8C?=
=?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E7=BA=BF=E7=A8=8B=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=92=8C=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=20-=20=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0=20MessageHandleBuild=20=E7=B1=BB=EF=BC=8C=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=20message=5Fid=20=E5=8F=82=E6=95=B0=20-=20=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD=E7=9A=84=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E9=94=99=E8=AF=AF=EF=BC=8C=E6=8F=90=E9=AB=98=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F=E7=A8=B3=E5=AE=9A=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 38 +++
.../napcat/handle/MessageHandleBuild.java | 10 +-
.../com/yutou/napcat/http/MessageAPI.java | 4 +-
.../com/yutou/qqbot/data/baidu/Message.java | 4 +-
.../qqbot/models/BiliBili/BiliVideo.java | 3 +-
.../yutou/qqbot/models/Commands/BaiduGPT.java | 59 +++--
.../java/com/yutou/qqbot/models/Model.java | 36 ++-
.../com/yutou/qqbot/models/setu/QQSetu.java | 9 +-
.../yutou/qqbot/utlis/BaiduGPTManager.java | 239 +++++++++++++-----
.../com/yutou/qqbot/utlis/ConfigTools.java | 1 +
.../yutou/qqbot/utlis/DateFormatUtils.java | 134 ++++++++++
.../com/yutou/qqbot/utlis/DynamicLogFile.java | 124 +++++++++
src/main/java/com/yutou/qqbot/utlis/Log.java | 83 ++++--
src/main/resources/application.properties | 5 +-
src/main/resources/log4j2.xml | 39 +++
15 files changed, 679 insertions(+), 109 deletions(-)
create mode 100644 src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java
create mode 100644 src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java
create mode 100644 src/main/resources/log4j2.xml
diff --git a/pom.xml b/pom.xml
index 87bcf7a..92f5470 100644
--- a/pom.xml
+++ b/pom.xml
@@ -164,6 +164,44 @@
jsoup
1.17.2
+
+ com.baidubce
+ qianfan
+ 0.1.1
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.24.1
+
+
+ org.apache.logging.log4j
+ log4j-bom
+ 2.24.1
+ import
+ pom
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.24.1
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
diff --git a/src/main/java/com/yutou/napcat/handle/MessageHandleBuild.java b/src/main/java/com/yutou/napcat/handle/MessageHandleBuild.java
index ff782fd..c91f67e 100644
--- a/src/main/java/com/yutou/napcat/handle/MessageHandleBuild.java
+++ b/src/main/java/com/yutou/napcat/handle/MessageHandleBuild.java
@@ -3,6 +3,7 @@ package com.yutou.napcat.handle;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.okhttp.BaseBean;
+import com.yutou.qqbot.utlis.StringUtils;
import lombok.Data;
import java.util.ArrayList;
@@ -13,6 +14,7 @@ public class MessageHandleBuild {
private long qq;
private boolean autoEscape;
private boolean isGroup;
+ private String messageId;
public static MessageHandleBuild create() {
return new MessageHandleBuild();
@@ -41,7 +43,10 @@ public class MessageHandleBuild {
isGroup = group;
return this;
}
-
+ public MessageHandleBuild setMessageId(String messageId) {
+ this.messageId = messageId;
+ return this;
+ }
public JSONObject build() {
JSONObject json=new JSONObject();
if(isGroup){
@@ -51,6 +56,9 @@ public class MessageHandleBuild {
}
json.put("auto_escape", autoEscape);
json.put("message", msgList);
+ if(!StringUtils.isEmpty(messageId)){
+ json.put("message_id",messageId);
+ }
return json;
}
}
diff --git a/src/main/java/com/yutou/napcat/http/MessageAPI.java b/src/main/java/com/yutou/napcat/http/MessageAPI.java
index 7ee7735..8ab2e71 100644
--- a/src/main/java/com/yutou/napcat/http/MessageAPI.java
+++ b/src/main/java/com/yutou/napcat/http/MessageAPI.java
@@ -44,9 +44,9 @@ public interface MessageAPI {
@Field("message_id") long messageId
);
- @FormUrlEncoded
@POST("/get_msg")
Call> getMessage(
- @Field("message_id") long messageId
+ @Body
+ JSONObject message
);
}
diff --git a/src/main/java/com/yutou/qqbot/data/baidu/Message.java b/src/main/java/com/yutou/qqbot/data/baidu/Message.java
index 0ff2d6a..fc5d53e 100644
--- a/src/main/java/com/yutou/qqbot/data/baidu/Message.java
+++ b/src/main/java/com/yutou/qqbot/data/baidu/Message.java
@@ -22,9 +22,7 @@ public class Message {
public static Message create(String message, boolean isGTP) {
Message msg = new Message();
msg.content = message;
- if (isGTP) {
- msg.role = "assistant";
- }
+ msg.role = isGTP ? "assistant" : "user";
return msg;
}
}
diff --git a/src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java b/src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java
index c558774..390cf33 100644
--- a/src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java
+++ b/src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java
@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.enums.MessageEnum;
import com.yutou.napcat.event.MessageEvent;
+import com.yutou.napcat.handle.MessageHandleBuild;
import com.yutou.napcat.handle.OtherHandle;
import com.yutou.napcat.handle.Reply;
import com.yutou.napcat.http.NapCatApi;
@@ -78,7 +79,7 @@ public class BiliVideo extends Model {
private String onAIVideo(long id) {
try {
- Response> execute = NapCatApi.getMessageApi().getMessage(id).execute();
+ Response> execute = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(id)).build()).execute();
if(execute.body()==null){
return "省流失败";
}
diff --git a/src/main/java/com/yutou/qqbot/models/Commands/BaiduGPT.java b/src/main/java/com/yutou/qqbot/models/Commands/BaiduGPT.java
index c6d37ab..2c78f6f 100644
--- a/src/main/java/com/yutou/qqbot/models/Commands/BaiduGPT.java
+++ b/src/main/java/com/yutou/qqbot/models/Commands/BaiduGPT.java
@@ -1,20 +1,24 @@
package com.yutou.qqbot.models.Commands;
import com.yutou.napcat.QQDatabase;
-import com.yutou.napcat.handle.At;
-import com.yutou.napcat.handle.BaseHandle;
-import com.yutou.napcat.handle.Text;
+import com.yutou.napcat.handle.*;
+import com.yutou.napcat.http.NapCatApi;
import com.yutou.qqbot.Annotations.UseModel;
+import com.yutou.qqbot.QQBotApplication;
import com.yutou.qqbot.QQBotManager;
+import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.data.baidu.ResponseMessage;
+import com.yutou.qqbot.interfaces.DownloadInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.BaiduGPTManager;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.qqbot.utlis.ConfigTools;
+import com.yutou.qqbot.utlis.HttpTools;
import com.yutou.qqbot.utlis.StringUtils;
import lombok.val;
import org.apache.catalina.valves.JsonErrorReportValve;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -42,16 +46,6 @@ public class BaiduGPT extends Model {
@Override
public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
super.onMessage(qq, event, isGroup);
- String version = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, String.class);
- if (StringUtils.isEmpty(version)) {
- version = "3.5";
- BaiduGPTManager.getManager().setModelFor35();
- }
- if ("3.5".equals(version)) {
- BaiduGPTManager.getManager().setModelFor35();
- } else if ("4.0".equals(version)) {
- BaiduGPTManager.getManager().setModelFor40();
- }
if (event.getTextMessage().equals(QQGroupCommands.GPT_CLEAR)) {
BaiduGPTManager.getManager().clear();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text("已经失忆捏"));
@@ -87,17 +81,52 @@ public class BaiduGPT extends Model {
QQBotManager.getInstance().sendMessage(event.isUser(), qq, list);
}
return;
+ }else if(event.getTextMessage().contains("画画")){
+ val file = BaiduGPTManager.getManager().textToImage(String.valueOf(qq), event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").replace("画画", "").trim());
+ if(file==null){
+ QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text("画不出"));
+ }else{
+ QQBotManager.getInstance().sendMessage(file,qq, event.getMessageId().toString(), "好嘞");
+ }
+ return;
}
- ResponseMessage message = BaiduGPTManager.getManager().sendMessage(
+ if(checkImage()) {
+ parseImage(event, qq);
+ return;
+
+ }
+ Message message = BaiduGPTManager.getManager().sendMessage(
String.valueOf(qq),
event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").trim());
String sb = "调用版本:" +
BaiduGPTManager.getManager().getGPTVersion() +
"\n" +
- message.getResult();
+ message.getContent();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text(sb));
}
}
+ private void parseImage(MessageEvent event, Long qq) {
+ Image imageHandle = event.findType(Image.class);
+ val replyHandle = event.findType(Reply.class);
+ if (replyHandle != null &&imageHandle==null) {
+ imageHandle = getReply(replyHandle.getData().getId()).findType(Image.class);
+ }
+ if(imageHandle==null){
+ return;
+ }
+ HttpTools.download(imageHandle.getData().getUrl(), "gpt_parse_image.png", new DownloadInterface() {
+ @Override
+ public void onDownload(File file) {
+ super.onDownload(file);
+ if(file==null){
+ return;
+ }
+ val text = BaiduGPTManager.getManager().imageToText(String.valueOf(qq), file);
+ QQBotManager.getInstance().sendMessage(event.isUser(),qq,new Reply(event.getMessageId()),new Text(text));
+ }
+ });
+
+ }
public static void main(String[] args) {
System.out.println(ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION));
diff --git a/src/main/java/com/yutou/qqbot/models/Model.java b/src/main/java/com/yutou/qqbot/models/Model.java
index 1cf7318..684a89d 100644
--- a/src/main/java/com/yutou/qqbot/models/Model.java
+++ b/src/main/java/com/yutou/qqbot/models/Model.java
@@ -2,12 +2,18 @@ package com.yutou.qqbot.models;
import com.yutou.napcat.event.GroupMessageEvent;
import com.yutou.napcat.event.MessageEvent;
-import com.yutou.napcat.handle.At;
+import com.yutou.napcat.handle.*;
+import com.yutou.napcat.http.NapCatApi;
+import com.yutou.napcat.model.MessageBean;
+import com.yutou.okhttp.HttpBody;
import com.yutou.qqbot.QQNumberManager;
import com.yutou.qqbot.interfaces.ModelInterface;
import com.yutou.qqbot.utlis.ConfigTools;
+import com.yutou.qqbot.utlis.Log;
+import lombok.val;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -129,6 +135,34 @@ public abstract class Model implements ModelInterface {
public boolean isAt() {
return msg.contains("@" + ConfigTools.load(ConfigTools.CONFIG, ConfigTools.QQ_NUMBER, String.class));
}
+ public boolean checkImage(){
+ val image = event.findType(Image.class);
+ if(image!=null){
+ return true;
+ }
+ val reply = event.findType(Reply.class);
+ if(reply!=null){
+ val replyEvent = getReply(reply.getData().getId());
+ if(replyEvent!=null){
+ return replyEvent.findType(Image.class)!=null;
+ }
+ }
+ return false;
+ }
+ public MessageEvent getReply(long replyId){
+ HttpBody body = null;
+ try {
+ body = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(replyId)).build()).execute().body();
+ if (body == null) {
+ return null;
+ }
+ return MessageEvent.parseHandleHttp(body.getSrc());
+ } catch (IOException e) {
+ Log.e(e);
+ }
+ return null;
+
+ }
public boolean isAdmin() {
return user == 583819556L;
diff --git a/src/main/java/com/yutou/qqbot/models/setu/QQSetu.java b/src/main/java/com/yutou/qqbot/models/setu/QQSetu.java
index 9b4463c..628d9fa 100644
--- a/src/main/java/com/yutou/qqbot/models/setu/QQSetu.java
+++ b/src/main/java/com/yutou/qqbot/models/setu/QQSetu.java
@@ -7,10 +7,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.enums.MessageEnum;
import com.yutou.napcat.event.GroupMessageEvent;
-import com.yutou.napcat.handle.At;
-import com.yutou.napcat.handle.BaseHandle;
-import com.yutou.napcat.handle.Image;
-import com.yutou.napcat.handle.Text;
+import com.yutou.napcat.handle.*;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.MessageBean;
import com.yutou.okhttp.HttpBody;
@@ -70,7 +67,9 @@ public class QQSetu extends Model {
RedisTools.set(db_print, redisKey, json.toJSONString());
JSONObject info = setu.getJSONObject("info");
JSONObject score = setu.getJSONObject("score");
- HttpBody body = NapCatApi.getMessageApi().getMessage(info.getLong("id")).execute().body();
+ HttpBody body = NapCatApi.getMessageApi().getMessage(
+ MessageHandleBuild.create().setMessageId(String.valueOf(info.getLong("id"))).build()
+ ).execute().body();
List> sendList = new ArrayList<>();
if (body == null) {
sendList.add(new Text("[图片获取失败]"));
diff --git a/src/main/java/com/yutou/qqbot/utlis/BaiduGPTManager.java b/src/main/java/com/yutou/qqbot/utlis/BaiduGPTManager.java
index 26ea5f4..8f12d45 100644
--- a/src/main/java/com/yutou/qqbot/utlis/BaiduGPTManager.java
+++ b/src/main/java/com/yutou/qqbot/utlis/BaiduGPTManager.java
@@ -1,104 +1,225 @@
package com.yutou.qqbot.utlis;
-import com.alibaba.fastjson2.JSONObject;
+import com.baidubce.qianfan.Qianfan;
+import com.baidubce.qianfan.model.chat.ChatResponse;
+import com.baidubce.qianfan.model.image.Image2TextResponse;
+import com.baidubce.qianfan.model.image.Text2ImageResponse;
import com.yutou.qqbot.data.baidu.Message;
-import com.yutou.qqbot.data.baidu.ResponseMessage;
+import lombok.val;
-import java.nio.charset.StandardCharsets;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Base64;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
public class BaiduGPTManager {
- private static int MAX_MESSAGE = 5;
- private static BaiduGPTManager manager;
- private static final String url_3_5 = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions";
- //4.0
- private static final String url_4_0 = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro";
- private static String url = url_3_5;
+ private static final AtomicInteger MAX_MESSAGE = new AtomicInteger(20);
private static final String AppID = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_APPID, String.class);
private static final String ApiKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_API_KEY, String.class);
+ //ConfigTools.load操作可以确保获取到相关参数,所以无需关心
+ private static final String AccessKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_ACCESS_KEY, String.class);
private static final String SecretKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_SECRET_KEY, String.class);
- private static Map> msgMap;
+ private final ConcurrentHashMap> msgMap;
+ private final static String modelFor40 = "ERNIE-4.0-8K";
+ private final static String modelFor35 = "ERNIE-3.5-8K";
+ private String model = modelFor35;
+ // 新增锁映射表
+ private final ConcurrentHashMap userLocks = new ConcurrentHashMap<>();
+ private final Qianfan qianfan;
private BaiduGPTManager() {
- msgMap = new HashMap<>();
+ msgMap = new ConcurrentHashMap<>();
+ qianfan = new Qianfan(AccessKey, SecretKey);
+ String savedVersion = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, String.class);
+ if (StringUtils.isEmpty(savedVersion) || (!"3.5".equals(savedVersion) && !"4.0".equals(savedVersion))) {
+ savedVersion = "3.5";
+ ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, savedVersion);
+ }
+ model = "3.5".equals(savedVersion) ? modelFor35 : modelFor40;
}
+ private static volatile BaiduGPTManager manager;
+
public static BaiduGPTManager getManager() {
if (manager == null) {
- manager = new BaiduGPTManager();
+ synchronized (BaiduGPTManager.class) {
+ if (manager == null) {
+ manager = new BaiduGPTManager();
+ }
+ }
}
return manager;
}
public int setMaxMessageCount(int count) {
- MAX_MESSAGE = count;
- return MAX_MESSAGE;
+ MAX_MESSAGE.set(count);
+ return count;
}
- public void setModelFor40() {
- url = url_4_0;
+ public synchronized void setModelFor40() {
+ model = modelFor40;
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, "4.0");
}
- public void setModelFor35() {
- url = url_3_5;
+ public synchronized void setModelFor35() {
+ model = modelFor35;
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, "3.5");
}
- public void clear() {
+ /**
+ * 这里确实是需要清空所有数据
+ */
+ public synchronized void clear() { // 添加同步
msgMap.clear();
+ for (AtomicBoolean value : userLocks.values()) {
+ value.set(false);
+ }
+ userLocks.forEachValue(1, atomicBoolean -> atomicBoolean.set(false));
+ userLocks.clear();
}
- private String getToken() {
- String _url = String.format("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s"
- , ApiKey
- , SecretKey
- );
- String get = HttpTools.get(_url);
- JSONObject response = JSONObject.parseObject(get);
- return response.getString("access_token");
+
+ // 这个是官方的示例代码,表示连续对话
+ private static void exampleChat() {
+ Qianfan qianfan = new Qianfan();
+ ChatResponse response = qianfan.chatCompletion()
+ // 设置需要使用的模型,与endpoint同时只能设置一种
+ .model("ERNIE-Bot")
+ // 通过传入历史对话记录来实现多轮对话
+ .addMessage("user", "你好!你叫什么名字?")
+ .addMessage("assistant", "你好!我是文心一言,英文名是ERNIE Bot。")
+ // 传入本轮对话的用户输入
+ .addMessage("user", "刚刚我的问题是什么?")
+ .execute();
+ System.out.println("输出内容:" + response.getResult());
}
- public ResponseMessage sendMessage(String user, String message) {
- List messages = msgMap.getOrDefault(user, new ArrayList<>());
- if (messages.size() > MAX_MESSAGE * 2) {
- messages.remove(0);
- messages.remove(1);
+ public Message sendMessage(String user, String message) {
+ // 获取或创建用户锁
+ AtomicBoolean lock = userLocks.computeIfAbsent(user, k -> new AtomicBoolean(false));
+ // 尝试加锁(如果已被锁定则立即返回提示)
+ if (!lock.compareAndSet(false, true)) {
+ return Message.create("您有请求正在处理中,请稍后再试", true);
}
- messages.add(Message.create(message));
-
- JSONObject json = new JSONObject();
- json.put("messages", messages);
- System.out.println("json = " + json);
-
- Map map = new HashMap<>();
- map.put("Content-Type", "application/json");
- map.put("Content-Length", String.valueOf(json.toJSONString().getBytes(StandardCharsets.UTF_8).length));
- String post = HttpTools.http_post(url + "?access_token=" + getToken()
- , json.toJSONString().getBytes(StandardCharsets.UTF_8), 0, map);
- System.out.println("post = " + post);
- if (StringUtils.isEmpty(post)) {
- clear();
- return sendMessage(user, message);
+ try {
+ List list = msgMap.computeIfAbsent(user, k -> Collections.synchronizedList(new ArrayList<>()));
+ // 限制历史消息的最大数量
+ synchronized (list) {
+ if (list.size() >= MAX_MESSAGE.get()) {
+ int removeCount = list.size() - MAX_MESSAGE.get() + 1; // 腾出空间给新消息
+ list.subList(0, removeCount).clear();
+ }
+ list.add(Message.create(message));
+ }
+ val builder = qianfan.chatCompletion()
+ .model(model);
+ for (Message msg : list) {
+ builder.addMessage(msg.getRole(), msg.getContent());
+ }
+ ChatResponse chatResponse = builder.execute();
+ Message response = Message.create(chatResponse.getResult(), true);
+ synchronized (list) {
+ list.add(response);
+ if (list.size() > MAX_MESSAGE.get()) {
+ int overflow = list.size() - MAX_MESSAGE.get();
+ list.subList(0, overflow).clear();
+ }
+ }
+// msgMap.put(user, list);
+ return response;
+ } catch (Exception e) {
+ Log.e(e, message);
+ return Message.create("请求失败,请重试", true);
+ } finally {
+ lock.set(false);
+ userLocks.remove(user, lock);
}
- ResponseMessage response = JSONObject.parseObject(post, ResponseMessage.class);
- messages.add(Message.create(response.getResult(), true));
- msgMap.put(user, messages);
- System.out.println("\n\n");
- return response;
+
+ }
+
+ /**
+ * 将文本转换为图像文件
+ * 该方法使用预训练的AI模型将给定的文本转换为图像,并将其保存为文件
+ *
+ * @param user 用户标识符,用于为生成的图像文件命名
+ * @param text 要转换为图像的文本
+ * @return 返回生成的图像文件对象,如果转换过程中发生错误,则返回null
+ */
+ public File textToImage(String user, String text) {
+ // 使用QianFan的text2Image方法将文本转换为图像数据
+ Text2ImageResponse response = qianfan.text2Image()
+ .prompt(text)
+ .execute();
+ // 获取转换后的图像数据,以Base64编码的图像字符串形式
+ val b64Image = response.getData().get(0).getB64Image();
+ // 将Base64编码的图像数据转换为图像文件
+ // 创建一个临时目录下的图像文件,文件名包含用户标识符和当前时间戳,以确保唯一性
+ val imageFile = new File("tmp" + File.separator + user + "_" + System.currentTimeMillis() + ".png");
+ try (val inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(b64Image))) {
+ // 将解码后的图像数据复制到图像文件中,替换现有文件
+ Files.copy(inputStream, imageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ return imageFile;
+ } catch (Exception e) {
+ // 如果在图像文件生成过程中发生错误,记录错误信息
+ Log.e(e);
+ }
+ // 如果发生错误,返回null
+ return null;
+ }
+
+ /**
+ * 将图片转换为文本描述
+ *
+ * @param user 使用该功能的用户标识
+ * @param file 要转换的图片文件
+ * @return 转换后的文本描述,如果转换失败则返回null
+ */
+ public String imageToText(String user, File file) {
+ // 将file文件转换成base64的代码
+ try {
+ // 读取文件内容并转换为Base64编码
+ val base64 = Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath()));
+
+ // 调用图像转文本的API
+ Image2TextResponse response = qianfan.image2Text()
+ .image(base64)
+ .prompt("分析图片,如果图中有文本则加上文本内容")
+ .execute();
+
+ // 获取API返回的结果
+ String result = response.getResult();
+
+ // 如果结果不是中文,通过sendMessage函数尝试将其翻译成中文
+ result = sendMessage("bot", "你是一个语言翻译专家,如果这段内容不是中文,请翻译成中文,如果已经是中文则不需要翻译:" + result).getContent();
+
+ // 返回最终的中文描述结果
+ return result;
+ } catch (Exception e) {
+ // 异常处理:记录错误日志
+ Log.e(e);
+ }
+ // 如果发生异常,返回null
+ return null;
}
public String getGPTVersion() {
- return (url.equals(url_3_5) ? "3.5" : "4.0");
+ return (model.equals(modelFor35) ? "3.5" : "4.0");
}
public static void main(String[] args) throws Exception {
- ResponseMessage message = BaiduGPTManager.getManager().sendMessage("test", "现在假设小猪等于1,小猴等于2");
- System.out.println(message.getResult());
- message = BaiduGPTManager.getManager().sendMessage("test", "那么小猪加上小猴等于多少?");
- System.out.println(message.getResult());
+// BaiduGPTManager.getManager().textToImage("user","画一个猫娘,用二次元动画画风,她是粉色头发,坐在地上");
+// BaiduGPTManager.getManager().imageToText("user",new File("test.png"));
+// Message message = BaiduGPTManager.getManager().sendMessage("user", "现在假设小猪等于1,小猴等于2");
+// System.out.println(message.getContent());
+// message = BaiduGPTManager.getManager().sendMessage("user", "那么小猪加上小猴等于多少?");
+// System.out.println(message.getContent());
+ System.out.println(BaiduGPTManager.getManager().sendMessage("user", "分析这个网页链接的页面内容,而非链接本身:https://www.bilibili.com/video/BV1TTf5YrESz/").getContent());
}
}
diff --git a/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java b/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java
index ecdd0de..ed308bb 100644
--- a/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java
+++ b/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java
@@ -30,6 +30,7 @@ public class ConfigTools {
public static final String BAIDU_GPT_VERSION = "baidu.gpt.version";
public static final String BAIDU_GPT_APPID = "baidu.gpt.appid";
public static final String BAIDU_GPT_API_KEY = "baidu.gpt.apikey";
+ public static final String BAIDU_GPT_ACCESS_KEY = "baidu.gpt.accessKey";
public static final String BAIDU_GPT_SECRET_KEY = "baidu.gpt.SecretKey";
public static final String TURNIP_PROPHET_SERVER = "turnip.server";
public static final String TURNIP_PROPHET_SEND_TMP_GROUP = "turnip.send.tmp.group";
diff --git a/src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java b/src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java
new file mode 100644
index 0000000..f6827fd
--- /dev/null
+++ b/src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java
@@ -0,0 +1,134 @@
+package com.yutou.qqbot.utlis;
+
+import org.springframework.util.StringUtils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DateFormatUtils {
+ private final Map> formats = new ConcurrentHashMap<>();
+ private static volatile DateFormatUtils utils;
+ public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
+
+ private DateFormatUtils() {}
+
+ public static DateFormatUtils getInstance() {
+ if (utils == null) {
+ synchronized (DateFormatUtils.class) {
+ if (utils == null) {
+ utils = new DateFormatUtils();
+ }
+ }
+ }
+ return utils;
+ }
+
+ public String format(Date date, String format) {
+ getFormat(format).applyPattern(format);
+ return getFormat(format).format(date);
+ }
+
+ public String format(long time, String format) {
+ return getFormat(format).format(new Date(time));
+ }
+
+ public String format(long time) {
+ if (time < 1000000000) {
+ time *= 1000;
+ }
+ return getFormat().format(new Date(time));
+ }
+
+ public String format(Date date) {
+ return getFormat().format(date);
+ }
+
+ public String format() {
+ return getFormat().format(new Date());
+ }
+
+ public Date parseTimer(String date) {
+ return parse(date, "HH:mm:ss");
+ }
+
+ public Date parse(String date, String format) {
+ try {
+ if(date.startsWith("1")){
+ return new Date(Long.parseLong(date));
+ }
+ return getFormat(format).parse(date);
+ } catch (ParseException e) {
+ System.err.println("Error parsing date: " + e.getMessage());
+ return null;
+ }
+ }
+
+ public String parseString(String date, String format) {
+ Date time = parse(date, format);
+ return format(time, format);
+ }
+ public String convertSeconds(long totalSeconds) {
+ // 计算总小时数
+ long hours = totalSeconds / 3600;
+ // 剩余的秒数
+ long remainingSecondsAfterHours = totalSeconds % 3600;
+ // 计算分钟数
+ long minutes = remainingSecondsAfterHours / 60;
+ // 最后剩余的秒数
+ long seconds = remainingSecondsAfterHours % 60;
+ return String.format("%d小时%d分%d秒", hours, minutes, seconds);
+ }
+ public String formatMillis(long millis) {
+ Duration duration = Duration.ofMillis(millis);
+ int seconds = (int) (duration.getSeconds() % 60);
+ int minutes = (int) ((duration.getSeconds() / 60) % 60);
+ int hours = (int) (duration.getSeconds() / 3600);
+
+ return String.format("%02d:%02d:%02d", hours, minutes, seconds);
+ }
+ public boolean checkTime(List weeks, String recordDate) {
+ if (!StringUtils.hasText(recordDate)) {
+ recordDate = "00:00:00 - 23:59:59";
+ }
+ String[] parts = recordDate.split(" - ");
+ LocalTime startTime = LocalTime.parse(parts[0], DateTimeFormatter.ofPattern("HH:mm:ss"));
+ LocalTime endTime = LocalTime.parse(parts[1], DateTimeFormatter.ofPattern("HH:mm:ss"));
+
+
+ // 获取当前时间
+ LocalTime currentTime = LocalTime.now();
+ LocalDate currentDate = LocalDate.now();
+
+
+ // 获取当前日期对应的星期几(1-7分别对应周一到周日)
+ int currentWeekDay = currentDate.getDayOfWeek().getValue();
+
+ // 判断当前日期是否在指定的星期列表中
+ boolean isSpecifiedWeekDay;
+ if (weeks == null) {
+ isSpecifiedWeekDay = true;
+ } else {
+ isSpecifiedWeekDay = weeks.contains(String.valueOf(currentWeekDay));
+ }
+
+ // 判断当前时间是否在指定的时间范围内
+ boolean isWithinRange = (currentTime.isAfter(startTime) || currentTime.equals(startTime)) &&
+ (currentTime.isBefore(endTime) || currentTime.equals(endTime));
+ return isWithinRange && isSpecifiedWeekDay;
+ }
+ private SimpleDateFormat getFormat() {
+ return getFormat(DEFAULT_PATTERN);
+ }
+
+ private SimpleDateFormat getFormat(String format) {
+ return formats.computeIfAbsent(format, key -> ThreadLocal.withInitial(() -> new SimpleDateFormat(key))).get();
+ }
+}
diff --git a/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java b/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java
new file mode 100644
index 0000000..39805d9
--- /dev/null
+++ b/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java
@@ -0,0 +1,124 @@
+package com.yutou.qqbot.utlis;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+
+import java.util.Date;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+public class DynamicLogFile {
+ // 创建一个缓存,用于存储Logger对象,最大容量为1000,过期时间为10分钟
+ static Cache cache = CacheBuilder.newBuilder()
+ .maximumSize(1000)
+ .expireAfterAccess(10, TimeUnit.MINUTES)
+ .removalListener(it -> {
+ if (it.wasEvicted()) {
+ if (it.getKey() != null) {
+ String loggerName = (String) it.getKey();
+ remove(loggerName, true);
+ }
+ }
+ })
+ .build();
+
+ // 根据loggerName获取Logger对象,如果缓存中不存在,则创建一个新的Logger对象并放入缓存
+ public static Logger getLogger(String loggerName) {
+ try {
+ return cache.get(loggerName, () -> {
+ configureLogger(loggerName);
+ return LogManager.getLogger(loggerName);
+ });
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // 配置Logger对象
+ private static void configureLogger(String loggerName) {
+
+ LoggerContext context = (LoggerContext) LogManager.getContext(false);
+ Configuration config = context.getConfiguration();
+
+ // 创建日志格式
+ Layout layout = PatternLayout.newBuilder()
+ .withPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n")
+ .build();
+
+ // 创建时间触发策略
+ TimeBasedTriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder()
+ .build();
+
+ // 创建文件大小触发策略
+ SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy("100 MB");
+
+ // 创建组合触发策略
+ CompositeTriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(timePolicy, sizePolicy);
+
+
+ // 创建滚动文件Appender
+ RollingFileAppender appender = RollingFileAppender.newBuilder()
+ .setName(loggerName)
+ .withFileName("logs" + "/" + DateFormatUtils.getInstance().format(new Date(), "yyyy-MM-dd") + "/" + loggerName + ".log")
+ .withFilePattern("logs" + "/" + "%d{yyyy-MM-dd}" + "/" + loggerName + "-%i.log.gz")
+ .setLayout(layout)
+ .setImmediateFlush(true)
+ .withAppend(true)
+ .setIgnoreExceptions(false)
+ .withPolicy(triggeringPolicy)
+ .build();
+
+ appender.start();
+ config.addAppender(appender);
+
+ // 获取Logger对象
+ org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
+ if (coreLogger == null) {
+ throw new IllegalStateException("Logger with name " + loggerName + " does not exist.");
+ }
+
+ // 将Appender添加到Logger对象中
+ coreLogger.addAppender(appender);
+ coreLogger.setLevel(Level.ALL);
+ coreLogger.setAdditive(false);
+
+ // 更新Logger对象
+ context.updateLoggers();
+
+ }
+
+ // 移除Logger对象
+ public static void remove(String loggerName) {
+ remove(loggerName, false);
+ }
+
+ // 私有方法,移除Logger对象,isAuto参数用于判断是否是自动移除
+ private static void remove(String loggerName, boolean isAuto) {
+ if (!isAuto) {
+ cache.invalidate(loggerName);
+ }
+ LoggerContext context = (LoggerContext) LogManager.getContext(false);
+ Configuration config = context.getConfiguration();
+ org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
+ Appender appender = config.getAppender(loggerName);
+ if (appender != null) {
+ appender.stop();
+ coreLogger.removeAppender(appender);
+ }
+ config.getAppenders().remove(loggerName);
+ context.updateLoggers();
+ }
+
+}
diff --git a/src/main/java/com/yutou/qqbot/utlis/Log.java b/src/main/java/com/yutou/qqbot/utlis/Log.java
index 7126c48..a21d6bd 100644
--- a/src/main/java/com/yutou/qqbot/utlis/Log.java
+++ b/src/main/java/com/yutou/qqbot/utlis/Log.java
@@ -1,33 +1,74 @@
package com.yutou.qqbot.utlis;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
+
public class Log {
- public static void i(String tag, Object log) {
- i('[' + tag + ']' + log);
+
+ public static void i() {
+ System.out.println();
}
- public static void i(Object log) {
- if (ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, boolean.class, false)) {
- System.out.printf("[%s]%s%n",
- AppTools.getToDayNowTimeToString(),
- log
- );
+ public static Logger getDynamicLogger(String loggerName) {
+ return DynamicLogFile.getLogger(loggerName);
+ }
+
+ public static void removeDynamicLogger(String loggerName) {
+ DynamicLogFile.remove(loggerName);
+ }
+
+ public static void i(Object... log) {
+ if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG))) {
+ return;
}
+ LogManager.getLogger(getStackTrace()).info(buildLog(log));
}
- public static void e(String tag, Exception e) {
- System.err.printf("[%s]%s - %s%n",
- AppTools.getToDayNowTimeToString(),
- tag,
- AppTools.getExceptionString(e)
- );
- }
-
- public static void i(Object tag, Object log) {
- if (tag instanceof String) {
- i("[" + tag + "]" + log);
- } else {
- i(tag.getClass().getSimpleName(), log);
+ public static void e(Object... log) {
+ if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
+ return;
}
+ LogManager.getLogger(getStackTrace()).error(buildLog(log));
+ }
+
+ public static void e(Throwable e, Object... log) {
+ if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
+ return;
+ }
+ LogManager.getLogger(getStackTrace()).error(buildLog(log), e);
+ }
+
+ public static void e(Throwable e) {
+ if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
+ return;
+ }
+ LogManager.getLogger().error(getStackTrace(), e);
+ }
+
+
+ public static void d(Object... log) {
+ if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
+ return;
+ }
+ LogManager.getLogger().debug(buildLog(log));
+ }
+
+ private static String getStackTrace() {
+ StackTraceElement element = StackLocatorUtil.getStackTraceElement(3);
+ return "(" + element.getFileName() + ":" + element.getLineNumber() + ")";
+ }
+
+ private static String buildLog(Object... log) {
+ StringBuilder sb = new StringBuilder();
+ for (Object obj : log) {
+ if (!sb.isEmpty()) {
+ sb.append("|");
+ }
+ sb.append(obj);
+ }
+ return sb.toString();
}
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 3c76dd8..8d07152 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,3 +1,6 @@
server.port=8002
spring.servlet.multipart.max-file-size=10MB
-spring.servlet.multipart.max-request-size=100MB
\ No newline at end of file
+spring.servlet.multipart.max-request-size=100MB
+logging.config=classpath:log4j2.xml
+logging.file.path=./logs
+logging.level.com.log.controller = trace
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..801c1d5
--- /dev/null
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n%throwable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+