commit c042c3cc61be033fccf6528f85d227c8cc5308e0 Author: slhaf <2998813882@qq.com> Date: Sat Sep 28 19:07:39 2024 +0800 1 diff --git a/src/main/java/plugin/App.java b/src/main/java/plugin/App.java new file mode 100644 index 0000000..70834e6 --- /dev/null +++ b/src/main/java/plugin/App.java @@ -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!"); + } +} \ No newline at end of file diff --git a/src/main/java/plugin/constant/AIConstant.java b/src/main/java/plugin/constant/AIConstant.java new file mode 100644 index 0000000..ee9aad1 --- /dev/null +++ b/src/main/java/plugin/constant/AIConstant.java @@ -0,0 +1,17 @@ +package plugin.constant; + +/** + * @author SLHAF + */ +public class AIConstant { + /** + * 当前模型 + */ + public static final String CURRENT_MODEL = "当前模型"; + + + /** + * 错误结果 + */ + public static final String ERROR = "ERROR"; +} diff --git a/src/main/java/plugin/constant/ChatConstant.java b/src/main/java/plugin/constant/ChatConstant.java new file mode 100644 index 0000000..d8b785d --- /dev/null +++ b/src/main/java/plugin/constant/ChatConstant.java @@ -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"; +} diff --git a/src/main/java/plugin/listener/UserMessageListener.java b/src/main/java/plugin/listener/UserMessageListener.java new file mode 100644 index 0000000..6803c3e --- /dev/null +++ b/src/main/java/plugin/listener/UserMessageListener.java @@ -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); + } +} diff --git a/src/main/java/plugin/pojo/OCRDataInfo.java b/src/main/java/plugin/pojo/OCRDataInfo.java new file mode 100644 index 0000000..ed0b4df --- /dev/null +++ b/src/main/java/plugin/pojo/OCRDataInfo.java @@ -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 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 getPrism_wordsInfo() { + return prism_wordsInfo; + } + + public void setPrism_wordsInfo(List 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 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 getPos() { + return pos; + } + + public void setPos(List 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; + } + } + } +} diff --git a/src/main/java/plugin/utils/AIUtil.java b/src/main/java/plugin/utils/AIUtil.java new file mode 100644 index 0000000..83de2d9 --- /dev/null +++ b/src/main/java/plugin/utils/AIUtil.java @@ -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> userMessagesNormal = new HashMap<>(); + private static final HashMap userLatestTimeNormal = new HashMap<>(); + private static String modelNormal; + + private static final HashMap> userMessagesCode = new HashMap<>(); + private static final HashMap 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 = 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 = 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 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> userMessages, HashMap 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); + } + +} diff --git a/src/main/java/plugin/utils/ConfigUtil.java b/src/main/java/plugin/utils/ConfigUtil.java new file mode 100644 index 0000000..3604b59 --- /dev/null +++ b/src/main/java/plugin/utils/ConfigUtil.java @@ -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 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); + } + } +} diff --git a/src/main/java/plugin/utils/OCRUtil.java b/src/main/java/plugin/utils/OCRUtil.java new file mode 100644 index 0000000..002806d --- /dev/null +++ b/src/main/java/plugin/utils/OCRUtil.java @@ -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"; + } + } +} diff --git a/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin b/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin new file mode 100644 index 0000000..5843b5d --- /dev/null +++ b/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin @@ -0,0 +1 @@ +plugin.App \ No newline at end of file