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 net.lamgc.cgj.bot.boot.ApplicationBoot;
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.coolq.SpringCQApplication;
import net.lamgc.cgj.bot.framework.mirai.MiraiMain;
@ -88,19 +89,29 @@ public class Main {
@Command
public static void botMode(@Argument(name = "args", force = false) String argsStr) {
MiraiMain main = new MiraiMain();
main.init();
main.close();
try {
FrameworkManager.registerFramework(new MiraiMain()).join();
} catch (InterruptedException ignored) {
}
}
@Command
public static void consoleMode() throws IOException {
ConsoleMain.start();
public static void consoleMode() {
try {
FrameworkManager.registerFramework(new ConsoleMain()).join();
} catch (InterruptedException ignored) {
}
}
@Command
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

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.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.ConsoleMessageSenderFactory;
import net.lamgc.cgj.bot.message.MessageSenderBuilder;
@ -12,13 +15,18 @@ import org.jline.terminal.TerminalBuilder;
import org.slf4j.Logger;
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 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());
ApplicationBoot.initialBot();
LineReader lineReader = LineReaderBuilder.builder()
@ -45,7 +53,22 @@ public class ConsoleMain {
} catch (InterruptedException 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;
import com.google.common.base.Strings;
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.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.ContextStoppedEvent;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@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();
public void start(String argsStr) {
log.info("酷Q机器人根目录: {}", BotGlobal.getGlobal().getDataStoreDir().getPath());
Pattern pattern = Pattern.compile("/\\s*(\".+?\"|[^:\\s])+((\\s*:\\s*(\".+?\"|[^\\s])+)|)|(\".+?\"|[^\"\\s])+");
Matcher matcher = pattern.matcher(Strings.nullToEmpty(argsStr));
ArrayList<String> argsList = new ArrayList<>();
while (matcher.find()) {
argsList.add(matcher.group());
@Override
public void init(FrameworkResources resources) {
this.log = resources.getLogger();
}
String[] args = new String[argsList.size()];
argsList.toArray(args);
ConfigurableApplicationContext context = SpringApplication.run(SpringCQApplication.class, args);
public void run() {
log.info("酷Q机器人根目录: {}", BotGlobal.getGlobal().getDataStoreDir().getPath());
ConfigurableApplicationContext context = SpringApplication.run(SpringCQApplication.class);
registerShutdownHook(context);
try {
synchronized (quitLock) {
@ -42,22 +35,30 @@ public class SpringCQApplication {
} catch (InterruptedException e) {
log.warn("发生中断, 退出SpringCQ...", e);
}
context.stop();
context.close();
}
private void registerShutdownHook(ConfigurableApplicationContext context) {
context.addApplicationListener((ApplicationListener<ApplicationFailedEvent>)
event -> notifyThread());
event -> close());
context.addApplicationListener((ApplicationListener<ContextClosedEvent>)
event -> notifyThread());
event -> close());
context.addApplicationListener((ApplicationListener<ContextStoppedEvent>)
event -> notifyThread());
Runtime.getRuntime().addShutdownHook(new Thread(this::notifyThread));
event -> close());
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
private void notifyThread() {
public void close() {
synchronized (quitLock) {
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.BotGlobal;
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.MiraiMessageSenderFactory;
import net.lamgc.cgj.bot.message.MessageSenderBuilder;
@ -25,7 +27,7 @@ import java.io.*;
import java.util.Base64;
import java.util.Properties;
public class MiraiMain implements Closeable {
public class MiraiMain implements Framework {
private final Logger log = LoggerFactory.getLogger(MiraiMain.class);
@ -35,7 +37,8 @@ public class MiraiMain implements Closeable {
private final GroupMuteManager muteManager = new GroupMuteManager();
public void init() {
@Override
public void init(FrameworkResources resources) {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
try {
Class.forName(BotEventHandler.class.getName());
@ -72,6 +75,7 @@ public class MiraiMain implements Closeable {
bot = BotFactoryJvm.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")),
Base64.getDecoder().decode(botProperties.getProperty("bot.password", "")), configuration);
// TODO: 看看能不能单独订阅某个Bot?
Events.subscribeAlways(GroupMessageEvent.class, this::executeMessageEvent);
Events.subscribeAlways(FriendMessageEvent.class, this::executeMessageEvent);
Events.subscribeAlways(TempMessageEvent.class, this::executeMessageEvent);
@ -85,6 +89,12 @@ public class MiraiMain implements Closeable {
bot.join();
}
@Override
public void run() {
bot.login();
bot.join();
}
/**
* 处理消息事件
* @param message 消息事件对象
@ -107,6 +117,7 @@ public class MiraiMain implements Closeable {
/**
* 关闭机器人
*/
@Override
public synchronized void close() {
if(bot == null) {
return;
@ -117,4 +128,9 @@ public class MiraiMain implements Closeable {
log.warn("机器人已关闭.");
}
@Override
public String getFrameworkName() {
return "MiraiQQ";
}
}