mirror of
https://github.com/LamGC/ContentGrabbingJi.git
synced 2025-04-30 06:37:36 +00:00
[Add] BotCode 增加用于操作机器人功能代码的工具类;
[Add] BotEventHandler, MessageEvent 将CQPluginMain消息处理部分迁移至BotEventHandler; [Add] SpringCQMessageEvent, MiraiMessageEvent 增加两个消息事件实现; [Add] MiraiMain 增加对 Mirai 框架的支持; [Add] Main 增加 Mirai 框架的支持;
This commit is contained in:
parent
7d843c3f77
commit
bc6aaa49a0
@ -7,6 +7,8 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import net.lamgc.cgj.bot.CQConfig;
|
||||||
|
import net.lamgc.cgj.bot.MiraiMain;
|
||||||
import net.lamgc.cgj.pixiv.*;
|
import net.lamgc.cgj.pixiv.*;
|
||||||
import net.lamgc.cgj.proxy.PixivAccessProxyServer;
|
import net.lamgc.cgj.proxy.PixivAccessProxyServer;
|
||||||
import net.lamgc.cgj.proxy.PixivLoginProxyServer;
|
import net.lamgc.cgj.proxy.PixivLoginProxyServer;
|
||||||
@ -49,12 +51,6 @@ public class Main {
|
|||||||
|
|
||||||
public static HttpHost proxy;
|
public static HttpHost proxy;
|
||||||
|
|
||||||
static {
|
|
||||||
if(!storeDir.exists() && !storeDir.mkdirs()) {
|
|
||||||
log.error("创建文件夹失败!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, ClassNotFoundException {
|
public static void main(String[] args) throws IOException, ClassNotFoundException {
|
||||||
ArgumentsProperties argsProp = new ArgumentsProperties(args);
|
ArgumentsProperties argsProp = new ArgumentsProperties(args);
|
||||||
if(argsProp.containsKey("proxy")) {
|
if(argsProp.containsKey("proxy")) {
|
||||||
@ -65,11 +61,16 @@ public class Main {
|
|||||||
proxy = null;
|
proxy = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!storeDir.exists() && !storeDir.mkdirs()) {
|
||||||
|
log.error("创建文件夹失败!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 需要修改参数名了, 大概改成类似于 workerDir这样的吧
|
||||||
if(argsProp.containsKey("cqRootDir")) {
|
if(argsProp.containsKey("cqRootDir")) {
|
||||||
log.info("cqRootDir: {}", argsProp.getValue("cqRootDir"));
|
log.info("cqRootDir: {}", argsProp.getValue("cqRootDir"));
|
||||||
System.setProperty("cgj.cqRootDir", argsProp.getValue("cqRootDir"));
|
System.setProperty("cgj.cqRootDir", argsProp.getValue("cqRootDir"));
|
||||||
} else {
|
} else {
|
||||||
log.info("未设置cqRootDir, 当前运行目录将作为酷Q机器人所在目录.");
|
log.warn("未设置cqRootDir, 当前运行目录将作为酷Q机器人所在目录.");
|
||||||
System.setProperty("cgj.cqRootDir", "./");
|
System.setProperty("cgj.cqRootDir", "./");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +98,11 @@ public class Main {
|
|||||||
ArgumentsRunner.run(Main.class, args);
|
ArgumentsRunner.run(Main.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
public static void botMode(@Argument(name = "args", force = false) String argsStr) {
|
||||||
|
new MiraiMain().init();
|
||||||
|
}
|
||||||
|
|
||||||
@Command
|
@Command
|
||||||
public static void pluginMode(@Argument(name = "args", force = false) String argsStr) {
|
public static void pluginMode(@Argument(name = "args", force = false) String argsStr) {
|
||||||
if(!System.getProperty("cgj.cqRootDir").endsWith("\\") && !System.getProperty("cgj.cqRootDir").endsWith("/")) {
|
if(!System.getProperty("cgj.cqRootDir").endsWith("\\") && !System.getProperty("cgj.cqRootDir").endsWith("/")) {
|
||||||
|
158
src/main/java/net/lamgc/cgj/bot/BotCode.java
Normal file
158
src/main/java/net/lamgc/cgj/bot/BotCode.java
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package net.lamgc.cgj.bot;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class BotCode {
|
||||||
|
|
||||||
|
public static BotCode build(String platformName, String functionName) {
|
||||||
|
return build(platformName, functionName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BotCode build(String platformName, String functionName, Map<String, String> parameter) {
|
||||||
|
if(Strings.isNullOrEmpty(platformName)) {
|
||||||
|
throw new IllegalArgumentException("platformName is Null or Empty.");
|
||||||
|
} else if(Strings.isNullOrEmpty(functionName)) {
|
||||||
|
throw new IllegalArgumentException("functionName is Null or Empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BotCode(platformName, functionName, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Pattern codePattern = Pattern.compile("\\[.*?:.*?]");
|
||||||
|
public static BotCode parse(String str) {
|
||||||
|
if (!codePattern.matcher(str).matches()) {
|
||||||
|
throw new IllegalArgumentException("invalid string input: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = str.substring(1, str.length() - 1);
|
||||||
|
String[] texts = text.split(",");
|
||||||
|
if(texts.length <= 0) {
|
||||||
|
throw new IllegalArgumentException("invalid string input: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] keys = texts[0].split(":", 2);
|
||||||
|
if(keys.length != 2) {
|
||||||
|
throw new IllegalArgumentException("invalid string input: " + str);
|
||||||
|
}
|
||||||
|
if(Strings.isNullOrEmpty(keys[0]) || Strings.isNullOrEmpty(keys[1])) {
|
||||||
|
throw new IllegalArgumentException("invalid string input: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, String> param = new HashMap<>(texts.length - 1);
|
||||||
|
for (int i = 1; i < texts.length; i++) {
|
||||||
|
String[] items = texts[i].split("=");
|
||||||
|
if(items.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.put(items[0].trim(), items[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BotCode(keys[0], keys[1], param);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String platformName;
|
||||||
|
private String functionName;
|
||||||
|
private Hashtable<String, String> parameter = new Hashtable<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个机器功能码
|
||||||
|
* @param platformName 平台代码
|
||||||
|
* @param functionName 功能名
|
||||||
|
* @param parameter 参数Map
|
||||||
|
*/
|
||||||
|
private BotCode(String platformName, String functionName, Map<String, String> parameter) {
|
||||||
|
this.platformName = platformName;
|
||||||
|
this.functionName = functionName;
|
||||||
|
if(parameter != null && !parameter.isEmpty()) {
|
||||||
|
this.parameter.putAll(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置平台代码
|
||||||
|
* @param platformName 欲设置的平台代码
|
||||||
|
*/
|
||||||
|
public void setPlatformName(String platformName) {
|
||||||
|
this.platformName = platformName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取平台代码
|
||||||
|
* @return 当前设置的平台代码
|
||||||
|
*/
|
||||||
|
public String getPlatformName() {
|
||||||
|
return platformName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置功能名
|
||||||
|
* @param functionName 欲设置的新功能名
|
||||||
|
*/
|
||||||
|
public void setFunctionName(String functionName) {
|
||||||
|
this.functionName = functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取功能名
|
||||||
|
* @return 返回当前设置的功能名
|
||||||
|
*/
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加参数
|
||||||
|
* @param key 参数键
|
||||||
|
* @param value 参数值
|
||||||
|
*/
|
||||||
|
public void addParameter(String key, String value) {
|
||||||
|
parameter.put(key.trim(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查一个参数项是否存在
|
||||||
|
* @param key 欲查询其存在的参数项所属键
|
||||||
|
* @return 返回true则参数项存在
|
||||||
|
*/
|
||||||
|
public boolean containsParameter(String key) {
|
||||||
|
return parameter.containsKey(key.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数项的值
|
||||||
|
* @param key 欲获取参数值的参数项所属键
|
||||||
|
* @return 返回参数项的参数值
|
||||||
|
*/
|
||||||
|
public String getParameter(String key) {
|
||||||
|
return parameter.get(key.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有参数项的参数键
|
||||||
|
* @return 返回存储了所有参数键的Set对象
|
||||||
|
*/
|
||||||
|
public Set<String> parameterKeys() {
|
||||||
|
return new HashSet<>(parameter.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将BotCode对象转为功能代码文本
|
||||||
|
* 格式:
|
||||||
|
* <pre>[Platform:Function, parameter...]</pre>
|
||||||
|
* @return 功能代码文本
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder("[" + platformName + ":" + functionName);
|
||||||
|
if(!parameter.isEmpty()) {
|
||||||
|
builder.append(", ");
|
||||||
|
parameter.forEach((key, value) -> builder.append(key).append("=").append(value).append(", "));
|
||||||
|
builder.replace(builder.length() - 2, builder.length(), "");
|
||||||
|
}
|
||||||
|
return builder.append("]").toString();
|
||||||
|
}
|
||||||
|
}
|
52
src/main/java/net/lamgc/cgj/bot/MiraiMain.java
Normal file
52
src/main/java/net/lamgc/cgj/bot/MiraiMain.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package net.lamgc.cgj.bot;
|
||||||
|
|
||||||
|
import net.lamgc.cgj.bot.event.BotEventHandler;
|
||||||
|
import net.lamgc.cgj.bot.event.MiraiMessageEvent;
|
||||||
|
import net.mamoe.mirai.Bot;
|
||||||
|
import net.mamoe.mirai.japt.Events;
|
||||||
|
import net.mamoe.mirai.message.FriendMessage;
|
||||||
|
import net.mamoe.mirai.message.GroupMessage;
|
||||||
|
import net.mamoe.mirai.qqandroid.QQAndroid;
|
||||||
|
import net.mamoe.mirai.utils.BotConfiguration;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class MiraiMain implements Closeable {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(this.toString());
|
||||||
|
|
||||||
|
private Bot bot;
|
||||||
|
|
||||||
|
private final static Properties botProperties = new Properties();
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
try {
|
||||||
|
Class.forName(BotEventHandler.class.getName());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
log.error("加载BotEventHandler时发生异常", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File botPropFile = new File("./bot.properties");
|
||||||
|
try (Reader reader = new BufferedReader(new FileReader(botPropFile))) {
|
||||||
|
botProperties.load(reader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("机器人配置文件读取失败!", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bot = QQAndroid.INSTANCE.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")), botProperties.getProperty("bot.password", ""), new BotConfiguration());
|
||||||
|
Events.subscribeAlways(GroupMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg)));
|
||||||
|
Events.subscribeAlways(FriendMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg)));
|
||||||
|
bot.login();
|
||||||
|
bot.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
bot.close(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
174
src/main/java/net/lamgc/cgj/bot/event/BotEventHandler.java
Normal file
174
src/main/java/net/lamgc/cgj/bot/event/BotEventHandler.java
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package net.lamgc.cgj.bot.event;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import net.lamgc.cgj.bot.BotAdminCommandProcess;
|
||||||
|
import net.lamgc.cgj.bot.BotCommandProcess;
|
||||||
|
import net.lamgc.cgj.util.DateParser;
|
||||||
|
import net.lamgc.cgj.util.PagesQualityParser;
|
||||||
|
import net.lamgc.utils.base.runner.ArgumentsRunner;
|
||||||
|
import net.lamgc.utils.base.runner.ArgumentsRunnerConfig;
|
||||||
|
import net.lamgc.utils.base.runner.exception.DeveloperRunnerException;
|
||||||
|
import net.lamgc.utils.base.runner.exception.NoSuchCommandException;
|
||||||
|
import net.lamgc.utils.base.runner.exception.ParameterNoFoundException;
|
||||||
|
import net.lamgc.utils.event.EventExecutor;
|
||||||
|
import net.lamgc.utils.event.EventHandler;
|
||||||
|
import net.lamgc.utils.event.EventObject;
|
||||||
|
import net.lamgc.utils.event.EventUncaughtExceptionHandler;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import redis.clients.jedis.JedisPool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class BotEventHandler implements EventHandler {
|
||||||
|
|
||||||
|
public final static String COMMAND_PREFIX = ".cgj";
|
||||||
|
|
||||||
|
private final ArgumentsRunner processRunner;
|
||||||
|
private final ArgumentsRunner adminRunner;
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger("BotEventHandler@" + Integer.toHexString(this.hashCode()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有缓存共用的JedisPool
|
||||||
|
*/
|
||||||
|
private final static URI redisServerUri = URI.create("redis://" + System.getProperty("cgj.redisAddress"));
|
||||||
|
public final static JedisPool redisServer = new JedisPool(redisServerUri.getHost(), redisServerUri.getPort() == -1 ? 6379 : redisServerUri.getPort());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息事件执行器
|
||||||
|
*/
|
||||||
|
public final static EventExecutor executor = new EventExecutor(new ThreadPoolExecutor(
|
||||||
|
(int) Math.ceil(Runtime.getRuntime().availableProcessors() / 2F),
|
||||||
|
Runtime.getRuntime().availableProcessors(),
|
||||||
|
30L,
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
new LinkedBlockingQueue<>(1536),
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("CommandProcess-%d")
|
||||||
|
.build()
|
||||||
|
));
|
||||||
|
|
||||||
|
static {
|
||||||
|
executor.setEventUncaughtExceptionHandler(new EventUncaughtExceptionHandler() {
|
||||||
|
private final Logger log = LoggerFactory.getLogger("EventUncaughtExceptionHandler");
|
||||||
|
@Override
|
||||||
|
public void exceptionHandler(Thread executeThread, EventHandler handler, Method handlerMethod, EventObject event, Throwable cause) {
|
||||||
|
log.error("发生未捕获异常:\nThread:{}, EventHandler: {}, HandlerMethod: {}, EventObject: {}\n{}",
|
||||||
|
executeThread.getName(),
|
||||||
|
handler.toString(),
|
||||||
|
handlerMethod.getName(),
|
||||||
|
event.toString(),
|
||||||
|
Throwables.getStackTraceAsString(cause));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
executor.addHandler(new BotEventHandler());
|
||||||
|
executor.addHandler(new TestEventHandler());
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
LoggerFactory.getLogger("BotEventHandler@Static").error("添加Handler时发生异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BotEventHandler() {
|
||||||
|
ArgumentsRunnerConfig runnerConfig = new ArgumentsRunnerConfig();
|
||||||
|
runnerConfig.setUseDefaultValueInsteadOfException(true);
|
||||||
|
runnerConfig.setCommandIgnoreCase(true);
|
||||||
|
runnerConfig.addStringParameterParser(new DateParser(new SimpleDateFormat("yyyy-MM-dd")));
|
||||||
|
runnerConfig.addStringParameterParser(new PagesQualityParser());
|
||||||
|
|
||||||
|
log.debug("DateParser添加情况: {}", runnerConfig.hasStringParameterParser(Date.class));
|
||||||
|
|
||||||
|
processRunner = new ArgumentsRunner(BotCommandProcess.class, runnerConfig);
|
||||||
|
adminRunner = new ArgumentsRunner(BotAdminCommandProcess.class, runnerConfig);
|
||||||
|
|
||||||
|
BotCommandProcess.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processMessage(MessageEvent event) {
|
||||||
|
String msg = event.getMessage();
|
||||||
|
if(!match(msg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile("/\\s*(\".+?\"|[^:\\s])+((\\s*:\\s*(\".+?\"|[^\\s])+)|)|(\".+?\"|[^\"\\s])+");
|
||||||
|
Matcher matcher = pattern.matcher(Strings.nullToEmpty(msg));
|
||||||
|
ArrayList<String> argsList = new ArrayList<>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
String arg = matcher.group();
|
||||||
|
int startIndex = 0;
|
||||||
|
int endIndex = arg.length();
|
||||||
|
if(arg.startsWith("\"")) {
|
||||||
|
while(arg.indexOf("\"", startIndex) == startIndex) {
|
||||||
|
startIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(arg.endsWith("\"")) {
|
||||||
|
while(arg.charAt(endIndex - 1) == '\"') {
|
||||||
|
endIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argsList.add(arg.substring(startIndex, endIndex));
|
||||||
|
}
|
||||||
|
String[] args = new String[argsList.size()];
|
||||||
|
argsList.toArray(args);
|
||||||
|
log.debug("传入参数: {}", Arrays.toString(args));
|
||||||
|
|
||||||
|
log.info("正在处理命令...");
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
Object result;
|
||||||
|
try {
|
||||||
|
if(msg.toLowerCase().startsWith(COMMAND_PREFIX + "admin")) {
|
||||||
|
if(!String.valueOf(event.getFromQQ()).equals(BotCommandProcess.globalProp.getProperty("admin.adminId"))) {
|
||||||
|
event.sendMessage("你没有执行该命令的权限!");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
result = adminRunner.run(new BotAdminCommandProcess(), args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = processRunner.run(args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length));
|
||||||
|
}
|
||||||
|
} catch(NoSuchCommandException e) {
|
||||||
|
result = "没有这个命令!请使用“.cgj”查看帮助说明!";
|
||||||
|
} catch(ParameterNoFoundException e) {
|
||||||
|
result = "命令缺少参数: " + e.getParameterName();
|
||||||
|
} catch(DeveloperRunnerException e) {
|
||||||
|
log.error("执行命令时发生异常", e);
|
||||||
|
result = "命令执行时发生错误,无法完成!";
|
||||||
|
}
|
||||||
|
log.info("命令处理完成.(耗时: {}ms)", System.currentTimeMillis() - time);
|
||||||
|
if(Objects.requireNonNull(result) instanceof String) {
|
||||||
|
try {
|
||||||
|
event.sendMessage((String) result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送消息时发生异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("命令反馈完成.(耗时: {}ms)", System.currentTimeMillis() - time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查消息是否需要提交
|
||||||
|
* @param message 要检查的消息
|
||||||
|
* @return 如果为true则提交
|
||||||
|
*/
|
||||||
|
public static boolean match(String message) {
|
||||||
|
return message.startsWith(COMMAND_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
src/main/java/net/lamgc/cgj/bot/event/MessageEvent.java
Normal file
33
src/main/java/net/lamgc/cgj/bot/event/MessageEvent.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package net.lamgc.cgj.bot.event;
|
||||||
|
|
||||||
|
import net.lamgc.utils.event.EventObject;
|
||||||
|
|
||||||
|
public abstract class MessageEvent implements EventObject {
|
||||||
|
|
||||||
|
private final long fromGroup;
|
||||||
|
private final long fromQQ;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
public MessageEvent(long fromGroup, long fromQQ, String message) {
|
||||||
|
this.fromGroup = fromGroup;
|
||||||
|
this.fromQQ = fromQQ;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract int sendMessage(final String message);
|
||||||
|
|
||||||
|
public abstract Object getRawMessage();
|
||||||
|
|
||||||
|
public long getFromGroup() {
|
||||||
|
return fromGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFromQQ() {
|
||||||
|
return fromQQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
184
src/main/java/net/lamgc/cgj/bot/event/MiraiMessageEvent.java
Normal file
184
src/main/java/net/lamgc/cgj/bot/event/MiraiMessageEvent.java
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
package net.lamgc.cgj.bot.event;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import net.lamgc.cgj.bot.BotCode;
|
||||||
|
import net.lamgc.cgj.bot.cache.CacheStore;
|
||||||
|
import net.lamgc.cgj.bot.cache.HotDataCacheStore;
|
||||||
|
import net.lamgc.cgj.bot.cache.LocalHashCacheStore;
|
||||||
|
import net.lamgc.cgj.bot.cache.StringRedisCacheStore;
|
||||||
|
import net.mamoe.mirai.message.ContactMessage;
|
||||||
|
import net.mamoe.mirai.message.FriendMessage;
|
||||||
|
import net.mamoe.mirai.message.GroupMessage;
|
||||||
|
import net.mamoe.mirai.message.data.CombinedMessage;
|
||||||
|
import net.mamoe.mirai.message.data.Image;
|
||||||
|
import net.mamoe.mirai.message.data.Message;
|
||||||
|
import net.mamoe.mirai.message.data.MessageUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class MiraiMessageEvent extends MessageEvent {
|
||||||
|
|
||||||
|
private final ContactMessage messageObject;
|
||||||
|
private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(this.hashCode()));
|
||||||
|
private final static CacheStore<String> imageIdCache = new HotDataCacheStore<>(
|
||||||
|
new StringRedisCacheStore(BotEventHandler.redisServer, "mirai.imageId"),
|
||||||
|
new LocalHashCacheStore<>(),
|
||||||
|
5400000, 1800000);
|
||||||
|
|
||||||
|
|
||||||
|
public MiraiMessageEvent(ContactMessage message) {
|
||||||
|
super(message instanceof GroupMessage ? ((GroupMessage) message).getGroup().getId() : 0,
|
||||||
|
message.getSender().getId(), message.getMessage().toString());
|
||||||
|
this.messageObject = Objects.requireNonNull(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendMessage(final String message) {
|
||||||
|
log.debug("处理前的消息内容:\n{}", message);
|
||||||
|
Message msgBody = processMessage(Objects.requireNonNull(message));
|
||||||
|
log.debug("处理后的消息内容(可能出现乱序的情况, 但实际上顺序是没问题的):\n{}", msgBody);
|
||||||
|
if(getFromGroup() == 0) {
|
||||||
|
FriendMessage msgObject = (FriendMessage) messageObject;
|
||||||
|
//FIXME(LamGC, 2020.04.10): 当前 Mirai 不支持私聊长文本, 所以发生异常是正常情况...
|
||||||
|
msgObject.getSender().sendMessage(msgBody);
|
||||||
|
} else {
|
||||||
|
GroupMessage msgObject = (GroupMessage) messageObject;
|
||||||
|
msgObject.getGroup().sendMessage(msgBody);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getRawMessage() {
|
||||||
|
return messageObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Pattern cqCodePattern = Pattern.compile("\\[.*?:.*?]");
|
||||||
|
private Message processMessage(final String message) {
|
||||||
|
Matcher matcher = cqCodePattern.matcher(message);
|
||||||
|
ArrayList<String> cqCode = new ArrayList<>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
cqCode.add(matcher.group());
|
||||||
|
}
|
||||||
|
String[] texts = message
|
||||||
|
.replaceAll("&", "&38")
|
||||||
|
.replaceAll("\\{", "&" + Character.getNumericValue('{'))
|
||||||
|
.replaceAll(cqCodePattern.pattern(), "|{BotCode}|")
|
||||||
|
.replaceAll("&" + Character.getNumericValue('{'), "{")
|
||||||
|
.replaceAll("&38", "&")
|
||||||
|
.split("\\|");
|
||||||
|
|
||||||
|
CombinedMessage chain = MessageUtils.newChain().plus("");
|
||||||
|
int codeIndex = 0;
|
||||||
|
for(String text : texts) {
|
||||||
|
if(text.equals("{BotCode}")) {
|
||||||
|
BotCode code;
|
||||||
|
try {
|
||||||
|
code = BotCode.parse(cqCode.get(codeIndex++));
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
log.warn("解析待发送消息内的BotCode时发生异常, 请检查错误格式BotCode的来源并尽快排错!", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
chain = chain.plus(processBotCode(code));
|
||||||
|
} else {
|
||||||
|
chain = chain.plus(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Message processBotCode(BotCode code) {
|
||||||
|
switch(code.getFunctionName().toLowerCase()) {
|
||||||
|
case "image":
|
||||||
|
if(code.containsParameter("id")) {
|
||||||
|
return MessageUtils.newImage(code.getParameter("id"));
|
||||||
|
} else if(code.containsParameter("absolutePath")) {
|
||||||
|
return uploadImage(code);
|
||||||
|
} else {
|
||||||
|
return MessageUtils.newChain("(参数不存在)");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.warn("解析到不支持的BotCode: {}", code);
|
||||||
|
return MessageUtils.newChain("(不支持的BotCode)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image uploadImage(BotCode code) {
|
||||||
|
log.debug("传入BotCode信息:\n{}", code);
|
||||||
|
String absolutePath = code.getParameter("absolutePath");
|
||||||
|
if(Strings.isNullOrEmpty(absolutePath)) {
|
||||||
|
throw new IllegalArgumentException("BotCode does not contain the absolutePath parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
String imageName = code.getParameter("imageName");
|
||||||
|
if(!Strings.isNullOrEmpty(imageName)) {
|
||||||
|
Image image = null;
|
||||||
|
imageName = (getMessageSource() + "." + imageName).intern();
|
||||||
|
if(!imageIdCache.exists(imageName) ||
|
||||||
|
Strings.nullToEmpty(code.getParameter("updateCache")).equalsIgnoreCase("true")) {
|
||||||
|
synchronized (imageName) {
|
||||||
|
if(!imageIdCache.exists(imageName) ||
|
||||||
|
Strings.nullToEmpty(code.getParameter("updateCache")) .equalsIgnoreCase("true")) {
|
||||||
|
log.debug("imageName [{}] 缓存失效或强制更新, 正在更新缓存...", imageName);
|
||||||
|
image = uploadImage0(new File(absolutePath));
|
||||||
|
if(Objects.isNull(image)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cacheExpireAt;
|
||||||
|
long expireTime = 0;
|
||||||
|
if(!Strings.isNullOrEmpty(cacheExpireAt = code.getParameter("cacheExpireAt"))) {
|
||||||
|
try {
|
||||||
|
expireTime = Integer.parseInt(cacheExpireAt);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("BotCode中的cacheExpireAt参数无效: {}", cacheExpireAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imageIdCache.update(imageName, image.getImageId(), expireTime);
|
||||||
|
log.info("imageName [{}] 缓存更新完成.(有效时间: {})", imageName, expireTime);
|
||||||
|
} else {
|
||||||
|
log.debug("ImageName: [{}] 缓存命中.", imageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("ImageName: [{}] 缓存命中.", imageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
String cache = imageIdCache.getCache(imageName);
|
||||||
|
return image != null ? image : MessageUtils.newImage(cache);
|
||||||
|
} else {
|
||||||
|
log.debug("未设置imageName, 无法使用缓存.");
|
||||||
|
return uploadImage0(new File(absolutePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image uploadImage0(File imageFile) {
|
||||||
|
if(messageObject instanceof FriendMessage) {
|
||||||
|
return messageObject.getSender().uploadImage(imageFile);
|
||||||
|
} else if(messageObject instanceof GroupMessage) {
|
||||||
|
return ((GroupMessage) messageObject).getGroup().uploadImage(imageFile);
|
||||||
|
} else {
|
||||||
|
log.warn("未知的ContactMessage类型: " + messageObject.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessageSource() {
|
||||||
|
if(messageObject instanceof FriendMessage) {
|
||||||
|
return "Private";
|
||||||
|
} else if(messageObject instanceof GroupMessage) {
|
||||||
|
return "Group";
|
||||||
|
} else {
|
||||||
|
log.warn("未知的ContactMessage类型: " + messageObject.toString());
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package net.lamgc.cgj.bot.event;
|
||||||
|
|
||||||
|
import net.lz1998.cq.event.message.CQDiscussMessageEvent;
|
||||||
|
import net.lz1998.cq.event.message.CQGroupMessageEvent;
|
||||||
|
import net.lz1998.cq.event.message.CQMessageEvent;
|
||||||
|
import net.lz1998.cq.robot.CoolQ;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SpringCQMessageEvent extends MessageEvent {
|
||||||
|
|
||||||
|
private final static int TYPE_PRIVATE = 0;
|
||||||
|
private final static int TYPE_GROUP = 1;
|
||||||
|
private final static int TYPE_DISCUSS = 2;
|
||||||
|
|
||||||
|
private final CoolQ cq;
|
||||||
|
private final int type;
|
||||||
|
private final CQMessageEvent messageEvent;
|
||||||
|
|
||||||
|
public SpringCQMessageEvent(CoolQ cq, CQMessageEvent messageEvent) {
|
||||||
|
super(messageEvent instanceof CQGroupMessageEvent ? (
|
||||||
|
(CQGroupMessageEvent) messageEvent).getGroupId() :
|
||||||
|
messageEvent instanceof CQDiscussMessageEvent ?
|
||||||
|
((CQDiscussMessageEvent) messageEvent).getDiscussId() : 0,
|
||||||
|
messageEvent.getUserId(), messageEvent.getMessage());
|
||||||
|
this.cq = Objects.requireNonNull(cq);
|
||||||
|
if(messageEvent instanceof CQGroupMessageEvent) {
|
||||||
|
type = TYPE_GROUP;
|
||||||
|
} else if (messageEvent instanceof CQDiscussMessageEvent) {
|
||||||
|
type = TYPE_DISCUSS;
|
||||||
|
} else {
|
||||||
|
type = TYPE_PRIVATE;
|
||||||
|
}
|
||||||
|
this.messageEvent = messageEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendMessage(final String message) {
|
||||||
|
switch(type) {
|
||||||
|
case TYPE_PRIVATE:
|
||||||
|
return cq.sendPrivateMsg(getFromQQ(), message, false).getData().getMessageId();
|
||||||
|
case TYPE_GROUP:
|
||||||
|
case TYPE_DISCUSS:
|
||||||
|
return cq.sendGroupMsg(getFromGroup(), message, false).getData().getMessageId();
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getRawMessage() {
|
||||||
|
return messageEvent;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user