Compare commits

...

10 Commits

Author SHA1 Message Date
5637ef30d4 Merge branch 'master' of github.com:LamGC/ContentGrabbingJi into add-framework-interface 2020-07-15 14:40:38 +08:00
6fc7d8ad78 [Change] 调整Framework接口, 为框架提供特定数据存储目录;
[Change] Framework 适配FrameworkResources的更改, 将`getName`调整为`getIdentify`, 增加`getFrameworkName`方法用于获取框架名(可能会改为注解方式以防止更改);
[Change] FrameworkManager 适配Framework的更改, 增加`checkFramework`方法以对FrameworkName进行检查;
[Change] FrameworkManager, FrameworkResources 将FrameworkResources从FrameworkManager分离成单独的类;
[Change] ConsoleMain, MiraiMain, SpringCQApplication 适配相关更改;
2020-07-15 10:57:30 +08:00
9a7d16124a [Add] FrameworkManager 添加"frameworkSet"方法; 2020-07-03 10:05:20 +08:00
b754559187 Merge branch 'master' into add-framework-interface 2020-07-03 09:15:27 +08:00
a87735d9e0 [Change] Main, SpringCQApplication 移除仅与SpringCQ相关的Spring相关代码, 转移到SpringCQApplication;
[Change] FrameworkManager 调整"registerFramework"返回值, 调整"shutdownAllFramework"的过程;
2020-07-03 08:19:21 +08:00
6ec99dbf17 Merge branch 'update-SpringCQ' into add-framework-interface
# Conflicts:
#	src/main/java/net/lamgc/cgj/Main.java
#	src/main/java/net/lamgc/cgj/bot/framework/coolq/SpringCQApplication.java
2020-07-03 08:02:50 +08:00
1599a5325a [Change] Framework 调整"getName"方法的默认实现;
[Change] Main 调整pluginMode启动方式;
[Change] SpringCQApplication 实现Framework接口;
2020-07-03 08:01:31 +08:00
5c2b6b4ee5 Merge branch 'update-SpringCQ' into add-framework-interface 2020-07-03 07:48:16 +08:00
c2e8a07500 [Change] ConsoleMain, MiraiMain 尝试为平台框架实现Framework接口;
[Change] Framework 补充Javadoc, 增加"getName"方法的默认方法体;
[Change] FrameworkManager 调整向frameworkThreadGroup发起中断的时机;
2020-07-03 00:46:23 +08:00
1b937953c3 [Add] Framework, FrameworkManager 增加框架统一管理接口; 2020-07-03 00:27:54 +08:00
7 changed files with 237 additions and 35 deletions

View File

