1
This commit is contained in:
63
src/main/java/plugin/App.java
Normal file
63
src/main/java/plugin/App.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package plugin;
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.JavaPlugin;
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescriptionBuilder;
|
||||||
|
import net.mamoe.mirai.event.GlobalEventChannel;
|
||||||
|
import net.mamoe.mirai.event.events.FriendMessageEvent;
|
||||||
|
import net.mamoe.mirai.event.events.GroupMessageEvent;
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger;
|
||||||
|
import plugin.listener.UserMessageListener;
|
||||||
|
import plugin.utils.ConfigUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static plugin.utils.ConfigUtil.config;
|
||||||
|
|
||||||
|
|
||||||
|
public final class App extends JavaPlugin {
|
||||||
|
public static final App INSTANCE = new App();
|
||||||
|
public static MiraiLogger logger;
|
||||||
|
private String owner, bot;
|
||||||
|
|
||||||
|
private App() {
|
||||||
|
super(new JvmPluginDescriptionBuilder("com.plugin.chatAI-InGroup-v2", "0.1.0")
|
||||||
|
.name("ChatAI-InGroup-v2")
|
||||||
|
.author("SLHAF")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
//加载配置
|
||||||
|
try {
|
||||||
|
logger = getLogger();
|
||||||
|
ConfigUtil.load();
|
||||||
|
owner = config.get("owner").substring(1);
|
||||||
|
bot = config.get("bot").substring(1);
|
||||||
|
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
getLogger().info("ChatAI-InGroup-v2 loaded!");
|
||||||
|
|
||||||
|
|
||||||
|
//群聊监听器
|
||||||
|
GlobalEventChannel.INSTANCE.filterIsInstance(GroupMessageEvent.class)
|
||||||
|
.filter(event -> {
|
||||||
|
String msg = event.getMessage().contentToString();
|
||||||
|
return (msg.startsWith(".") && msg.length() != 1) || msg.startsWith("@"+bot) || msg.startsWith("/c ");
|
||||||
|
}).registerListenerHost(new UserMessageListener());
|
||||||
|
|
||||||
|
//私聊监听器
|
||||||
|
GlobalEventChannel.INSTANCE.filterIsInstance(FriendMessageEvent.class)
|
||||||
|
.filter(event -> true)
|
||||||
|
.registerListenerHost(new UserMessageListener());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
getLogger().info("ChatAI-InGroup-v2 disabled!");
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/plugin/constant/AIConstant.java
Normal file
17
src/main/java/plugin/constant/AIConstant.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package plugin.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SLHAF
|
||||||
|
*/
|
||||||
|
public class AIConstant {
|
||||||
|
/**
|
||||||
|
* 当前模型
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_MODEL = "当前模型";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误结果
|
||||||
|
*/
|
||||||
|
public static final String ERROR = "ERROR";
|
||||||
|
}
|
||||||
47
src/main/java/plugin/constant/ChatConstant.java
Normal file
47
src/main/java/plugin/constant/ChatConstant.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package plugin.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SLHAF
|
||||||
|
*/
|
||||||
|
public class ChatConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配含有图片信息的消息
|
||||||
|
*/
|
||||||
|
public static final String MATCH_MESSAGE = ".*[mirai:image:(https?://[\\w./?&=]+)].*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配图片信息
|
||||||
|
*/
|
||||||
|
public static final String MATCH_IMAGE = "\\[mirai:image:(.*?)]";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单次对话标志
|
||||||
|
*/
|
||||||
|
public static final String ONCE_MESSAGE_START = ".";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通对话标志
|
||||||
|
*/
|
||||||
|
public static final String NORMAL_MESSAGE_START = "@";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* code对话标志
|
||||||
|
*/
|
||||||
|
public static final String CODE_MESSAGE_START = "/c ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换模型
|
||||||
|
*/
|
||||||
|
public static final String CHANGE_MODEL = "切换模型";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有者
|
||||||
|
*/
|
||||||
|
public static final String OWNER = "owner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理消息
|
||||||
|
*/
|
||||||
|
public static final String CLEAR = "clear";
|
||||||
|
}
|
||||||
149
src/main/java/plugin/listener/UserMessageListener.java
Normal file
149
src/main/java/plugin/listener/UserMessageListener.java
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package plugin.listener;
|
||||||
|
|
||||||
|
import kotlin.coroutines.CoroutineContext;
|
||||||
|
import net.mamoe.mirai.event.EventHandler;
|
||||||
|
import net.mamoe.mirai.event.SimpleListenerHost;
|
||||||
|
import net.mamoe.mirai.event.events.FriendMessageEvent;
|
||||||
|
import net.mamoe.mirai.event.events.GroupMessageEvent;
|
||||||
|
import net.mamoe.mirai.message.data.At;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import plugin.constant.ChatConstant;
|
||||||
|
import plugin.utils.AIUtil;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static plugin.App.logger;
|
||||||
|
import static plugin.utils.ConfigUtil.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SLHAF
|
||||||
|
*/
|
||||||
|
public class UserMessageListener extends SimpleListenerHost {
|
||||||
|
|
||||||
|
public enum Methods {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正常对话
|
||||||
|
*/
|
||||||
|
NORMAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单次对话
|
||||||
|
*/
|
||||||
|
ONCE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预设:code
|
||||||
|
*/
|
||||||
|
CODE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未匹配
|
||||||
|
*/
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleException(@NotNull CoroutineContext context, @NotNull Throwable exception) {
|
||||||
|
super.handleException(context, exception);
|
||||||
|
logger.error(exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void groupMessageHandler(GroupMessageEvent event) {
|
||||||
|
//处理消息
|
||||||
|
String id = String.valueOf(event.getSender().getId());
|
||||||
|
String content = event.getMessage().contentToString();
|
||||||
|
String miraiCode = event.getMessage().serializeToMiraiCode();
|
||||||
|
String url = null;
|
||||||
|
if (miraiCode.matches(ChatConstant.MATCH_MESSAGE)) {
|
||||||
|
String regex = ChatConstant.MATCH_IMAGE;
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
|
||||||
|
// 创建Matcher对象
|
||||||
|
Matcher matcher = pattern.matcher(miraiCode);
|
||||||
|
|
||||||
|
// 查找并提取链接
|
||||||
|
if (matcher.find()) {
|
||||||
|
//提取第一个括号内的内容
|
||||||
|
url = matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Methods method = Methods.NONE;
|
||||||
|
|
||||||
|
if (content.contains(ChatConstant.CHANGE_MODEL) && !id.equals(config.get(ChatConstant.OWNER).substring(1))) {
|
||||||
|
event.getGroup().sendMessage(new At(Long.parseLong(id)).plus("没有权限!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//消息头处理
|
||||||
|
if (content.startsWith(ChatConstant.ONCE_MESSAGE_START)) {
|
||||||
|
content = content.substring(1);
|
||||||
|
method = Methods.ONCE;
|
||||||
|
} else if (content.startsWith(ChatConstant.NORMAL_MESSAGE_START + event.getBot().getId())) {
|
||||||
|
content = content.substring((ChatConstant.NORMAL_MESSAGE_START + event.getBot().getId()).length());
|
||||||
|
method = Methods.NORMAL;
|
||||||
|
} else if (content.startsWith(ChatConstant.CODE_MESSAGE_START)) {
|
||||||
|
content = content.substring(3);
|
||||||
|
method = Methods.CODE;
|
||||||
|
}
|
||||||
|
//消息内容处理
|
||||||
|
if (content.isBlank()) {
|
||||||
|
content = "在吗";
|
||||||
|
}
|
||||||
|
//发送请求并获取回应
|
||||||
|
String response = switch (method) {
|
||||||
|
case CODE -> AIUtil.chatCode(Long.valueOf(id), content, url);
|
||||||
|
case NORMAL -> AIUtil.chatNormal(Long.valueOf(id), content, url);
|
||||||
|
case ONCE -> AIUtil.chatOnce(content, url);
|
||||||
|
default -> "ERROR!";
|
||||||
|
};
|
||||||
|
event.getGroup().sendMessage(new At(Long.parseLong(id)).plus("\r\n").plus(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void friendMessageHandler(FriendMessageEvent event) {
|
||||||
|
String id = String.valueOf(event.getFriend().getId());
|
||||||
|
String content = event.getMessage().contentToString();
|
||||||
|
String miraiCode = event.getMessage().serializeToMiraiCode();
|
||||||
|
String url = null;
|
||||||
|
if (miraiCode.matches(ChatConstant.MATCH_MESSAGE)) {
|
||||||
|
String regex = ChatConstant.MATCH_IMAGE;
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
|
||||||
|
// 创建Matcher对象
|
||||||
|
Matcher matcher = pattern.matcher(miraiCode);
|
||||||
|
|
||||||
|
// 查找并提取链接
|
||||||
|
if (matcher.find()) {
|
||||||
|
//提取第一个括号内的内容
|
||||||
|
url = matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Methods method = Methods.NORMAL;
|
||||||
|
|
||||||
|
|
||||||
|
if (content.contains(ChatConstant.CHANGE_MODEL) && !id.equals(config.get(ChatConstant.OWNER).substring(1))) {
|
||||||
|
event.getFriend().sendMessage("没有权限!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理消息头
|
||||||
|
if (content.startsWith(ChatConstant.CODE_MESSAGE_START)) {
|
||||||
|
content = content.substring(3);
|
||||||
|
method = Methods.CODE;
|
||||||
|
} else if (content.startsWith(ChatConstant.ONCE_MESSAGE_START)) {
|
||||||
|
content = content.substring(1);
|
||||||
|
method = Methods.ONCE;
|
||||||
|
}
|
||||||
|
//发送请求并获取回应
|
||||||
|
String response = switch (method) {
|
||||||
|
case CODE -> AIUtil.chatCode(Long.valueOf(id), content, url);
|
||||||
|
case ONCE -> AIUtil.chatOnce(content, url);
|
||||||
|
case NORMAL -> AIUtil.chatNormal(Long.valueOf(id), content, url);
|
||||||
|
default -> "ERROR!";
|
||||||
|
};
|
||||||
|
event.getFriend().sendMessage(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
253
src/main/java/plugin/pojo/OCRDataInfo.java
Normal file
253
src/main/java/plugin/pojo/OCRDataInfo.java
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
package plugin.pojo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OCRDataInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* algo_version
|
||||||
|
*/
|
||||||
|
private String algo_version;
|
||||||
|
/**
|
||||||
|
* 文字内容-段落
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
/**
|
||||||
|
* height
|
||||||
|
*/
|
||||||
|
private int height;
|
||||||
|
/**
|
||||||
|
* orgHeight
|
||||||
|
*/
|
||||||
|
private int orgHeight;
|
||||||
|
/**
|
||||||
|
* orgWidth
|
||||||
|
*/
|
||||||
|
private int orgWidth;
|
||||||
|
/**
|
||||||
|
* prism_version
|
||||||
|
*/
|
||||||
|
private String prism_version;
|
||||||
|
/**
|
||||||
|
* prism_wnum
|
||||||
|
*/
|
||||||
|
private int prism_wnum;
|
||||||
|
/**
|
||||||
|
* prism_wordsInfo
|
||||||
|
*/
|
||||||
|
private List<PrismWordsInfoBean> prism_wordsInfo;
|
||||||
|
/**
|
||||||
|
* width
|
||||||
|
*/
|
||||||
|
private int width;
|
||||||
|
|
||||||
|
public String getAlgo_version() {
|
||||||
|
return algo_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgo_version(String algo_version) {
|
||||||
|
this.algo_version = algo_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int height) {
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrgHeight() {
|
||||||
|
return orgHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgHeight(int orgHeight) {
|
||||||
|
this.orgHeight = orgHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrgWidth() {
|
||||||
|
return orgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgWidth(int orgWidth) {
|
||||||
|
this.orgWidth = orgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrism_version() {
|
||||||
|
return prism_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrism_version(String prism_version) {
|
||||||
|
this.prism_version = prism_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrism_wnum() {
|
||||||
|
return prism_wnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrism_wnum(int prism_wnum) {
|
||||||
|
this.prism_wnum = prism_wnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PrismWordsInfoBean> getPrism_wordsInfo() {
|
||||||
|
return prism_wordsInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrism_wordsInfo(List<PrismWordsInfoBean> prism_wordsInfo) {
|
||||||
|
this.prism_wordsInfo = prism_wordsInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int width) {
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PrismWordsInfoBean {
|
||||||
|
/**
|
||||||
|
* angle
|
||||||
|
*/
|
||||||
|
private int angle;
|
||||||
|
/**
|
||||||
|
* direction
|
||||||
|
*/
|
||||||
|
private int direction;
|
||||||
|
/**
|
||||||
|
* height
|
||||||
|
*/
|
||||||
|
private int height;
|
||||||
|
/**
|
||||||
|
* pos
|
||||||
|
*/
|
||||||
|
private List<PosBean> pos;
|
||||||
|
/**
|
||||||
|
* prob
|
||||||
|
*/
|
||||||
|
private int prob;
|
||||||
|
/**
|
||||||
|
* width
|
||||||
|
*/
|
||||||
|
private int width;
|
||||||
|
/**
|
||||||
|
* 文字内容-行
|
||||||
|
*/
|
||||||
|
private String word;
|
||||||
|
/**
|
||||||
|
* x
|
||||||
|
*/
|
||||||
|
private int x;
|
||||||
|
/**
|
||||||
|
* y
|
||||||
|
*/
|
||||||
|
private int y;
|
||||||
|
|
||||||
|
public int getAngle() {
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAngle(int angle) {
|
||||||
|
this.angle = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDirection() {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirection(int direction) {
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int height) {
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PosBean> getPos() {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPos(List<PosBean> pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProb() {
|
||||||
|
return prob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProb(int prob) {
|
||||||
|
this.prob = prob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int width) {
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWord() {
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWord(String word) {
|
||||||
|
this.word = word;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PosBean {
|
||||||
|
/**
|
||||||
|
* x
|
||||||
|
*/
|
||||||
|
private int x;
|
||||||
|
/**
|
||||||
|
* y
|
||||||
|
*/
|
||||||
|
private int y;
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
240
src/main/java/plugin/utils/AIUtil.java
Normal file
240
src/main/java/plugin/utils/AIUtil.java
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
package plugin.utils;
|
||||||
|
|
||||||
|
import com.zhipu.oapi.ClientV4;
|
||||||
|
import com.zhipu.oapi.Constants;
|
||||||
|
import com.zhipu.oapi.service.v4.model.ChatCompletionRequest;
|
||||||
|
import com.zhipu.oapi.service.v4.model.ChatMessage;
|
||||||
|
import com.zhipu.oapi.service.v4.model.ChatMessageRole;
|
||||||
|
import com.zhipu.oapi.service.v4.model.ModelApiResponse;
|
||||||
|
import plugin.constant.AIConstant;
|
||||||
|
import plugin.constant.ChatConstant;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static plugin.App.logger;
|
||||||
|
import static plugin.utils.ConfigUtil.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SLHAF
|
||||||
|
*/
|
||||||
|
public class AIUtil {
|
||||||
|
private static final String apikey;
|
||||||
|
private static final ClientV4 client;
|
||||||
|
private static final String requestIdTemplate = "ChatAI_InGroup_v2";
|
||||||
|
private static final HashMap<Long, List<ChatMessage>> userMessagesNormal = new HashMap<>();
|
||||||
|
private static final HashMap<Long, Long> userLatestTimeNormal = new HashMap<>();
|
||||||
|
private static String modelNormal;
|
||||||
|
|
||||||
|
private static final HashMap<Long, List<ChatMessage>> userMessagesCode = new HashMap<>();
|
||||||
|
private static final HashMap<Long, Long> userLatestTimeCode = new HashMap<>();
|
||||||
|
private static String modelCode;
|
||||||
|
|
||||||
|
private static final Long checkTime, timeout;
|
||||||
|
|
||||||
|
static {
|
||||||
|
apikey = config.get("apikey");
|
||||||
|
client = new ClientV4.Builder(apikey).build();
|
||||||
|
modelNormal = config.get("model_normal");
|
||||||
|
modelCode = config.get("model_code");
|
||||||
|
checkTime = Long.valueOf(ConfigUtil.config.get("time_check").substring(1));
|
||||||
|
timeout = Long.valueOf(config.get("timeout").substring(1));
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(checkTime);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (userMessagesNormal) {
|
||||||
|
if (!userLatestTimeNormal.isEmpty()) {
|
||||||
|
//查看user最近时间,如果超过30min,则清理对应记录
|
||||||
|
userLatestTimeNormal.forEach((id, latestTime) -> {
|
||||||
|
Long currentTime = System.currentTimeMillis();
|
||||||
|
if (currentTime - latestTime > timeout && userMessagesNormal.containsKey(id)) {
|
||||||
|
userMessagesNormal.remove(id);
|
||||||
|
logger.info("Normal记录清理:" + id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (userMessagesCode) {
|
||||||
|
if (!userLatestTimeCode.isEmpty()) {
|
||||||
|
//查看user最近时间,如果超过30min,则清理对应记录
|
||||||
|
userLatestTimeCode.forEach((id, latestTime) -> {
|
||||||
|
Long currentTime = System.currentTimeMillis();
|
||||||
|
if (currentTime - latestTime > 30 * 60 * 1000 && userMessagesCode.containsKey(id)) {
|
||||||
|
userMessagesCode.remove(id);
|
||||||
|
logger.info("Code记录清理:" + id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
logger.info("清理线程启动");
|
||||||
|
logger.info("当前代码模型: " + modelCode);
|
||||||
|
logger.info("当前聊天模型: " + modelNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AIUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String chatCode(Long id, String content, String url) {
|
||||||
|
synchronized (userMessagesCode) {
|
||||||
|
if (ChatConstant.CLEAR.equals(content.replace(" ", ""))) {
|
||||||
|
userMessagesCode.remove(id);
|
||||||
|
return "消息记录已清空";
|
||||||
|
} else if (content.replace(" ", "").startsWith(ChatConstant.CHANGE_MODEL)) {
|
||||||
|
content = content.replace(" ", "");
|
||||||
|
modelCode = content.substring(4);
|
||||||
|
ConfigUtil.modelCodeChange(modelCode);
|
||||||
|
return "聊天模型切换为: " + modelCode;
|
||||||
|
} else if (AIConstant.CURRENT_MODEL.equals(content.replace(" ", ""))) {
|
||||||
|
return "当前模型为: " + modelCode;
|
||||||
|
}
|
||||||
|
//查看本次id是否有记录存在
|
||||||
|
if (!userMessagesCode.containsKey(id)) {
|
||||||
|
//创建消息list
|
||||||
|
List<ChatMessage> chatMessage = new ArrayList<>();
|
||||||
|
chatMessage.add(new ChatMessage(ChatMessageRole.SYSTEM.value(), "你是一位智能编程助手,你会为用户回答关于编程、代码、计算机方面的任何问题,并提供格式规范、可以执行、准确安全的代码,并在必要时提供详细的解释。 请用中文回答。"));
|
||||||
|
userMessagesCode.put(id, chatMessage);
|
||||||
|
}
|
||||||
|
return getChatResponse(id, content, url,modelCode, userMessagesCode, userLatestTimeCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String chatNormal(Long id, String content,String url) {
|
||||||
|
synchronized (userMessagesNormal) {
|
||||||
|
if (ChatConstant.CLEAR.equals(content.replace(" ", ""))) {
|
||||||
|
userMessagesNormal.remove(id);
|
||||||
|
return "消息记录已清空";
|
||||||
|
} else if (content.replace(" ", "").startsWith(ChatConstant.CHANGE_MODEL)) {
|
||||||
|
content = content.replace(" ", "");
|
||||||
|
modelNormal = content.substring(4);
|
||||||
|
ConfigUtil.modelNormalChange(modelNormal);
|
||||||
|
return "聊天模型切换为: " + modelNormal;
|
||||||
|
} else if (ChatConstant.CHANGE_MODEL.equals(content.replace(" ", ""))) {
|
||||||
|
return "当前模型为: " + modelNormal;
|
||||||
|
}
|
||||||
|
//查看本次id是否有记录存在
|
||||||
|
if (!userMessagesNormal.containsKey(id)) {
|
||||||
|
//创建消息list
|
||||||
|
List<ChatMessage> chatMessage = new ArrayList<>();
|
||||||
|
userMessagesNormal.put(id, chatMessage);
|
||||||
|
}
|
||||||
|
return getChatResponse(id, content, url,modelNormal, userMessagesNormal, userLatestTimeNormal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String chatOnce(String content,String url) {
|
||||||
|
if (content.replace(" ", "").startsWith(ChatConstant.CHANGE_MODEL)) {
|
||||||
|
content = content.replace(" ", "");
|
||||||
|
modelNormal = content.substring(4);
|
||||||
|
ConfigUtil.modelNormalChange(modelNormal);
|
||||||
|
return "代码模型切换为: " + modelNormal;
|
||||||
|
} else if (AIConstant.CURRENT_MODEL.equals(content.replace(" ", ""))) {
|
||||||
|
return "当前模型为: " + modelNormal;
|
||||||
|
}
|
||||||
|
String result = "";
|
||||||
|
if(url != null){
|
||||||
|
if (!OCRUtil.isSupported){
|
||||||
|
return "当前不支持文字识别,请检查阿里云OCR相关配置。";
|
||||||
|
} else {
|
||||||
|
String contentOfImage = OCRUtil.getContentOfImage(url);
|
||||||
|
if (contentOfImage == null){
|
||||||
|
result = "未识别出图片内容。";
|
||||||
|
}else if (AIConstant.ERROR.equals(contentOfImage)){
|
||||||
|
result = "识别图片内容出错,请查看控制台。";
|
||||||
|
}else {
|
||||||
|
content = content.replace("[图片]", "\r\n[" + contentOfImage + "]\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
|
||||||
|
List<ChatMessage> messages = new ArrayList<>();
|
||||||
|
messages.add(new ChatMessage(ChatMessageRole.USER.value(), content));
|
||||||
|
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
|
||||||
|
.model(modelNormal)
|
||||||
|
.stream(Boolean.FALSE)
|
||||||
|
.invokeMethod(Constants.invokeMethod)
|
||||||
|
.messages(messages)
|
||||||
|
.requestId(requestId)
|
||||||
|
.build();
|
||||||
|
ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
|
||||||
|
int code = invokeModelApiResp.getCode();
|
||||||
|
if (code == 200) {
|
||||||
|
printTokenInfo(invokeModelApiResp);
|
||||||
|
return invokeModelApiResp.getData().getChoices().get(0).getMessage().getContent().toString();
|
||||||
|
} else {
|
||||||
|
logger.warning("code: " + code);
|
||||||
|
logger.warning("ErrorCode:" + invokeModelApiResp.getData().getError().getCode());
|
||||||
|
logger.warning("msg:" + invokeModelApiResp.getData().getError().getMessage());
|
||||||
|
return invokeModelApiResp.getMsg()+"\r\n<"+result+">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getChatResponse(Long id, String content, String url,String model, HashMap<Long, List<ChatMessage>> userMessages, HashMap<Long, Long> userLatestTime) {
|
||||||
|
userLatestTime.put(id, System.currentTimeMillis());
|
||||||
|
|
||||||
|
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
|
||||||
|
//处理url内容
|
||||||
|
String result = "";
|
||||||
|
if(url != null){
|
||||||
|
if (!OCRUtil.isSupported){
|
||||||
|
logger.warning("unSupportedOCR");
|
||||||
|
return "当前不支持文字识别,请检查阿里云OCR相关配置。";
|
||||||
|
} else {
|
||||||
|
String contentOfImage = OCRUtil.getContentOfImage(url);
|
||||||
|
if (contentOfImage == null){
|
||||||
|
result = "未识别出图片内容。";
|
||||||
|
}else if (AIConstant.ERROR.equals(contentOfImage)){
|
||||||
|
result = "识别图片内容出错,请查看控制台。";
|
||||||
|
}else {
|
||||||
|
content = content.replace("[图片]", "\r\n[" + contentOfImage + "]\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("final content:" + content);
|
||||||
|
|
||||||
|
//添加消息
|
||||||
|
userMessages.get(id).add(new ChatMessage(ChatMessageRole.USER.value(), content));
|
||||||
|
|
||||||
|
//创建并发送请求
|
||||||
|
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
|
||||||
|
.model(model)
|
||||||
|
.stream(Boolean.FALSE)
|
||||||
|
.invokeMethod(Constants.invokeMethod)
|
||||||
|
.messages(userMessages.get(id))
|
||||||
|
.requestId(requestId)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
|
||||||
|
int code = invokeModelApiResp.getCode();
|
||||||
|
if (code == 200) {
|
||||||
|
printTokenInfo(invokeModelApiResp);
|
||||||
|
String response = invokeModelApiResp.getData().getChoices().get(0).getMessage().getContent().toString();
|
||||||
|
userMessages.get(id).add(new ChatMessage(ChatMessageRole.ASSISTANT.value(), response));
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
logger.warning("code: " + code);
|
||||||
|
logger.warning("ErrorCode:" + invokeModelApiResp.getData().getError().getCode());
|
||||||
|
logger.warning("msg:" + invokeModelApiResp.getData().getError().getMessage());
|
||||||
|
return invokeModelApiResp.getMsg()+"\r\n<"+result+">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printTokenInfo(ModelApiResponse invokeModelApiResp) {
|
||||||
|
int promptTokens = invokeModelApiResp.getData().getUsage().getPromptTokens();
|
||||||
|
int completionTokens = invokeModelApiResp.getData().getUsage().getCompletionTokens();
|
||||||
|
int totalTokens = invokeModelApiResp.getData().getUsage().getTotalTokens();
|
||||||
|
logger.info("prompt_tokens: " + promptTokens);
|
||||||
|
logger.info("completion_tokens: " + completionTokens);
|
||||||
|
logger.info("total_tokens: " + totalTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
src/main/java/plugin/utils/ConfigUtil.java
Normal file
85
src/main/java/plugin/utils/ConfigUtil.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package plugin.utils;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static plugin.App.logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SLHAF
|
||||||
|
*/
|
||||||
|
public class ConfigUtil {
|
||||||
|
public static HashMap<String, String> config;
|
||||||
|
private static final String CONFIG_PATH = "./config/ChatAIinGroup/config.yaml";
|
||||||
|
private static final Yaml yaml;
|
||||||
|
|
||||||
|
private ConfigUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static{
|
||||||
|
DumperOptions options = new DumperOptions();
|
||||||
|
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
|
yaml = new Yaml(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查配置
|
||||||
|
* @throws IOException 配置文件写入出错
|
||||||
|
*/
|
||||||
|
public static void load() throws IOException, ClassNotFoundException {
|
||||||
|
//检查配置文件
|
||||||
|
File file = new File(CONFIG_PATH);
|
||||||
|
if (!file.exists()) {
|
||||||
|
//创建配置文件
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
FileWriter writer = new FileWriter(file);
|
||||||
|
writer.write("apikey: \r\n");
|
||||||
|
writer.write("accessKeyId: \r\n");
|
||||||
|
writer.write("accessKeySecret: \r\n");
|
||||||
|
writer.write("owner: \r\n");
|
||||||
|
writer.write("model_normal: \r\n");
|
||||||
|
writer.write("model_code: \r\n");
|
||||||
|
writer.write("bot: \r\n");
|
||||||
|
writer.write("timeout: M3600000\r\n");
|
||||||
|
writer.write("time_check: M60000");
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
logger.warning("配置文件创建成功,请关闭后进行配置");
|
||||||
|
System.exit(0);
|
||||||
|
} else {
|
||||||
|
//读取配置文件
|
||||||
|
InputStream inputStream = new FileInputStream(CONFIG_PATH);
|
||||||
|
config = yaml.load(inputStream);
|
||||||
|
inputStream.close();
|
||||||
|
logger.info(config.toString());
|
||||||
|
logger.info("读取配置文件完毕");
|
||||||
|
Class.forName("plugin.utils.AIUtil");
|
||||||
|
Class.forName("plugin.utils.OCRUtil");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置改变(模型)
|
||||||
|
* @param modelName 模型名称
|
||||||
|
*/
|
||||||
|
public static void modelNormalChange(String modelName) {
|
||||||
|
try {
|
||||||
|
config.put("model_normal", modelName);
|
||||||
|
yaml.dump(config, new FileWriter(CONFIG_PATH));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void modelCodeChange(String modelName) {
|
||||||
|
try {
|
||||||
|
config.put("model_code", modelName);
|
||||||
|
yaml.dump(config, new FileWriter(CONFIG_PATH));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/main/java/plugin/utils/OCRUtil.java
Normal file
83
src/main/java/plugin/utils/OCRUtil.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package plugin.utils;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.aliyun.ocr_api20210707.Client;
|
||||||
|
import com.aliyun.ocr_api20210707.models.RecognizeAdvancedRequest;
|
||||||
|
import com.aliyun.ocr_api20210707.models.RecognizeAdvancedResponse;
|
||||||
|
import com.aliyun.tea.TeaException;
|
||||||
|
import com.aliyun.teaopenapi.models.Config;
|
||||||
|
import com.aliyun.teautil.models.RuntimeOptions;
|
||||||
|
import plugin.pojo.OCRDataInfo;
|
||||||
|
|
||||||
|
import static plugin.App.logger;
|
||||||
|
import static plugin.utils.ConfigUtil.config;
|
||||||
|
|
||||||
|
public class OCRUtil {
|
||||||
|
private static Client client;
|
||||||
|
public static boolean isSupported;
|
||||||
|
|
||||||
|
static {
|
||||||
|
//读取密钥
|
||||||
|
String accessKeyId = config.get("accessKeyId");
|
||||||
|
String accessKeySecret = config.get("accessKeySecret");
|
||||||
|
if (accessKeySecret == null || accessKeyId == null) {
|
||||||
|
isSupported = false;
|
||||||
|
logger.warning("未检测到阿里云OCR配置信息,图片文字识别将不可用。");
|
||||||
|
} else {
|
||||||
|
isSupported = true;
|
||||||
|
Config config = new Config()
|
||||||
|
.setAccessKeyId(accessKeyId)
|
||||||
|
.setAccessKeySecret(accessKeySecret);
|
||||||
|
config.endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
|
||||||
|
try {
|
||||||
|
client = new Client(config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("创建client出错");
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
}
|
||||||
|
logger.info("阿里云OCR已配置。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getContentOfImage(String url) {
|
||||||
|
//设置请求信息
|
||||||
|
RecognizeAdvancedRequest recognizeAdvancedRequest = new RecognizeAdvancedRequest()
|
||||||
|
.setUrl(url).setNeedRotate(Boolean.TRUE)
|
||||||
|
.setNeedRotate(Boolean.TRUE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
//发送请求并处理回应
|
||||||
|
RecognizeAdvancedResponse response = client.recognizeAdvancedWithOptions(recognizeAdvancedRequest, new RuntimeOptions());
|
||||||
|
OCRDataInfo ocrDataInfo = JSONUtil.toBean(response.getBody().getData(), OCRDataInfo.class);
|
||||||
|
if (!ocrDataInfo.getContent().isEmpty()) {
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
ocrDataInfo.getPrism_wordsInfo().forEach(prismWordsInfoBean -> {
|
||||||
|
str.append(prismWordsInfoBean.getWord());
|
||||||
|
if (!ocrDataInfo.getPrism_wordsInfo().get(ocrDataInfo.getPrism_wordsInfo().size() - 1).equals(prismWordsInfoBean)) {
|
||||||
|
str.append("\r\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return str.toString();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (TeaException error) {
|
||||||
|
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||||
|
// 错误 message
|
||||||
|
logger.error(error.getMessage());
|
||||||
|
// 诊断地址
|
||||||
|
logger.error(error.getData().get("Recommend").toString());
|
||||||
|
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||||
|
return "ERROR";
|
||||||
|
} catch (Exception _error) {
|
||||||
|
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||||
|
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||||
|
// 错误 message
|
||||||
|
logger.error(error.getMessage());
|
||||||
|
// 诊断地址
|
||||||
|
logger.error(error.getData().get("Recommend").toString());
|
||||||
|
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
plugin.App
|
||||||
Reference in New Issue
Block a user