feat: adding command system

This commit is contained in:
wzp 2024-08-01 17:45:28 +08:00
parent fa81e235bb
commit 932f903455
19 changed files with 436 additions and 29 deletions

2
.idea/compiler.xml generated
View File

@ -10,6 +10,6 @@
<module name="MyBot.main" /> <module name="MyBot.main" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

View File

@ -1 +1 @@
0.0.1-dev 0.0.2-dev

2
.idea/misc.xml generated
View File

@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@ -1,39 +1,62 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
val projectName = rootProject.name val projectName = rootProject.name
val groupName by extra("cn.wzpmc") val groupName by extra("cn.wzpmc")
val projectArtifactId by extra("my-bot") val projectArtifactId by extra("my-bot")
val projectVersion by extra("0.0.1-dev") val projectVersion by extra("0.0.2-dev")
plugins { plugins {
id("java") id("java")
id("maven-publish") id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1"
} }
group = groupName group = groupName
version = projectVersion version = projectVersion
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://libraries.minecraft.net") maven("https://libraries.minecraft.net")
} }
dependencies { dependencies {
implementation("net.minecrell:terminalconsoleappender:1.3.0") {
exclude(group = "org.apache.logging.log4j", module = "log4j-core")
exclude(group = "org.apache.logging.log4j", module = "log4j-api")
}
implementation("com.mojang:brigadier:1.0.18") implementation("com.mojang:brigadier:1.0.18")
// https://mvnrepository.com/artifact/io.netty/netty-all // https://mvnrepository.com/artifact/io.netty/netty-all
implementation("io.netty:netty-all:4.1.112.Final") implementation("io.netty:netty-all:4.1.112.Final")
// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
implementation("org.apache.logging.log4j:log4j-core:2.23.1") implementation("org.apache.logging.log4j:log4j-core:2.23.1")
// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api
implementation("org.apache.logging.log4j:log4j-api:2.23.1")
// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-jul
implementation("org.apache.logging.log4j:log4j-jul:2.23.1")
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 // https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
implementation("com.alibaba.fastjson2:fastjson2:2.0.52") implementation("com.alibaba.fastjson2:fastjson2:2.0.52")
// https://mvnrepository.com/artifact/org.yaml/snakeyaml // https://mvnrepository.com/artifact/org.yaml/snakeyaml
implementation("org.yaml:snakeyaml:2.2") implementation("org.yaml:snakeyaml:2.2")
// https://mvnrepository.com/artifact/org.jline/jline // https://mvnrepository.com/artifact/org.jline/jline
implementation("org.jline:jline:3.26.3") implementation("org.jline:jline-terminal:3.26.3")
implementation("org.jline:jline-reader:3.26.3")
/*implementation("org.jline:jline-terminal-jni:3.26.3")
implementation("org.jline:jline-terminal-ffm:3.26.3")*/
// https://mvnrepository.com/artifact/org.jline/jline-terminal-jansi
implementation("org.jline:jline-terminal-jansi:3.26.3")
// https://mvnrepository.com/artifact/org.fusesource.jansi/jansi
implementation("org.fusesource.jansi:jansi:2.4.1")
/*// https://mvnrepository.com/artifact/org.jline/jline-terminal-jna
implementation("org.jline:jline-terminal-jna:3.26.3")
// https://mvnrepository.com/artifact/net.java.dev.jna/jna
implementation("net.java.dev.jna:jna:5.14.0")*/
// https://mvnrepository.com/artifact/org.projectlombok/lombok // https://mvnrepository.com/artifact/org.projectlombok/lombok
compileOnly("org.projectlombok:lombok:1.18.34") compileOnly("org.projectlombok:lombok:1.18.34")
annotationProcessor("org.projectlombok:lombok:1.18.34") annotationProcessor("org.projectlombok:lombok:1.18.34")
testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.jupiter:junit-jupiter")
} }
tasks.compileJava { tasks.compileJava {
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
@ -48,6 +71,21 @@ tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources") archiveClassifier.set("sources")
from(sourceSets.main.get().allSource) from(sourceSets.main.get().allSource)
} }
tasks.withType<ShadowJar> {
manifest {
attributes(
"Main-Class" to "cn.wzpmc.Main"
)
}
archiveBaseName.set("MyBot")
archiveVersion.set(projectVersion)
archiveClassifier.set("")
transform(Log4j2PluginsCacheFileTransformer::class.java)
}
tasks.named("build") {
dependsOn(tasks.named("shadowJar"))
}
publishing { publishing {
publications { publications {
create<MavenPublication>("mavenJava") { create<MavenPublication>("mavenJava") {
@ -88,15 +126,14 @@ publishing {
repositories { repositories {
maven { maven {
val releasesRepoUrl = uri("http://server.wzpmc.cn:8081/repository/maven-releases") val releasesRepoUrl = uri("https://wzpmc.cn:90/repository/maven-releases")
val snapshotsRepoUrl = uri("http://server.wzpmc.cn:8081/repository/maven-snapshots") val snapshotsRepoUrl = uri("https://wzpmc.cn:90/repository/maven-snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials { credentials {
username = project.findProperty("repo.user") as String? ?: "" username = project.findProperty("repo.user") as String? ?: ""
password = project.findProperty("repo.password") as String? ?: "" password = project.findProperty("repo.password") as String? ?: ""
} }
isAllowInsecureProtocol = true
} }
} }
} }

View File

@ -1,10 +1,13 @@
package cn.wzpmc; package cn.wzpmc;
import cn.wzpmc.commands.StopCommand;
import cn.wzpmc.configuration.Configuration; import cn.wzpmc.configuration.Configuration;
import cn.wzpmc.console.MyBotConsole;
import cn.wzpmc.entities.user.bot.MyBot;
import cn.wzpmc.network.WebSocketConnectionHandler; import cn.wzpmc.network.WebSocketConnectionHandler;
import cn.wzpmc.plugins.CommandManager;
import cn.wzpmc.utils.TemplateFileUtils; import cn.wzpmc.utils.TemplateFileUtils;
import cn.wzpmc.utils.YamlUtils; import cn.wzpmc.utils.YamlUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@ -18,6 +21,8 @@ public class Main {
private static final String DEFAULT_CONFIGURATION_FILE_PATH = "templates/config.yaml"; private static final String DEFAULT_CONFIGURATION_FILE_PATH = "templates/config.yaml";
@SneakyThrows @SneakyThrows
public static void main(String[] args) { public static void main(String[] args) {
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
System.setProperty("terminal.jline", "true");
log.info("启动MyBot..."); log.info("启动MyBot...");
File configurationFile = new File("config.yaml"); File configurationFile = new File("config.yaml");
if (TemplateFileUtils.saveDefaultConfig(Main.class.getClassLoader(), DEFAULT_CONFIGURATION_FILE_PATH, configurationFile)) { if (TemplateFileUtils.saveDefaultConfig(Main.class.getClassLoader(), DEFAULT_CONFIGURATION_FILE_PATH, configurationFile)) {
@ -36,7 +41,10 @@ public class Main {
} }
WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler(); WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler();
ChannelFuture future = webSocketConnectionHandler.connect(uri); ChannelFuture future = webSocketConnectionHandler.connect(uri);
Channel channel = future.sync().channel(); MyBot myBot = new MyBot(configuration);
CommandManager commandManager = myBot.getCommandManager();
commandManager.registerCommand(new StopCommand(myBot));
MyBotConsole myBotConsole = new MyBotConsole(myBot, webSocketConnectionHandler);
myBotConsole.start();
} }
} }

View File

@ -37,7 +37,6 @@ public interface JsonMessagePart {
.append('(') .append('(')
.append('\n'); .append('\n');
Map<String, String> data = this.getData(); Map<String, String> data = this.getData();
int i = 0;
data.forEach((key, value) -> stringBuilder.append('\t').append(key).append('=').append(value).append(',').append('\n')); data.forEach((key, value) -> stringBuilder.append('\t').append(key).append('=').append(value).append(',').append('\n'));
int length = stringBuilder.length(); int length = stringBuilder.length();
stringBuilder.delete(length - 2, length - 1); stringBuilder.delete(length - 2, length - 1);

View File

@ -0,0 +1,48 @@
package cn.wzpmc.api.plugins;
import cn.wzpmc.api.user.IBot;
/**
* 插件基类
* @author wzp
* @version 0.0.2-dev
* @since 2024/7/31 下午6:02
*/
public interface BasePlugin {
/**
* 获取插件主类
* @author wzp
* @since 2024/7/31 下午7:07 v0.0.2-dev
* @param pluginClass 插件主类类名
* @return 插件主类
* @param <T> 插件主类类型
*/
static <T extends BasePlugin> T getPlugin(Class<T> pluginClass){
ClassLoader loader = pluginClass.getClassLoader();
if (loader instanceof IPluginClassLoader){
BasePlugin plugin = ((IPluginClassLoader) loader).getPlugin();
if (pluginClass.isInstance(plugin)) {
return pluginClass.cast(plugin);
}
}
throw new RuntimeException(new IllegalAccessException("You shouldn't load plugin class without PluginClassLoader!!!"));
}
/**
* 获取Bot
* @author wzp
* @since 2024/7/31 下午7:06 v0.0.2-dev
* @return Bot对象
*/
default IBot getBot() {
IPluginClassLoader classLoader = this.getClassLoader();
return classLoader.getBot();
}
/**
* 获取插件所使用的类加载器
* @author wzp
* @since 2024/7/31 下午7:11 v0.0.2-dev
* @return 类加载器
*/
IPluginClassLoader getClassLoader();
}

View File

@ -0,0 +1,35 @@
package cn.wzpmc.api.plugins;
import cn.wzpmc.api.user.IBot;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 插件类加载器
* @author wzp
* @version 0.0.2-dev
* @since 2024/7/31 下午6:59
*/
public abstract class IPluginClassLoader extends URLClassLoader {
public IPluginClassLoader(URL[] urls) {
super(urls);
}
/**
* 获取当前插件
* @author wzp
* @since 2024/7/31 下午7:15 v0.0.2-dev
* @return 插件
*/
abstract public BasePlugin getPlugin();
/**
* 获取Bot
* @author wzp
* @since 2024/7/31 下午7:15 v0.0.2-dev
* @return Bot对象
*/
abstract public IBot getBot();
}

View File

@ -1,6 +1,7 @@
package cn.wzpmc.api.user; package cn.wzpmc.api.user;
import cn.wzpmc.api.message.MessageComponent; import cn.wzpmc.api.message.MessageComponent;
import cn.wzpmc.api.user.permission.Permissions;
/** /**
* 消息发送者 * 消息发送者
@ -31,4 +32,22 @@ public interface CommandSender {
* @param messageComponent 消息组件 * @param messageComponent 消息组件
*/ */
void sendMessage(MessageComponent messageComponent); void sendMessage(MessageComponent messageComponent);
/**
* 获取指令发送者的权限
* @author wzp
* @since 2024/8/1 下午4:50 v0.0.2-dev
* @return 权限
*/
Permissions getPermission();
/**
* 指令发送者是否为管理员
* @author wzp
* @since 2024/8/1 下午4:50 v0.0.2-dev
* @return 是否为管理员
*/
default boolean isAdmin(){
return Permissions.ADMIN.equals(this.getPermission());
}
} }

View File

@ -25,4 +25,11 @@ public interface IBot extends CommandSender {
* @return 指令管理器 * @return 指令管理器
*/ */
ICommandManager getCommandManager(); ICommandManager getCommandManager();
/**
* 停止Bot运行
* @author wzp
* @since 2024/8/1 下午4:57 v0.0.2-dev
*/
void stop();
} }

View File

@ -0,0 +1,20 @@
package cn.wzpmc.api.user.permission;
/**
* 权限
* @author wzp
* @version 0.0.2-dev
* @since 2024/8/1 下午4:48
*/
public enum Permissions {
/**
* 普通用户
* @since 2024/8/1 下午4:49 v0.0.2-dev
*/
USER,
/**
* 管理员
* @since 2024/8/1 下午4:49 v0.0.2-dev
*/
ADMIN
}

View File

@ -1,9 +1,27 @@
package cn.wzpmc.commands; package cn.wzpmc.commands;
import cn.wzpmc.api.commands.BrigadierCommand;
import cn.wzpmc.api.user.CommandSender;
import cn.wzpmc.api.user.IBot;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import lombok.RequiredArgsConstructor;
/** /**
* /stop指令
* @author wzp * @author wzp
* @version 0.0.1-dev * @version 0.0.1-dev
* @since 2024/7/31 上午2:26 * @since 2024/7/31 上午2:26
*/ */
public class StopCommand { @RequiredArgsConstructor
public class StopCommand implements BrigadierCommand {
private final IBot bot;
@Override
public LiteralArgumentBuilder<CommandSender> getCommandNode() {
return LiteralArgumentBuilder.<CommandSender>literal("stop")
.requires(CommandSender::isAdmin)
.executes((e) -> {
this.bot.stop();
return 0;
});
}
} }

View File

@ -0,0 +1,56 @@
package cn.wzpmc.console;
import cn.wzpmc.entities.user.bot.MyBot;
import cn.wzpmc.network.WebSocketConnectionHandler;
import cn.wzpmc.plugins.CommandManager;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.minecrell.terminalconsole.SimpleTerminalConsole;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
/**
* 主控制台
* @author wzp
* @version 0.0.2-dev
* @since 2024/7/31 下午9:47
*/
@Log4j2
@RequiredArgsConstructor
public class MyBotConsole extends SimpleTerminalConsole {
@Getter
private boolean running = true;
private final MyBot bot;
private final CommandManager commandManager;
private final WebSocketConnectionHandler webSocketConnectionHandler;
public MyBotConsole(MyBot bot, WebSocketConnectionHandler webSocketConnectionHandler){
this.bot = bot;
this.commandManager = bot.getCommandManager();
this.webSocketConnectionHandler = webSocketConnectionHandler;
}
@Override
protected LineReader buildReader(LineReaderBuilder builder) {
return super.buildReader(builder.appName("MyBot").completer(commandManager).highlighter(commandManager));
}
@Override
protected void runCommand(String command) {
if (!commandManager.execute(this.bot, command)) {
log.warn("执行指令:`{}`失败!", command);
}
}
@Override
public void shutdown() {
running = false;
this.webSocketConnectionHandler.kill();
}
@Override
public void start() {
this.bot.setConsole(this);
super.start();
}
}

View File

@ -4,10 +4,13 @@ import cn.wzpmc.api.message.MessageComponent;
import cn.wzpmc.api.message.StringMessage; import cn.wzpmc.api.message.StringMessage;
import cn.wzpmc.api.message.json.JsonMessage; import cn.wzpmc.api.message.json.JsonMessage;
import cn.wzpmc.api.user.IBot; import cn.wzpmc.api.user.IBot;
import cn.wzpmc.api.user.permission.Permissions;
import cn.wzpmc.configuration.Configuration; import cn.wzpmc.configuration.Configuration;
import cn.wzpmc.console.MyBotConsole;
import cn.wzpmc.plugins.CommandManager; import cn.wzpmc.plugins.CommandManager;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
/** /**
@ -21,9 +24,13 @@ import lombok.extern.log4j.Log4j2;
@Getter @Getter
public class MyBot implements IBot { public class MyBot implements IBot {
private final Configuration configuration; private final Configuration configuration;
private final Long id; @Setter
private final Long name; private Long id;
private final CommandManager commandManager = new CommandManager(); @Setter
private Long name;
private final CommandManager commandManager = new CommandManager(this);
@Setter
private MyBotConsole console = null;
@Override @Override
public void sendMessage(MessageComponent messageComponent) { public void sendMessage(MessageComponent messageComponent) {
@ -34,4 +41,16 @@ public class MyBot implements IBot {
log.info(((JsonMessage) messageComponent).toTextDisplay()); log.info(((JsonMessage) messageComponent).toTextDisplay());
} }
} }
@Override
public Permissions getPermission() {
return Permissions.ADMIN;
}
@Override
public void stop() {
if (this.console != null) {
this.console.shutdown();
}
}
} }

View File

@ -27,6 +27,7 @@ public class WebSocketConnectionHandler {
* @author wzp * @author wzp
* @since 2024/7/30 下午11:55 v0.0.1-dev * @since 2024/7/30 下午11:55 v0.0.1-dev
* @param websocket websocket连接地址 * @param websocket websocket连接地址
* @return 一个ChannelFuture对象
*/ */
public ChannelFuture connect(URI websocket){ public ChannelFuture connect(URI websocket){
log.info("正在连接websocket"); log.info("正在连接websocket");
@ -44,6 +45,7 @@ public class WebSocketConnectionHandler {
* @since 2024/7/31 上午2:04 v0.0.1-dev * @since 2024/7/31 上午2:04 v0.0.1-dev
*/ */
public void kill(){ public void kill(){
log.info("结束连接...");
this.eventLoopGroup.shutdownGracefully(); this.eventLoopGroup.shutdownGracefully();
} }
} }

View File

@ -2,21 +2,31 @@ package cn.wzpmc.plugins;
import cn.wzpmc.api.commands.BrigadierCommand; import cn.wzpmc.api.commands.BrigadierCommand;
import cn.wzpmc.api.commands.RawCommand; import cn.wzpmc.api.commands.RawCommand;
import cn.wzpmc.api.message.StringMessage;
import cn.wzpmc.api.plugins.ICommandManager; import cn.wzpmc.api.plugins.ICommandManager;
import cn.wzpmc.api.user.CommandSender; import cn.wzpmc.api.user.CommandSender;
import cn.wzpmc.api.user.IBot;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.ParseResults;
import com.mojang.brigadier.context.ParsedCommandNode;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestion; import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import lombok.NoArgsConstructor; import com.mojang.brigadier.tree.LiteralCommandNode;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.ToString;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.jline.reader.*;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.regex.Pattern;
/** /**
* 指令管理器实现类 * 指令管理器实现类
@ -25,10 +35,14 @@ import java.util.stream.Collectors;
* @since 2024/7/31 上午3:13 * @since 2024/7/31 上午3:13
*/ */
@Log4j2 @Log4j2
@NoArgsConstructor public class CommandManager implements ICommandManager, Completer, Highlighter {
public class CommandManager implements ICommandManager {
private final CommandDispatcher<CommandSender> dispatcher = new CommandDispatcher<>(); private final CommandDispatcher<CommandSender> dispatcher = new CommandDispatcher<>();
private final ConcurrentHashMap<String, RawCommand> rawCommands = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, RawCommand> rawCommands = new ConcurrentHashMap<>();
private static final int[] COLORS = {AttributedStyle.CYAN, AttributedStyle.YELLOW, AttributedStyle.GREEN, AttributedStyle.MAGENTA, AttributedStyle.BLUE};
private final IBot bot;
public CommandManager(IBot bot) {
this.bot = bot;
}
@Override @Override
public void registerCommand(RawCommand rawCommand, String name) { public void registerCommand(RawCommand rawCommand, String name) {
if (rawCommands.containsKey(name)){ if (rawCommands.containsKey(name)){
@ -41,6 +55,7 @@ public class CommandManager implements ICommandManager {
public void registerCommand(BrigadierCommand brigadierCommand){ public void registerCommand(BrigadierCommand brigadierCommand){
dispatcher.register(brigadierCommand.getCommandNode()); dispatcher.register(brigadierCommand.getCommandNode());
} }
@ToString
private static final class CommandPart { private static final class CommandPart {
private final String name; private final String name;
private final List<String> args; private final List<String> args;
@ -67,6 +82,11 @@ public class CommandManager implements ICommandManager {
try { try {
dispatcher.execute(rawCommandLine, sender); dispatcher.execute(rawCommandLine, sender);
} catch (CommandSyntaxException e) { } catch (CommandSyntaxException e) {
if (e.getType().equals(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())) {
sender.sendMessage(StringMessage.text(this.bot.getConfiguration().getFallback().getCommand()));
return false;
}
sender.sendMessage(StringMessage.text(e.getLocalizedMessage()));
return false; return false;
} }
} }
@ -79,17 +99,83 @@ public class CommandManager implements ICommandManager {
* @since 2024/7/31 上午3:36 v0.0.1-dev * @since 2024/7/31 上午3:36 v0.0.1-dev
* @param sender 消息发送者 * @param sender 消息发送者
* @param rawCommandLine 完整命令行 * @param rawCommandLine 完整命令行
* @param cursor 当前光标位置
* @return 所有被补全的指令 * @return 所有被补全的指令
*/ */
@SneakyThrows @SneakyThrows
public List<String> tabComplete(CommandSender sender, String rawCommandLine){ public List<String> tabComplete(CommandSender sender, String rawCommandLine, int cursor){
CommandPart commandPart = new CommandPart(rawCommandLine); CommandPart commandPart = new CommandPart(rawCommandLine);
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
if (rawCommands.containsKey(commandPart.name)) { if (rawCommands.containsKey(commandPart.name)) {
result.addAll(rawCommands.get(commandPart.name).onTabComplete(sender, commandPart.args)); result.addAll(rawCommands.get(commandPart.name).onTabComplete(sender, commandPart.args));
} }
Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(rawCommandLine, sender)).get(); for (Map.Entry<String, RawCommand> stringRawCommandEntry : rawCommands.entrySet()) {
result.addAll(suggestions.getList().stream().map(Suggestion::getText).collect(Collectors.toList())); String key = stringRawCommandEntry.getKey();
if (key.contains(commandPart.name)){
result.add(key);
}
}
Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(rawCommandLine, sender), cursor).get();
result.addAll(suggestions.getList().stream().map(Suggestion::getText).toList());
return result; return result;
} }
@Override
public void complete(LineReader lineReader, ParsedLine parsedLine, List<Candidate> list) {
String line = parsedLine.line();
int cursor = parsedLine.cursor();
list.addAll(this.tabComplete(this.bot, line, cursor).stream().map(Candidate::new).toList());
}
@Override
public AttributedString highlight(LineReader lineReader, String s) {
final AttributedStringBuilder builder = new AttributedStringBuilder();
String[] strings = s.split(" ");
String commandName = strings[0];
if (rawCommands.containsKey(commandName)){
builder.append(commandName, AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN)).append(' ');
for (int i = 1; i < strings.length; i++) {
builder.append(strings[i]).append(' ');
}
}else {
final ParseResults<CommandSender> results = this.dispatcher.parse(s, this.bot);
int pos = 0;
if (s.startsWith("/")) {
builder.append("/", AttributedStyle.DEFAULT);
pos = 1;
}
int component = -1;
for (final ParsedCommandNode<CommandSender> node : results.getContext().getLastChild().getNodes()) {
if (node.getRange().getStart() >= s.length()) {
break;
}
final int start = node.getRange().getStart();
final int end = Math.min(node.getRange().getEnd(), s.length());
builder.append(s.substring(pos, start), AttributedStyle.DEFAULT);
if (node.getNode() instanceof LiteralCommandNode) {
builder.append(s.substring(start, end), AttributedStyle.DEFAULT);
} else {
if (++component >= COLORS.length) {
component = 0;
}
builder.append(s.substring(start, end), AttributedStyle.DEFAULT.foreground(COLORS[component]));
}
pos = end;
}
if (pos < s.length()) {
builder.append((s.substring(pos)), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
}
}
return builder.toAttributedString();
}
@Override
public void setErrorPattern(Pattern pattern) {
}
@Override
public void setErrorIndex(int i) {
}
} }

View File

@ -0,0 +1,23 @@
package cn.wzpmc.plugins;
import cn.wzpmc.api.plugins.BasePlugin;
import cn.wzpmc.api.plugins.IPluginClassLoader;
/**
* Java插件基类
* @author wzp
* @version 0.0.2-dev
* @since 2024/7/31 下午7:01
*/
public class JavaPlugin implements BasePlugin {
@Override
public IPluginClassLoader getClassLoader() {
Class<? extends JavaPlugin> aClass = this.getClass();
ClassLoader loader = aClass.getClassLoader();
if (loader instanceof IPluginClassLoader){
return (IPluginClassLoader) loader;
}
throw new RuntimeException(new IllegalAccessException("You shouldn't load plugin class without PluginClassLoader!!!"));
}
}

View File

@ -0,0 +1,30 @@
package cn.wzpmc.plugins;
import cn.wzpmc.api.plugins.BasePlugin;
import cn.wzpmc.api.plugins.IPluginClassLoader;
import cn.wzpmc.api.user.IBot;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.net.URL;
/**
* 插件类加载器实现
* @author wzp
* @version 0.0.2-dev
* @since 2024/7/31 下午7:12
*/
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
public class PluginClassLoader extends IPluginClassLoader {
private final IBot bot;
@Setter
private BasePlugin plugin;
public PluginClassLoader(URL[] urls, IBot bot) {
super(urls);
this.bot = bot;
}
}

View File

@ -2,10 +2,10 @@
<Configuration status="WARN" monitorInterval="30"> <Configuration status="WARN" monitorInterval="30">
<Appenders> <Appenders>
<!-- 控制台输出 --> <!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT"> <TerminalConsole name="Console">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] %c - %m%n"/> <PatternLayout pattern="%highlight{[%d{HH:mm:ss} %level]: %msg%n%xEx}" disableAnsi="${tca:disableAnsi}"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Console> </TerminalConsole>
<!-- 文件输出 --> <!-- 文件输出 -->
<RollingFile name="File" fileName="./logs/latest.log" <RollingFile name="File" fileName="./logs/latest.log"
@ -14,7 +14,7 @@
<Policies> <Policies>
<OnStartupTriggeringPolicy/> <OnStartupTriggeringPolicy/>
<!-- 按日期滚动日志 --> <!-- 按日期滚动日志 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/> <TimeBasedTriggeringPolicy interval="86400" modulate="true"/>
</Policies> </Policies>
<DefaultRolloverStrategy fileIndex="max" max="7"/> <DefaultRolloverStrategy fileIndex="max" max="7"/>
</RollingFile> </RollingFile>