@ -7,6 +7,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import net.lamgc.cgj.bot.boot.ApplicationBoot; import net.lamgc.cgj.bot.boot.ApplicationBoot;
import net.lamgc.cgj.bot.boot.BotGlobal; import net.lamgc.cgj.bot.boot.BotGlobal;
import net.lamgc.cgj.bot.framework.FrameworkManager;
import net.lamgc.cgj.bot.framework.cli.ConsoleMain; import net.lamgc.cgj.bot.framework.cli.ConsoleMain;
import net.lamgc.cgj.bot.framework.coolq.SpringCQApplication; import net.lamgc.cgj.bot.framework.coolq.SpringCQApplication;
import net.lamgc.cgj.bot.framework.mirai.MiraiMain; import net.lamgc.cgj.bot.framework.mirai.MiraiMain;
@ -88,19 +89,29 @@ public class Main {
@Command @Command
public static void botMode(@Argument(name = "args", force = false) String argsStr) { public static void botMode(@Argument(name = "args", force = false) String argsStr) {
MiraiMain main = new MiraiMain(); try {
main.init(); FrameworkManager.registerFramework(new MiraiMain()).join();
main.close(); } catch (InterruptedException ignored) {
}
} }
@Command @Command
public static void consoleMode() throws IOException { public static void consoleMode() {
ConsoleMain.start(); try {
FrameworkManager.registerFramework(new ConsoleMain()).join();
} catch (InterruptedException ignored) {
}
} }
@Command @Command
public static void pluginMode(@Argument(name = "args", force = false) String argsStr) { public static void pluginMode(@Argument(name = "args", force = false) String argsStr) {
new SpringCQApplication().start(argsStr); try {
FrameworkManager.registerFramework(new SpringCQApplication()).join();
} catch (InterruptedException ignored) {
}
} }
@Command @Command

View File

@ -0,0 +1,41 @@
package net.lamgc.cgj.bot.framework;
public interface Framework {
/**
* 框架初始化方法
* @param resources 框架所分配到的资源.
* @throws Exception 当框架抛出异常时, 将不会继续运行框架.
* @see FrameworkResources
*/
void init(FrameworkResources resources) throws Exception;
/**
* 框架运行方法
* @throws Exception 当框架抛出异常时, 将会终止框架的所有活动.
*/
void run() throws Exception;
/**
* 关闭框架
* @throws Exception 即使该方法抛出异常, {@link FrameworkManager}依然会尝试向框架所属的线程发起中断, 以试图清除框架资源.
*/
void close() throws Exception;
/**
* 获取框架标识名.
* <p>可根据需要自行调整框架标识名.</p>
* @return 返回标识名.
*/
default String getIdentify() {
return this.getClass().getSimpleName() + "@" + Integer.toHexString(this.hashCode());
}
/**
* 获取框架名称.
* <p>框架名称不可更改.</p>
* @return 返回框架名称.
*/
String getFrameworkName();
}

View File

@ -0,0 +1,75 @@
package net.lamgc.cgj.bot.framework;
import org.slf4j.Logger;
import java.util.*;
import java.util.regex.Pattern;
public final class FrameworkManager {
private FrameworkManager() {}
private final static Map<Framework, FrameworkResources> resourcesMap = new HashMap<>();
private final static ThreadGroup frameworkRootGroup = new ThreadGroup("FrameworkRootGroup");
static {
Runtime.getRuntime()
.addShutdownHook(new Thread(FrameworkManager::shutdownAllFramework, "FrameworkManager-Shutdown"));
}
public static Thread registerFramework(Framework framework) {
checkFramework(framework);
FrameworkResources resources = new FrameworkResources(framework);
resourcesMap.put(framework, resources);
Thread frameworkThread = new Thread(resources.getFrameworkThreadGroup(),
() -> FrameworkManager.runFramework(framework), "FrameworkThread-" + framework.getIdentify());
frameworkThread.start();
return frameworkThread;
}
private static final Pattern FRAMEWORK_NAME_CHECK_PATTERN = Pattern.compile("^[A-Za-z0-9_\\-$]+$");
private static void checkFramework(Framework framework) {
if(!FRAMEWORK_NAME_CHECK_PATTERN.matcher(framework.getFrameworkName()).matches()) {
throw new IllegalStateException("Invalid Framework Name: " + framework.getFrameworkName());
}
}
public static Set<Framework> frameworkSet() {
return new HashSet<>(resourcesMap.keySet());
}
public static void shutdownAllFramework() {
for (Framework framework : resourcesMap.keySet()) {
FrameworkResources frameworkResources = resourcesMap.get(framework);
Logger frameworkLogger = frameworkResources.getLogger();
try {
frameworkLogger.info("正在关闭框架...");
framework.close();
frameworkLogger.info("框架已关闭.");
frameworkResources.getFrameworkThreadGroup().interrupt();
resourcesMap.remove(framework);
} catch(Throwable e) {
frameworkLogger.error("退出框架时发生异常", e);
}
}
}
static ThreadGroup getFrameworkRootGroup() {
return frameworkRootGroup;
}
private static void runFramework(Framework framework) {
FrameworkResources frameworkResources = resourcesMap.get(framework);
try {
framework.init(frameworkResources);
framework.run();
} catch(Throwable e) {
frameworkResources.getLogger().error("框架未捕获异常, 导致异常退出.", e);
} finally {
frameworkResources.getFrameworkThreadGroup().interrupt();
}
}
}

View File

@ -0,0 +1,35 @@
package net.lamgc.cgj.bot.framework;
import net.lamgc.cgj.bot.boot.BotGlobal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class FrameworkResources {
private final static File frameworkDataStoreRootDir = new File(BotGlobal.getGlobal().getDataStoreDir(),
"frameworks/");
private final ThreadGroup frameworkThreadGroup;
private final Logger logger;
private final File frameworkDataStoreDir;
public FrameworkResources(Framework framework) {
frameworkThreadGroup = new ThreadGroup(FrameworkManager.getFrameworkRootGroup(),
"Framework-" + framework.getIdentify());
frameworkDataStoreDir = new File(frameworkDataStoreRootDir, framework.getClass().getSimpleName());
logger = LoggerFactory.getLogger("Framework-" + framework.getIdentify());
}
ThreadGroup getFrameworkThreadGroup() {
return frameworkThreadGroup;
}
public Logger getLogger() {
return logger;
}
}

View File

@ -2,6 +2,9 @@ package net.lamgc.cgj.bot.framework.cli;
import net.lamgc.cgj.bot.boot.ApplicationBoot; import net.lamgc.cgj.bot.boot.ApplicationBoot;
import net.lamgc.cgj.bot.event.BotEventHandler; import net.lamgc.cgj.bot.event.BotEventHandler;
import net.lamgc.cgj.bot.framework.Framework;
import net.lamgc.cgj.bot.framework.FrameworkManager;
import net.lamgc.cgj.bot.framework.FrameworkResources;
import net.lamgc.cgj.bot.framework.cli.message.ConsoleMessageEvent; import net.lamgc.cgj.bot.framework.cli.message.ConsoleMessageEvent;
import net.lamgc.cgj.bot.framework.cli.message.ConsoleMessageSenderFactory; import net.lamgc.cgj.bot.framework.cli.message.ConsoleMessageSenderFactory;
import net.lamgc.cgj.bot.message.MessageSenderBuilder; import net.lamgc.cgj.bot.message.MessageSenderBuilder;
@ -12,13 +15,18 @@ import org.jline.terminal.TerminalBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean;
public class ConsoleMain { public class ConsoleMain implements Framework {
private final static Logger log = LoggerFactory.getLogger(ConsoleMain.class); private final static Logger log = LoggerFactory.getLogger(ConsoleMain.class);
private final AtomicBoolean quitState = new AtomicBoolean();
public static void start() throws IOException { @Override
public void init(FrameworkResources resources) { }
@Override
public void run() throws Exception {
MessageSenderBuilder.setCurrentMessageSenderFactory(new ConsoleMessageSenderFactory()); MessageSenderBuilder.setCurrentMessageSenderFactory(new ConsoleMessageSenderFactory());
ApplicationBoot.initialBot(); ApplicationBoot.initialBot();
LineReader lineReader = LineReaderBuilder.builder() LineReader lineReader = LineReaderBuilder.builder()
@ -45,7 +53,22 @@ public class ConsoleMain {
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.error("执行时发生中断", e); log.error("执行时发生中断", e);
} }
} while(true); } while(!quitState.get());
} }
@Override
public void close() {
quitState.set(true);
Thread.currentThread().getThreadGroup().interrupt();
}
@Override
public String getIdentify() {
return this.toString();
}
@Override
public String getFrameworkName() {
return "console";
}
} }

