feat(bot): 增加图片处理功能并优化日志系统
- 新增 textToImage 和 imageToText 功能,实现文本与图片的相互转换 - 优化日志系统,使用 log4j2 实现动态日志记录- 重构 BaiduGPTManager 类,增加多线程支持和错误处理 - 更新 MessageHandleBuild 类,支持 message_id 参数 - 修复部分功能的逻辑错误,提高系统稳定性
This commit is contained in:
parent
237c9273ca
commit
1041dfa909
38
pom.xml
38
pom.xml
@ -164,6 +164,44 @@
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.17.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baidubce</groupId>
|
||||
<artifactId>qianfan</artifactId>
|
||||
<version>0.1.1</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.24.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-bom</artifactId>
|
||||
<version>2.24.1</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.24.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ public interface MessageAPI {
|
||||
@Field("message_id") long messageId
|
||||
);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/get_msg")
|
||||
Call<HttpBody<MessageBean>> getMessage(
|
||||
@Field("message_id") long messageId
|
||||
@Body
|
||||
JSONObject message
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<HttpBody<MessageBean>> execute = NapCatApi.getMessageApi().getMessage(id).execute();
|
||||
Response<HttpBody<MessageBean>> execute = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(id)).build()).execute();
|
||||
if(execute.body()==null){
|
||||
return "省流失败";
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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<MessageBean> 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;
|
||||
|
@ -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<MessageBean> body = NapCatApi.getMessageApi().getMessage(info.getLong("id")).execute().body();
|
||||
HttpBody<MessageBean> body = NapCatApi.getMessageApi().getMessage(
|
||||
MessageHandleBuild.create().setMessageId(String.valueOf(info.getLong("id"))).build()
|
||||
).execute().body();
|
||||
List<BaseHandle<?>> sendList = new ArrayList<>();
|
||||
if (body == null) {
|
||||
sendList.add(new Text("[图片获取失败]"));
|
||||
|
@ -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<String, List<Message>> msgMap;
|
||||
private final ConcurrentHashMap<String, List<Message>> 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<String, AtomicBoolean> 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<Message> 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<String, String> 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<Message> 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());
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
134
src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java
Normal file
134
src/main/java/com/yutou/qqbot/utlis/DateFormatUtils.java
Normal file
@ -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<String, ThreadLocal<SimpleDateFormat>> 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<String> 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();
|
||||
}
|
||||
}
|
124
src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java
Normal file
124
src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java
Normal file
@ -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<String, Logger> 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<String> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
server.port=8002
|
||||
spring.servlet.multipart.max-file-size=10MB
|
||||
spring.servlet.multipart.max-request-size=100MB
|
||||
spring.servlet.multipart.max-request-size=100MB
|
||||
logging.config=classpath:log4j2.xml
|
||||
logging.file.path=./logs
|
||||
logging.level.com.log.controller = trace
|
39
src/main/resources/log4j2.xml
Normal file
39
src/main/resources/log4j2.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<!-- 控制台输出 -->
|
||||
<Console name="ConsoleAppender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%thread] (%F:%L) : %m%n%throwable"/>
|
||||
</Console>
|
||||
|
||||
<!-- 动态路由日志文件 -->
|
||||
<Routing name="RoutingAppender">
|
||||
<Routes pattern="$${date:yyyy-MM-dd}">
|
||||
<Route>
|
||||
<RollingFile name="Rolling-${date:yyyy-MM-dd}" fileName="logs/${date:yyyy-MM-dd}/system.log"
|
||||
filePattern="logs/%d{yyyy-MM-dd}/system-%i.log.gz">
|
||||
<PatternLayout>
|
||||
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n%throwable</pattern>
|
||||
</PatternLayout>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<SizeBasedTriggeringPolicy size="1024 MB"/>
|
||||
</Policies>
|
||||
</RollingFile>
|
||||
</Route>
|
||||
</Routes>
|
||||
</Routing>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<!-- 屏蔽 org.apache.hc.client5 包下的所有日志 -->
|
||||
<Logger name="org.apache.hc.client5" level="OFF"/>
|
||||
<!-- 根日志记录器 -->
|
||||
<Root level="info">
|
||||
<AppenderRef ref="ConsoleAppender"/>
|
||||
<AppenderRef ref="RoutingAppender"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user