View File

@ -1,9 +1,9 @@
package net.lamgc.cgj.bot.framework.coolq; package net.lamgc.cgj.bot.framework.coolq;
import com.google.common.base.Strings;
import net.lamgc.cgj.bot.boot.BotGlobal; import net.lamgc.cgj.bot.boot.BotGlobal;
import net.lamgc.cgj.bot.framework.Framework;
import net.lamgc.cgj.bot.framework.FrameworkResources;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationFailedEvent;
@ -12,28 +12,21 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextStoppedEvent; import org.springframework.context.event.ContextStoppedEvent;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SpringBootApplication @SpringBootApplication
public class SpringCQApplication { public class SpringCQApplication implements Framework {
private final static Logger log = LoggerFactory.getLogger(SpringCQApplication.class); private Logger log;
private final Object quitLock = new Object(); private final Object quitLock = new Object();
public void start(String argsStr) { @Override
public void init(FrameworkResources resources) {
this.log = resources.getLogger();
}
public void run() {
log.info("酷Q机器人根目录: {}", BotGlobal.getGlobal().getDataStoreDir().getPath()); log.info("酷Q机器人根目录: {}", BotGlobal.getGlobal().getDataStoreDir().getPath());
Pattern pattern = Pattern.compile("/\\s*(\".+?\"|[^:\\s])+((\\s*:\\s*(\".+?\"|[^\\s])+)|)|(\".+?\"|[^\"\\s])+"); ConfigurableApplicationContext context = SpringApplication.run(SpringCQApplication.class);
Matcher matcher = pattern.matcher(Strings.nullToEmpty(argsStr));
ArrayList<String> argsList = new ArrayList<>();
while (matcher.find()) {
argsList.add(matcher.group());
}
String[] args = new String[argsList.size()];
argsList.toArray(args);
ConfigurableApplicationContext context = SpringApplication.run(SpringCQApplication.class, args);
registerShutdownHook(context); registerShutdownHook(context);
try { try {
synchronized (quitLock) { synchronized (quitLock) {
@ -42,22 +35,30 @@ public class SpringCQApplication {
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.warn("发生中断, 退出SpringCQ...", e); log.warn("发生中断, 退出SpringCQ...", e);
} }
context.stop();
context.close();
} }
private void registerShutdownHook(ConfigurableApplicationContext context) { private void registerShutdownHook(ConfigurableApplicationContext context) {
context.addApplicationListener((ApplicationListener<ApplicationFailedEvent>) context.addApplicationListener((ApplicationListener<ApplicationFailedEvent>)
event -> notifyThread()); event -> close());
context.addApplicationListener((ApplicationListener<ContextClosedEvent>) context.addApplicationListener((ApplicationListener<ContextClosedEvent>)
event -> notifyThread()); event -> close());
context.addApplicationListener((ApplicationListener<ContextStoppedEvent>) context.addApplicationListener((ApplicationListener<ContextStoppedEvent>)
event -> notifyThread()); event -> close());
Runtime.getRuntime().addShutdownHook(new Thread(this::notifyThread)); Runtime.getRuntime().addShutdownHook(new Thread(this::close));
} }
private void notifyThread() { public void close() {
synchronized (quitLock) { synchronized (quitLock) {
quitLock.notify(); quitLock.notify();
} }
} }
@Override
public String getFrameworkName() {
return "SpringCoolQ";
}
} }

View File

@ -3,6 +3,8 @@ package net.lamgc.cgj.bot.framework.mirai;
import net.lamgc.cgj.bot.boot.ApplicationBoot; import net.lamgc.cgj.bot.boot.ApplicationBoot;
import net.lamgc.cgj.bot.boot.BotGlobal; import net.lamgc.cgj.bot.boot.BotGlobal;
import net.lamgc.cgj.bot.event.BotEventHandler; import net.lamgc.cgj.bot.event.BotEventHandler;
import net.lamgc.cgj.bot.framework.Framework;
import net.lamgc.cgj.bot.framework.FrameworkResources;
import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageEvent; import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageEvent;
import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageSenderFactory; import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageSenderFactory;
import net.lamgc.cgj.bot.message.MessageSenderBuilder; import net.lamgc.cgj.bot.message.MessageSenderBuilder;
@ -25,7 +27,7 @@ import java.io.*;
import java.util.Base64; import java.util.Base64;
import java.util.Properties; import java.util.Properties;
public class MiraiMain implements Closeable { public class MiraiMain implements Framework {
private final Logger log = LoggerFactory.getLogger(MiraiMain.class); private final Logger log = LoggerFactory.getLogger(MiraiMain.class);
@ -35,7 +37,8 @@ public class MiraiMain implements Closeable {
private final GroupMuteManager muteManager = new GroupMuteManager(); private final GroupMuteManager muteManager = new GroupMuteManager();
public void init() { @Override
public void init(FrameworkResources resources) {
Runtime.getRuntime().addShutdownHook(new Thread(this::close)); Runtime.getRuntime().addShutdownHook(new Thread(this::close));
try { try {
Class.forName(BotEventHandler.class.getName()); Class.forName(BotEventHandler.class.getName());
@ -72,6 +75,7 @@ public class MiraiMain implements Closeable {
bot = BotFactoryJvm.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")), bot = BotFactoryJvm.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")),
Base64.getDecoder().decode(botProperties.getProperty("bot.password", "")), configuration); Base64.getDecoder().decode(botProperties.getProperty("bot.password", "")), configuration);
// TODO: 看看能不能单独订阅某个Bot?
Events.subscribeAlways(GroupMessageEvent.class, this::executeMessageEvent); Events.subscribeAlways(GroupMessageEvent.class, this::executeMessageEvent);
Events.subscribeAlways(FriendMessageEvent.class, this::executeMessageEvent); Events.subscribeAlways(FriendMessageEvent.class, this::executeMessageEvent);
Events.subscribeAlways(TempMessageEvent.class, this::executeMessageEvent); Events.subscribeAlways(TempMessageEvent.class, this::executeMessageEvent);
@ -85,6 +89,12 @@ public class MiraiMain implements Closeable {
bot.join(); bot.join();
} }
@Override
public void run() {
bot.login();
bot.join();
}
/** /**
* 处理消息事件 * 处理消息事件
* @param message 消息事件对象 * @param message 消息事件对象
@ -107,6 +117,7 @@ public class MiraiMain implements Closeable {
/** /**
* 关闭机器人 * 关闭机器人
*/ */
@Override
public synchronized void close() { public synchronized void close() {
if(bot == null) { if(bot == null) {
return; return;
@ -117,4 +128,9 @@ public class MiraiMain implements Closeable {
log.warn("机器人已关闭."); log.warn("机器人已关闭.");
} }
@Override
public String getFrameworkName() {
return "MiraiQQ";
}
} }