From 9ce8ef60cde37d10db82c1ffc4c68ac0eeb6fbe6 Mon Sep 17 00:00:00 2001 From: huanmeng_qwq <49704373+huanmeng-qwq@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:28:47 +0800 Subject: [PATCH] Cloud -> LiteCommands (#63) In this commit we had removed everything about Cloud, and switched to another command framework called LiteCommands. Reviewed-by: xiaoACE6716, SNWCreations Tested-by: huanmeng_qwq --- CLOUD_MIGRATE_TO_LITECOMMANDS.md | 48 + pom.xml | 17 +- .../annotations/AnnotationParser.java | 875 ------------------ .../MethodCommandExecutionHandler.java | 290 ------ .../annotations/SyntaxParser.java | 75 -- .../arguments/CommandArgument.java | 829 ----------------- src/main/java/snw/kookbc/impl/KBCClient.java | 37 +- .../cloud/CloudBasedCommandManager.java | 420 --------- .../command/cloud/CloudCommandExecutor.java | 121 --- .../impl/command/cloud/CloudCommandInfo.java | 101 -- .../cloud/CloudCommandManagerImpl.java | 142 --- .../impl/command/cloud/CloudCommandMap.java | 62 -- .../impl/command/cloud/CloudConstants.java | 45 - .../cloud/CloudKookCommandBuilder.java | 71 -- .../CloudWrappedCommandExecutionHandler.java | 147 --- .../CommandPluginDisabledException.java | 33 - .../cloud/parser/GuildArgumentParser.java | 58 -- .../cloud/parser/PluginArgumentParser.java | 70 -- .../cloud/parser/UserArgumentParser.java | 65 -- .../command/internal/CloudHelpCommand.java | 246 ----- .../litecommands/KookLitePlatform.java | 77 ++ .../impl/command/litecommands/KookSender.java | 57 ++ .../litecommands/LiteKookCommandExecutor.java | 79 ++ .../command/litecommands/LiteKookFactory.java | 54 ++ .../LiteKookSettings.java} | 25 +- .../command/litecommands/StringHandler.java | 46 + .../WrappedLiteCommandsBuilder.java | 339 +++++++ .../annotations/prefix/Prefix.java} | 12 +- .../prefix/PrefixAnnotationResolver.java | 37 + .../litecommands/internal/PluginsCommand.java | 55 ++ .../completer/LiteCommandsCompleter.java | 86 ++ .../tools/KookMessageContextual.java} | 29 +- .../tools/KookOnlyConsoleContextual.java | 50 + .../tools/KookOnlyUserContextual.java | 50 + .../java/snw/kookbc/impl/console/Console.java | 3 +- .../internal/UserClickButtonListener.java | 8 +- .../java/snw/kookbc/launcher/Launcher.java | 6 - src/main/java/snw/kookbc/util/Util.java | 32 - 38 files changed, 1040 insertions(+), 3757 deletions(-) create mode 100644 CLOUD_MIGRATE_TO_LITECOMMANDS.md delete mode 100644 src/main/java/cloud/commandframework/annotations/AnnotationParser.java delete mode 100644 src/main/java/cloud/commandframework/annotations/MethodCommandExecutionHandler.java delete mode 100644 src/main/java/cloud/commandframework/annotations/SyntaxParser.java delete mode 100644 src/main/java/cloud/commandframework/arguments/CommandArgument.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudBasedCommandManager.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudCommandExecutor.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudCommandInfo.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudCommandManagerImpl.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudCommandMap.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudConstants.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudKookCommandBuilder.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/CloudWrappedCommandExecutionHandler.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/exception/CommandPluginDisabledException.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/parser/GuildArgumentParser.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/parser/PluginArgumentParser.java delete mode 100644 src/main/java/snw/kookbc/impl/command/cloud/parser/UserArgumentParser.java delete mode 100644 src/main/java/snw/kookbc/impl/command/internal/CloudHelpCommand.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/KookLitePlatform.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/KookSender.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/LiteKookCommandExecutor.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/LiteKookFactory.java rename src/main/java/snw/kookbc/impl/command/{cloud/annotations/CommandHelpContent.java => litecommands/LiteKookSettings.java} (65%) create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/StringHandler.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/WrappedLiteCommandsBuilder.java rename src/main/java/snw/kookbc/impl/command/{cloud/annotations/CommandPrefix.java => litecommands/annotations/prefix/Prefix.java} (84%) create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/PrefixAnnotationResolver.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/internal/PluginsCommand.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/internal/completer/LiteCommandsCompleter.java rename src/main/java/snw/kookbc/impl/command/{cloud/CloudCommandBuilder.java => litecommands/tools/KookMessageContextual.java} (54%) create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyConsoleContextual.java create mode 100644 src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyUserContextual.java diff --git a/CLOUD_MIGRATE_TO_LITECOMMANDS.md b/CLOUD_MIGRATE_TO_LITECOMMANDS.md new file mode 100644 index 00000000..1971b05e --- /dev/null +++ b/CLOUD_MIGRATE_TO_LITECOMMANDS.md @@ -0,0 +1,48 @@ +### 从Cloud迁移到LiteCommands + +### 注解 + +| Cloud | LiteCommands | +|------------------------|-----------------------| +| @CommandContainer | @RootCommand | +| @CommandMethod("test") | @Command(name="test") | +| @Argument | @Arg | + +### 示例 +#### Cloud + +```java +@CommandContainer +@CommandPrefix("!") +@CommandMethod("test") +public class TestCloudCommand { + @CommandMethod("sub1 ") + public void sub1(CommandSender sender, Message message, @Argument("parm1") String parm1) { + // ... + } +} +``` + +#### LiteCommands +> Github: [LiteCommands](https://github.com/Rollczi/LiteCommands) +> +> IDEA中安装[LiteCommands](https://plugins.jetbrains.com/plugin/20799-litecommands)插件后,可以自动识别LiteCommands的注解,并能够很好的辅助开发。 + +```java +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import snw.kookbc.impl.command.litecommands.annotations.prefix.Prefix; + +@Command(name = "test") +@Prefix("!") +public class TestLiteCommand { + @Execute(name = "sub1") + public void sub1(@Context CommandSender sender, + @Context Message message, + @Arg("parm1") String parm1) { + // ... + } +} +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 55267021..0e56f1f5 100644 --- a/pom.xml +++ b/pom.xml @@ -182,6 +182,10 @@ architectury https://maven.architectury.dev/ + + panda-repo + https://repo.panda-lang.org/releases + @@ -296,16 +300,11 @@ ${mixin.version} - - - cloud.commandframework - cloud-core - 1.8.3 - + - cloud.commandframework - cloud-annotations - 1.8.3 + dev.rollczi + litecommands-framework + 3.3.4 diff --git a/src/main/java/cloud/commandframework/annotations/AnnotationParser.java b/src/main/java/cloud/commandframework/annotations/AnnotationParser.java deleted file mode 100644 index 01d164dd..00000000 --- a/src/main/java/cloud/commandframework/annotations/AnnotationParser.java +++ /dev/null @@ -1,875 +0,0 @@ -// -// MIT License -// -// Copyright (c) 2022 Alexander Söderberg & Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -package cloud.commandframework.annotations; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.Command; -import cloud.commandframework.CommandManager; -import cloud.commandframework.annotations.injection.ParameterInjectorRegistry; -import cloud.commandframework.annotations.injection.RawArgs; -import cloud.commandframework.annotations.parsers.MethodArgumentParser; -import cloud.commandframework.annotations.parsers.Parser; -import cloud.commandframework.annotations.processing.CommandContainerProcessor; -import cloud.commandframework.annotations.specifier.Completions; -import cloud.commandframework.annotations.suggestions.MethodSuggestionsProvider; -import cloud.commandframework.annotations.suggestions.Suggestions; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.flags.CommandFlag; -import cloud.commandframework.arguments.parser.*; -import cloud.commandframework.arguments.preprocessor.RegexPreprocessor; -import cloud.commandframework.captions.Caption; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.execution.CommandExecutionHandler; -import cloud.commandframework.extra.confirmation.CommandConfirmationManager; -import cloud.commandframework.meta.CommandMeta; -import cloud.commandframework.meta.SimpleCommandMeta; -import io.leangen.geantyref.GenericTypeReflector; -import io.leangen.geantyref.TypeToken; -import org.apiguardian.api.API; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.command.cloud.annotations.CommandHelpContent; -import snw.kookbc.impl.command.cloud.annotations.CommandPrefix; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static snw.kookbc.impl.command.cloud.CloudConstants.*; - -/** - * Parser that parses class instances {@link Command commands} - * - * @param Command sender type - */ -public final class AnnotationParser { - - /** - * The value of {@link Argument} that should be used to infer argument names from parameter names. - */ - public static final String INFERRED_ARGUMENT_NAME = "__INFERRED_ARGUMENT_NAME__"; - - private final SyntaxParser syntaxParser = new SyntaxParser(); - private final ArgumentExtractor argumentExtractor = new ArgumentExtractor(); - - private final CommandManager manager; - private final Map, Function> annotationMappers; - private final Map, Function, - @NonNull Queue<@NonNull String>, @NonNull ArgumentParseResult>>> preprocessorMappers; - private final Map, BiFunction, Command.Builder>> - builderModifiers; - private final Map, Function, - MethodCommandExecutionHandler>> commandMethodFactories; - private final TypeToken commandSenderType; - private final MetaFactory metaFactory; - private final FlagExtractor flagExtractor; - - private StringProcessor stringProcessor; - - /** - * Construct a new annotation parser - * - * @param manager Command manager instance - * @param commandSenderClass Command sender class - * @param metaMapper Function that is used to create {@link CommandMeta} instances from annotations on the - * command methods. These annotations will be mapped to - * {@link ParserParameter}. Mappers for the - * parser parameters can be registered using {@link #registerAnnotationMapper(Class, Function)} - */ - public AnnotationParser( - final @NonNull CommandManager manager, - final @NonNull Class commandSenderClass, - final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper - ) { - this(manager, TypeToken.get(commandSenderClass), metaMapper); - } - - /** - * Construct a new annotation parser - * - * @param manager Command manager instance - * @param commandSenderType Command sender type - * @param metaMapper Function that is used to create {@link CommandMeta} instances from annotations on the - * command methods. These annotations will be mapped to - * {@link ParserParameter}. Mappers for the - * parser parameters can be registered using {@link #registerAnnotationMapper(Class, Function)} - * @since 1.7.0 - */ - @API(status = API.Status.STABLE, since = "1.7.0") - public AnnotationParser( - final @NonNull CommandManager manager, - final @NonNull TypeToken commandSenderType, - final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper - ) { - this.commandSenderType = commandSenderType; - this.manager = manager; - this.metaFactory = new MetaFactory(this, metaMapper); - this.annotationMappers = new HashMap<>(); - this.preprocessorMappers = new HashMap<>(); - this.builderModifiers = new HashMap<>(); - this.commandMethodFactories = new HashMap<>(); - this.flagExtractor = new FlagExtractor(manager, this); - this.registerAnnotationMapper(CommandDescription.class, d -> - ParserParameters.single(StandardParameters.DESCRIPTION, this.processString(d.value()))); - this.registerPreprocessorMapper(Regex.class, annotation -> RegexPreprocessor.of( - this.processString(annotation.value()), - Caption.of(this.processString(annotation.failureCaption())) - )); - this.getParameterInjectorRegistry().registerInjector( - String[].class, - (context, annotations) -> annotations.annotation(RawArgs.class) == null - ? null - : context.getRawInput().toArray(new String[0]) - ); - this.getParameterInjectorRegistry().registerInjector( - Plugin.class, - (context, annotations) -> context.get(PLUGIN_KEY) - ); - this.getParameterInjectorRegistry().registerInjector( - Message.class, - (context, annotations) -> { - if (!context.contains("ignore_null")) { - context.store("ignore_null", new ArrayList<>()); - } - ArrayList> list = context.get("ignore_null"); - list.add(Message.class); - return context.getOrDefault(KOOK_MESSAGE_KEY, null); - } - ); - this.stringProcessor = StringProcessor.noOp(); - } - - @SuppressWarnings("unchecked") - static @Nullable A getAnnotationRecursively( - final @NonNull AnnotationAccessor annotations, - final @NonNull Class clazz, - final @NonNull Set> checkedAnnotations - ) { - A innerCandidate = null; - for (final Annotation annotation : annotations.annotations()) { - if (!checkedAnnotations.add(annotation.annotationType())) { - continue; - } - if (annotation.annotationType().equals(clazz)) { - return (A) annotation; - } - if (annotation.annotationType().getPackage().getName().startsWith("java.lang")) { - continue; - } - final A inner = getAnnotationRecursively( - AnnotationAccessor.of(annotation.annotationType()), - clazz, - checkedAnnotations - ); - if (inner != null) { - innerCandidate = inner; - } - } - return innerCandidate; - } - - static @Nullable A getMethodOrClassAnnotation( - final @NonNull Method method, - final @NonNull Class clazz - ) { - A annotation = getAnnotationRecursively( - AnnotationAccessor.of(method), - clazz, - new HashSet<>() - ); - if (annotation == null) { - annotation = getAnnotationRecursively( - AnnotationAccessor.of(method.getDeclaringClass()), - clazz, - new HashSet<>() - ); - } - return annotation; - } - - static boolean methodOrClassHasAnnotation( - final @NonNull Method method, - final @NonNull Class clazz - ) { - return getMethodOrClassAnnotation(method, clazz) != null; - } - - /** - * Returns the command manager that was used to create this parser - * - * @return Command manager - * @since 1.6.0 - */ - public @NonNull CommandManager manager() { - return this.manager; - } - - /** - * Registers a new command execution method factory. This allows for the registration of - * custom command method execution strategies. - * - * @param predicate The predicate that decides whether or not to apply the custom execution handler to the given method - * @param function The function that produces the command execution handler - * @since 1.6.0 - */ - public void registerCommandExecutionMethodFactory( - final @NonNull Predicate<@NonNull Method> predicate, - final @NonNull Function, MethodCommandExecutionHandler> function - ) { - this.commandMethodFactories.put(predicate, function); - } - - /** - * Register a builder modifier for a specific annotation. The builder modifiers are - * allowed to act on a {@link Command.Builder} after all arguments have been added - * to the builder. This allows for modifications of the builder instance before - * the command is registered to the command manager. - * - * @param annotation Annotation (class) that the builder modifier reacts to - * @param builderModifier Modifier that acts on the given annotation and the incoming builder. Command builders - * are immutable, so the modifier should return the instance of the command builder that is - * returned as a result of any operation on the builder - * @param Annotation type - */ - public void registerBuilderModifier( - final @NonNull Class annotation, - final @NonNull BiFunction, Command.Builder> builderModifier - ) { - this.builderModifiers.put(annotation, builderModifier); - } - - /** - * Register an annotation mapper - * - * @param annotation Annotation class - * @param mapper Mapping function - * @param Annotation type - */ - public void registerAnnotationMapper( - final @NonNull Class annotation, - final @NonNull Function<@NonNull A, - @NonNull ParserParameters> mapper - ) { - this.annotationMappers.put(annotation, mapper); - } - - /** - * Register a preprocessor mapper - * - * @param annotation Annotation class - * @param preprocessorMapper Preprocessor mapper - * @param Annotation type - */ - public void registerPreprocessorMapper( - final @NonNull Class annotation, - final @NonNull Function, @NonNull Queue<@NonNull String>, - @NonNull ArgumentParseResult>> preprocessorMapper - ) { - this.preprocessorMappers.put(annotation, preprocessorMapper); - } - - /** - * Get the parameter injector registry instance that is used to inject non-{@link Argument argument} parameters - * into {@link CommandMethod} annotated {@link Method methods} - * - * @return Parameter injector registry - * @since 1.2.0 - */ - public @NonNull ParameterInjectorRegistry getParameterInjectorRegistry() { - return this.manager.parameterInjectorRegistry(); - } - - /** - * Returns the string processor used by this parser. - * - * @return the string processor - * @since 1.7.0 - */ - public @NonNull StringProcessor stringProcessor() { - return this.stringProcessor; - } - - /** - * Replaces the string processor of this parser. - * - * @param stringProcessor the new string processor - * @since 1.7.0 - */ - public void stringProcessor(final @NonNull StringProcessor stringProcessor) { - this.stringProcessor = stringProcessor; - } - - /** - * Processes the {@code input} string and returns the processed result. - * - * @param input the input string - * @return the processed string - * @since 1.7.0 - */ - public @NonNull String processString(final @NonNull String input) { - return this.stringProcessor().processString(input); - } - - /** - * Processes the input {@code strings} and returns the processed result. - * - * @param strings the input strings - * @return the processed strings - * @since 1.7.0 - */ - public @NonNull String[] processStrings(final @NonNull String[] strings) { - return Arrays.stream(strings).map(this::processString).toArray(String[]::new); - } - - public @NonNull Collection<@NonNull Command> parseContainers(final @NonNull Plugin plugin) throws Exception { - return parseContainers(this.getClass().getClassLoader(), plugin); - } - - /** - * Parses all known {@link cloud.commandframework.annotations.processing.CommandContainer command containers}. - * - * @return Collection of parsed commands - * @throws Exception re-throws all encountered exceptions. - * @see cloud.commandframework.annotations.processing.CommandContainer CommandContainer for more information. - * @since 1.7.0 - */ - public @NonNull Collection<@NonNull Command> parseContainers(final @NonNull ClassLoader classLoader, final @NonNull Plugin plugin) throws Exception { - final List> commands = new LinkedList<>(); - - final List classes; - try (InputStream stream = classLoader.getResourceAsStream(CommandContainerProcessor.PATH)) { - if (stream == null) { - return Collections.emptyList(); - } - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - classes = reader.lines().distinct().collect(Collectors.toList()); - } - } - - for (final String className : classes) { - final Class commandContainer = Class.forName(className); - - // We now have the class, and we now just need to decide what constructor to invoke. - // We first try to find a constructor which takes in the parser. - @MonotonicNonNull Object instance; - try { - instance = commandContainer.getConstructor(AnnotationParser.class).newInstance(this); - } catch (final NoSuchMethodException ignored) { - try { - // Then we try to find a no-arg constructor. - instance = commandContainer.getConstructor().newInstance(); - } catch (final NoSuchMethodException ignored2) { - try { - instance = commandContainer.getConstructor(Plugin.class).newInstance(plugin); - } catch (final NoSuchMethodException e) { - // If neither are found, we panic! - throw new IllegalStateException( - String.format( - "Command container %s has no valid constructors", - commandContainer - ), - e - ); - } - } - } - commands.addAll(this.parse(instance)); - } - - return Collections.unmodifiableList(commands); - } - - /** - * Scan a class instance of {@link CommandMethod} annotations and attempt to - * compile them into {@link Command} instances. - * - * @param instance Instance to scan - * @param Type of the instance - * @return Collection of parsed commands - */ - @SuppressWarnings({"deprecation", "unchecked", "rawtypes"}) - public @NonNull Collection<@NonNull Command> parse(final @NonNull T instance) { - /* Start by registering all @Suggestion annotated methods */ - this.parseSuggestions(instance); - /* Then register all parsers */ - this.parseParsers(instance); - /* Then construct commands from @CommandMethod annotated classes */ - final Method[] methods = instance.getClass().getDeclaredMethods(); - final Collection commandMethodPairs = new ArrayList<>(); - for (final Method method : methods) { - final CommandMethod commandMethod = method.getAnnotation(CommandMethod.class); - if (commandMethod == null) { - continue; - } - if (!method.isAccessible()) { - method.setAccessible(true); - } - if (Modifier.isStatic(method.getModifiers())) { - throw new IllegalArgumentException(String.format( - "@CommandMethod annotated method '%s' is static! @CommandMethod annotated methods should not be static.", - method.getName() - )); - } - commandMethodPairs.add(new CommandMethodPair(method, commandMethod)); - } - final Collection> commands = this.construct(instance, commandMethodPairs); - for (final Command command : commands) { - ((CommandManager) this.manager).command(command); - } - return commands; - } - - @SuppressWarnings("deprecation") - private void parseSuggestions(final @NonNull T instance) { - for (final Method method : instance.getClass().getMethods()) { - final Suggestions suggestions = method.getAnnotation(Suggestions.class); - if (suggestions == null) { - continue; - } - if (!method.isAccessible()) { - method.setAccessible(true); - } - if (method.getParameterCount() != 2 - || !method.getReturnType().equals(List.class) - || !method.getParameters()[0].getType().equals(CommandContext.class) - || !method.getParameters()[1].getType().equals(String.class) - ) { - throw new IllegalArgumentException(String.format( - "@Suggestions annotated method '%s' in class '%s' does not have the correct signature", - method.getName(), - instance.getClass().getCanonicalName() - )); - } - try { - this.manager.parserRegistry().registerSuggestionProvider( - this.processString(suggestions.value()), - new MethodSuggestionsProvider<>(instance, method) - ); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - } - - @SuppressWarnings("deprecation") - private void parseParsers(final @NonNull T instance) { - for (final Method method : instance.getClass().getMethods()) { - final Parser parser = method.getAnnotation(Parser.class); - if (parser == null) { - continue; - } - if (!method.isAccessible()) { - method.setAccessible(true); - } - if (method.getParameterCount() != 2 - || method.getReturnType().equals(Void.class) - || !method.getParameters()[0].getType().equals(CommandContext.class) - || !method.getParameters()[1].getType().equals(Queue.class) - ) { - throw new IllegalArgumentException(String.format( - "@Parser annotated method '%s' in class '%s' does not have the correct signature", - method.getName(), - instance.getClass().getCanonicalName() - )); - } - try { - final String suggestions = this.processString(parser.suggestions()); - final BiFunction, String, List> suggestionsProvider; - if (suggestions.isEmpty()) { - suggestionsProvider = (context, input) -> Collections.emptyList(); - } else { - suggestionsProvider = this.manager.parserRegistry().getSuggestionProvider(suggestions) - .orElseThrow(() -> new NullPointerException( - String.format( - "Cannot find the suggestions provider with name '%s'", - suggestions - ) - )); - } - final MethodArgumentParser methodArgumentParser = new MethodArgumentParser<>( - suggestionsProvider, - instance, - method - ); - final Function> parserFunction = - parameters -> methodArgumentParser; - final String name = this.processString(parser.name()); - if (name.isEmpty()) { - this.manager.parserRegistry().registerParserSupplier( - TypeToken.get(method.getGenericReturnType()), - parserFunction - ); - } else { - this.manager.parserRegistry().registerNamedParserSupplier( - name, - parserFunction - ); - } - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - } - - @SuppressWarnings("unchecked") - private @NonNull Collection<@NonNull Command> construct( - final @NonNull Object instance, - final @NonNull Collection<@NonNull CommandMethodPair> methodPairs) { - final AnnotationAccessor classAnnotations = AnnotationAccessor.of(instance.getClass()); - final CommandMethod classCommandMethod = classAnnotations.annotation(CommandMethod.class); - final String syntaxPrefix = classCommandMethod == null ? "" : (this.processString(classCommandMethod.value()) + " "); - final Collection> commands = new ArrayList<>(); - for (final CommandMethodPair commandMethodPair : methodPairs) { - final CommandMethod commandMethod = commandMethodPair.getCommandMethod(); - final Method method = commandMethodPair.getMethod(); - String methodSyntax = commandMethod.value(); - String processed = this.processString(methodSyntax); - String syntax = syntaxPrefix + processed; - CommandPrefix commandPrefix = classAnnotations.annotation(CommandPrefix.class); - if (commandPrefix != null) { - String str = syntax; - String suffix = null; - if (str.contains(" ")) { - String[] split = str.split(" ", 2); - str = split[0]; - suffix = split[1]; - } - StringBuilder stringBuilder = new StringBuilder(str); - Iterator iterator = Arrays.stream(commandPrefix.value()).iterator(); - while (iterator.hasNext()) { - stringBuilder.append("|").append(iterator.next()).append(str); - } - if (suffix != null) { - stringBuilder.append(' ').append(suffix); - } - syntax = stringBuilder.toString(); - } - CommandPrefix methodPrefix = commandMethodPair.getMethod().getAnnotation(CommandPrefix.class); - if (methodPrefix != null) { - String str = processed; - if (str.contains(" ")) { - str = str.split(" ", 2)[0]; - } - String prefix = syntax; - String suffix = null; - if (prefix.contains(" ")) { - String[] split = prefix.split(" ", 2); - prefix = split[0]; - suffix = split[1]; - } - StringBuilder stringBuilder = new StringBuilder(prefix); - Iterator iterator = Arrays.stream(methodPrefix.value()).iterator(); - while (iterator.hasNext()) { - stringBuilder.append("|").append(iterator.next()).append(str); - } - if (suffix != null) { - stringBuilder.append(' ').append(suffix); - } - syntax = stringBuilder.toString(); - } - final List tokens = this.syntaxParser.apply(syntax); - /* Determine command name */ - final String commandToken = syntax.split(" ")[0].split("\\|")[0]; - @SuppressWarnings("rawtypes") final CommandManager manager = this.manager; - final SimpleCommandMeta.Builder metaBuilder = SimpleCommandMeta.builder() - .with(this.metaFactory.apply(method)); - if (methodOrClassHasAnnotation(method, Confirmation.class)) { - metaBuilder.with(CommandConfirmationManager.META_CONFIRMATION_REQUIRED, true); - } - List prefixes = new ArrayList<>(); - List<@NonNull String> minor = tokens.get(0).getMinor(); - List aliases = new ArrayList<>(minor); - if (methodPrefix != null) { - prefixes.addAll(Arrays.asList(methodPrefix.value())); - } - if (commandPrefix != null) { - prefixes.addAll(Arrays.asList(commandPrefix.value())); - } - metaBuilder.with(ALIAS_KEY, aliases); - metaBuilder.with(PREFIX_KEY, prefixes); - metaBuilder.with(JKOOK_COMMAND_KEY, false); - if (methodOrClassHasAnnotation(method, CommandHelpContent.class)) { - String[] strings = method.getAnnotation(CommandHelpContent.class).value(); - metaBuilder.with(HELP_CONTENT_KEY, String.join("\n", strings)); - } else { - metaBuilder.with(HELP_CONTENT_KEY, "无前缀也可以执行。"); - } - @SuppressWarnings("rawtypes") - Command.Builder builder = manager.commandBuilder( - commandToken, - minor, - metaBuilder.build() - ); - final Collection arguments = this.argumentExtractor.apply(method); - final Collection> flags = this.flagExtractor.apply(method); - final Map> commandArguments = new HashMap<>(); - final Map, String> argumentDescriptions = new HashMap<>(); - /* Go through all annotated parameters and build up the argument tree */ - for (final ArgumentParameterPair argumentPair : arguments) { - final String argumentName = this.processString(argumentPair.argumentName()); - final CommandArgument argument = this.buildArgument( - method, - this.findSyntaxFragment(tokens, argumentName), - argumentPair - ); - commandArguments.put(argument.getName(), argument); - argumentDescriptions.put(argument, this.processString(argumentPair.getArgument().description())); - } - boolean commandNameFound = false; - /* Build the command tree */ - for (final SyntaxFragment token : tokens) { - if (!commandNameFound) { - commandNameFound = true; - continue; - } - if (token.getArgumentMode() == ArgumentMode.LITERAL) { - builder = builder.literal(token.getMajor(), token.getMinor().toArray(new String[0])); - } else { - final CommandArgument argument = commandArguments.get(token.getMajor()); - if (argument == null) { - throw new IllegalArgumentException(String.format( - "Found no mapping for argument '%s' in method '%s'", - token.getMajor(), method.getName() - )); - } - - final String description = argumentDescriptions.getOrDefault(argument, ""); - builder = builder.argument(argument, ArgumentDescription.of(description)); - } - } - /* Try to find the command sender type */ - Class senderType = null; - for (final Parameter parameter : method.getParameters()) { - if (parameter.isAnnotationPresent(Argument.class)) { - continue; - } - if (GenericTypeReflector.isSuperType(this.commandSenderType.getType(), parameter.getType())) { - senderType = (Class) parameter.getType(); - break; - } - } - - final CommandPermission commandPermission = getMethodOrClassAnnotation(method, CommandPermission.class); - if (commandPermission != null) { - builder = builder.permission(this.processString(commandPermission.value())); - } - - if (commandMethod.requiredSender() != Object.class) { - builder = builder.senderType(commandMethod.requiredSender()); - } else if (senderType != null) { - builder = builder.senderType(senderType); - } - try { - final MethodCommandExecutionHandler.CommandMethodContext context = - new MethodCommandExecutionHandler.CommandMethodContext<>( - instance, - commandArguments, - method, - this /* annotationParser */ - ); - - /* Create the command execution handler */ - CommandExecutionHandler commandExecutionHandler = new MethodCommandExecutionHandler<>(context); - for (final Map.Entry, Function, - MethodCommandExecutionHandler>> entry - : this.commandMethodFactories.entrySet()) { - if (entry.getKey().test(method)) { - commandExecutionHandler = entry.getValue().apply(context); - - /* Once we have our custom handler, we stop */ - break; - } - } - - builder = builder.handler(commandExecutionHandler); - } catch (final Exception e) { - throw new RuntimeException("Failed to construct command execution handler", e); - } - /* Check if the command should be hidden */ - if (methodOrClassHasAnnotation(method, Hidden.class)) { - builder = builder.hidden(); - } - /* Apply flags */ - for (final CommandFlag flag : flags) { - builder = builder.flag(flag); - } - - /* Apply builder modifiers */ - for (final Annotation annotation - : AnnotationAccessor.of(classAnnotations, AnnotationAccessor.of(method)).annotations()) { - @SuppressWarnings("rawtypes") final BiFunction builderModifier = this.builderModifiers.get( - annotation.annotationType() - ); - if (builderModifier == null) { - continue; - } - builder = (Command.Builder) builderModifier.apply(annotation, builder); - } - - /* Construct and register the command */ - final Command builtCommand = builder.build(); - commands.add(builtCommand); - /* Check if we need to construct a proxy */ - if (method.isAnnotationPresent(ProxiedBy.class)) { - final ProxiedBy proxyAnnotation = method.getAnnotation(ProxiedBy.class); - final String proxy = this.processString(proxyAnnotation.value()); - if (proxy.contains(" ")) { - throw new IllegalArgumentException("@ProxiedBy proxies may only contain single literals"); - } - Command.Builder proxyBuilder = manager.commandBuilder(proxy, builtCommand.getCommandMeta()) - .proxies(builtCommand); - if (proxyAnnotation.hidden()) { - proxyBuilder = proxyBuilder.hidden(); - } - manager.command(proxyBuilder.build()); - } - } - return commands; - } - - private @NonNull SyntaxFragment findSyntaxFragment( - final @NonNull List<@NonNull SyntaxFragment> fragments, - final @NonNull String argumentName - ) { - for (final SyntaxFragment fragment : fragments) { - if (fragment.getArgumentMode() != ArgumentMode.LITERAL - && fragment.getMajor().equals(argumentName)) { - return fragment; - } - } - throw new IllegalArgumentException("Argument is not declared in syntax: " + argumentName); - } - - @SuppressWarnings("unchecked") - private @NonNull CommandArgument buildArgument( - final @NonNull Method method, - final @Nullable SyntaxFragment syntaxFragment, - final @NonNull ArgumentParameterPair argumentPair - ) { - final Parameter parameter = argumentPair.getParameter(); - final Collection annotations = Arrays.asList(parameter.getAnnotations()); - final TypeToken token = TypeToken.get(parameter.getParameterizedType()); - final ParserParameters parameters = this.manager.parserRegistry() - .parseAnnotations(token, annotations); - /* Create the argument parser */ - final ArgumentParser parser; - if (argumentPair.getArgument().parserName().isEmpty()) { - parser = this.manager.parserRegistry() - .createParser(token, parameters) - .orElseThrow(() -> new IllegalArgumentException( - String.format("Parameter '%s' in method '%s' " - + "has parser '%s' but no parser exists " - + "for that type", - parameter.getName(), method.getName(), - token.getType().getTypeName() - ))); - } else { - parser = this.manager.parserRegistry() - .createParser(this.processString(argumentPair.getArgument().parserName()), parameters) - .orElseThrow(() -> new IllegalArgumentException( - String.format("Parameter '%s' in method '%s' " - + "has parser '%s' but no parser exists " - + "for that type", - parameter.getName(), method.getName(), - token.getType().getTypeName() - ))); - } - /* Check whether or not the corresponding method parameter actually exists */ - final String argumentName = this.processString(argumentPair.argumentName()); - if (syntaxFragment == null || syntaxFragment.getArgumentMode() == ArgumentMode.LITERAL) { - throw new IllegalArgumentException(String.format( - "Invalid command argument '%s' in method '%s': " - + "Missing syntax mapping", argumentName, method.getName())); - } - final Argument argument = argumentPair.getArgument(); - /* Create the argument builder */ - @SuppressWarnings("rawtypes") final CommandArgument.Builder argumentBuilder = CommandArgument.ofType( - parameter.getType(), - argumentName - ); - /* Set the argument requirement status */ - if (syntaxFragment.getArgumentMode() == ArgumentMode.OPTIONAL) { - if (argument.defaultValue().isEmpty()) { - argumentBuilder.asOptional(); - } else { - argumentBuilder.asOptionalWithDefault(this.processString(argument.defaultValue())); - } - } else { - argumentBuilder.asRequired(); - } - /* Check for Completions annotation */ - final Completions completions = parameter.getDeclaredAnnotation(Completions.class); - if (completions != null) { - final List suggestions = Arrays.asList( - completions.value().replace(" ", "").split(",") - ); - argumentBuilder.withSuggestionsProvider((commandContext, input) -> suggestions); - } else if (!argument.suggestions().isEmpty()) { /* Check whether or not a suggestion provider should be set */ - final String suggestionProviderName = this.processString(argument.suggestions()); - final Optional, String, List>> suggestionsFunction = - this.manager.parserRegistry().getSuggestionProvider(suggestionProviderName); - argumentBuilder.withSuggestionsProvider( - suggestionsFunction.orElseThrow(() -> - new IllegalArgumentException(String.format( - "There is no suggestion provider with name '%s'. Did you forget to register it?", - suggestionProviderName - ))) - ); - } - /* Build the argument */ - final CommandArgument builtArgument = argumentBuilder.manager(this.manager).withParser(parser).build(); - /* Add preprocessors */ - for (final Annotation annotation : annotations) { - @SuppressWarnings("rawtypes") final Function preprocessorMapper = - this.preprocessorMappers.get(annotation.annotationType()); - if (preprocessorMapper != null) { - final BiFunction<@NonNull CommandContext, @NonNull Queue<@NonNull String>, - @NonNull ArgumentParseResult> preprocessor = (BiFunction, - Queue, ArgumentParseResult>) preprocessorMapper.apply(annotation); - builtArgument.addPreprocessor(preprocessor); - } - } - /* Yay, we're done */ - return builtArgument; - } - - @NonNull Map<@NonNull Class<@NonNull ? extends Annotation>, - @NonNull Function<@NonNull ? extends Annotation, @NonNull ParserParameters>> getAnnotationMappers() { - return this.annotationMappers; - } -} diff --git a/src/main/java/cloud/commandframework/annotations/MethodCommandExecutionHandler.java b/src/main/java/cloud/commandframework/annotations/MethodCommandExecutionHandler.java deleted file mode 100644 index 9deb50ba..00000000 --- a/src/main/java/cloud/commandframework/annotations/MethodCommandExecutionHandler.java +++ /dev/null @@ -1,290 +0,0 @@ -// -// MIT License -// -// Copyright (c) 2022 Alexander Söderberg & Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -package cloud.commandframework.annotations; - -import cloud.commandframework.annotations.injection.ParameterInjectorRegistry; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.flags.FlagContext; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.CommandExecutionException; -import cloud.commandframework.execution.CommandExecutionHandler; -import org.checkerframework.checker.nullness.qual.NonNull; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.command.cloud.exception.CommandPluginDisabledException; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static snw.kookbc.impl.command.cloud.CloudConstants.PLUGIN_KEY; - -/** - * A command execution handler that invokes a method. - * - * @param Command sender type. - * @since 1.6.0 (Was made public in 1.6.0) - */ -public class MethodCommandExecutionHandler implements CommandExecutionHandler { - private final CommandMethodContext context; - private final Parameter[] parameters; - private final MethodHandle methodHandle; - private final AnnotationAccessor annotationAccessor; - private final AnnotationParser annotationParser; - - /** - * Constructs a new method command execution handler - * - * @param context The context - * @since 1.6.0 - */ - public MethodCommandExecutionHandler( - final @NonNull CommandMethodContext context - ) throws Exception { - this.context = context; - this.methodHandle = MethodHandles.lookup().unreflect(context.method).bindTo(context.instance); - this.parameters = context.method.getParameters(); - this.annotationAccessor = AnnotationAccessor.of(context.method); - this.annotationParser = context.annotationParser(); - } - - /** - * {@inheritDoc} - */ - @Override - public void execute(final @NonNull CommandContext commandContext) { - /* Invoke the command method */ - Plugin plugin = commandContext.getOptional(PLUGIN_KEY).orElse(null); - if (plugin == null || !plugin.isEnabled()) { - throw new CommandPluginDisabledException(new RuntimeException("Plugin is disabled"), commandContext, plugin); - } - try { - this.methodHandle.invokeWithArguments( - this.createParameterValues( - commandContext, - commandContext.flags(), - this.parameters - ) - ); - } catch (final Error e) { - throw e; - } catch (final Throwable throwable) { - throw new CommandExecutionException(throwable, commandContext); - } - } - - /** - * Creates a list containing the values for the given method parameters. - * - * @param commandContext The context - * @param flagContext The flag context - * @param parameters The parameters to create values for - * @return A list containing all parameters, in order - * @since 1.6.0 - */ - protected final List createParameterValues( - final CommandContext commandContext, - final FlagContext flagContext, - final Parameter[] parameters - ) { - final List arguments = new ArrayList<>(parameters.length); - for (final Parameter parameter : parameters) { - if (parameter.isAnnotationPresent(Argument.class)) { - final Argument argument = parameter.getAnnotation(Argument.class); - - final String argumentName; - if (argument.value().equals(AnnotationParser.INFERRED_ARGUMENT_NAME)) { - argumentName = parameter.getName(); - } else { - argumentName = this.annotationParser.processString(argument.value()); - } - - final CommandArgument commandArgument = this.context.commandArguments.get(argumentName); - if (commandArgument.isRequired()) { - arguments.add(commandContext.get(argumentName)); - } else { - final Object optional = commandContext.getOptional(argumentName).orElse(null); - arguments.add(optional); - } - } else if (parameter.isAnnotationPresent(Flag.class)) { - final Flag flag = parameter.getAnnotation(Flag.class); - final String flagName = this.annotationParser.processString(flag.value()); - if (parameter.getType().equals(boolean.class)) { - arguments.add(flagContext.isPresent(flagName)); - } else if (flag.repeatable() && parameter.getType().isAssignableFrom(List.class)) { - arguments.add(flagContext.getAll(flagName)); - } else { - arguments.add(flagContext.getValue(flagName, null)); - } - } else { - if (parameter.getType().isAssignableFrom(commandContext.getSender().getClass())) { - arguments.add(commandContext.getSender()); - } else { - final Optional value = this.context.injectorRegistry.getInjectable( - parameter.getType(), - commandContext, - AnnotationAccessor.of(AnnotationAccessor.of(parameter), this.annotationAccessor) - ); - if (value.isPresent()) { - Object e = value.get(); - arguments.add(e); - } else if (commandContext.contains("ignore_null") && commandContext.>>get("ignore_null").contains(parameter.getType())) { - arguments.add(null); - } else { - throw new IllegalArgumentException(String.format( - "Could not create value for parameter '%s' of type '%s' in method '%s'", - parameter.getName(), - parameter.getType().getTypeName(), - this.methodHandle.toString() - )); - } - } - } - } - return arguments; - } - - /** - * Returns the command method context - * - * @return The context - * @since 1.6.0 - */ - public @NonNull CommandMethodContext context() { - return this.context; - } - - /** - * Returns all parameters passed to the method - * - * @return The parameters - * @since 1.6.0 - */ - public final @NonNull Parameter @NonNull [] parameters() { - return this.parameters; - } - - /** - * Returns the compiled method handle for the command method. - * - * @return The method handle - * @since 1.6.0 - */ - public final @NonNull MethodHandle methodHandle() { - return this.methodHandle; - } - - /** - * The annotation accessor for the command method - * - * @return Annotation accessor - * @since 1.6.0 - */ - public final AnnotationAccessor annotationAccessor() { - return this.annotationAccessor; - } - - /** - * Context for command methods - * - * @param Command sender type - * @since 1.6.0 - */ - public static class CommandMethodContext { - - private final Object instance; - private final Map> commandArguments; - private final Method method; - private final ParameterInjectorRegistry injectorRegistry; - private final AnnotationParser annotationParser; - - CommandMethodContext( - final @NonNull Object instance, - final @NonNull Map<@NonNull String, @NonNull CommandArgument<@NonNull C, @NonNull ?>> commandArguments, - final @NonNull Method method, - final @NonNull AnnotationParser annotationParser - ) { - this.instance = instance; - this.commandArguments = commandArguments; - this.method = method; - this.method.setAccessible(true); - this.injectorRegistry = annotationParser.getParameterInjectorRegistry(); - this.annotationParser = annotationParser; - } - - /** - * The instance that owns the command method - * - * @return The instance - * @since 1.6.0 - */ - public @NonNull Object instance() { - return this.instance; - } - - /** - * The command method - * - * @return The method - * @since 1.6.0 - */ - public final @NonNull Method method() { - return this.method; - } - - /** - * The compiled command arguments - * - * @return Compiled command arguments - * @since 1.6.0 - */ - public final @NonNull Map<@NonNull String, @NonNull CommandArgument> commandArguments() { - return this.commandArguments; - } - - /** - * The injector registry - * - * @return Injector registry - * @since 1.6.0 - */ - public final @NonNull ParameterInjectorRegistry injectorRegistry() { - return this.injectorRegistry; - } - - /** - * The annotation parser - * - * @return Annotation parser - * @since 1.7.0 - */ - public @NonNull AnnotationParser annotationParser() { - return this.annotationParser; - } - } -} diff --git a/src/main/java/cloud/commandframework/annotations/SyntaxParser.java b/src/main/java/cloud/commandframework/annotations/SyntaxParser.java deleted file mode 100644 index c3ee3901..00000000 --- a/src/main/java/cloud/commandframework/annotations/SyntaxParser.java +++ /dev/null @@ -1,75 +0,0 @@ -// -// MIT License -// -// Copyright (c) 2022 Alexander Söderberg & Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -package cloud.commandframework.annotations; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.StringTokenizer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -/** - * Parses command syntax into syntax fragments. - *

- * Public since 1.7.0. - */ -public final class SyntaxParser implements Function<@NonNull String, @NonNull List<@NonNull SyntaxFragment>> { - - private static final Predicate PATTERN_ARGUMENT_LITERAL = Pattern.compile("\\S+").asPredicate(); - private static final Predicate PATTERN_ARGUMENT_REQUIRED = Pattern.compile("<(\\S+)>").asPredicate(); - private static final Predicate PATTERN_ARGUMENT_OPTIONAL = Pattern.compile("\\[(\\S+)]").asPredicate(); - - @Override - public @NonNull List<@NonNull SyntaxFragment> apply(final @NonNull String syntax) { - final StringTokenizer stringTokenizer = new StringTokenizer(syntax, " "); - final List syntaxFragments = new ArrayList<>(); - while (stringTokenizer.hasMoreTokens()) { - final String token = stringTokenizer.nextToken(); - String major; - List minor = new ArrayList<>(); - ArgumentMode mode; - if (PATTERN_ARGUMENT_REQUIRED.test(token)) { - major = token.substring(1, token.length() - 1); - mode = ArgumentMode.REQUIRED; - } else if (PATTERN_ARGUMENT_OPTIONAL.test(token)) { - major = token.substring(1, token.length() - 1); - mode = ArgumentMode.OPTIONAL; - } else if (PATTERN_ARGUMENT_LITERAL.test(token)) { - final String[] literals = token.split("\\|"); - /* Actually use the other literals as well */ - major = literals[0]; - minor.addAll(Arrays.asList(literals).subList(1, literals.length)); - mode = ArgumentMode.LITERAL; - } else { - throw new IllegalArgumentException(String.format("Unrecognizable syntax token '%s'", syntax)); - } - syntaxFragments.add(new SyntaxFragment(major, minor, mode)); - } - return syntaxFragments; - } -} diff --git a/src/main/java/cloud/commandframework/arguments/CommandArgument.java b/src/main/java/cloud/commandframework/arguments/CommandArgument.java deleted file mode 100644 index 41911ac5..00000000 --- a/src/main/java/cloud/commandframework/arguments/CommandArgument.java +++ /dev/null @@ -1,829 +0,0 @@ -// -// MIT License -// -// Copyright (c) 2022 Alexander Söderberg & Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -package cloud.commandframework.arguments; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.Command; -import cloud.commandframework.CommandManager; -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.arguments.parser.ParserParameters; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.keys.CloudKey; -import cloud.commandframework.keys.CloudKeyHolder; -import cloud.commandframework.keys.SimpleCloudKey; -import io.leangen.geantyref.TypeToken; - -import java.util.*; -import java.util.function.BiFunction; -import java.util.regex.Pattern; - -/** - * AN argument that belongs to a command - * - * @param Command sender type - * @param The type that the argument parses into - */ -@SuppressWarnings({"unused", "UnusedReturnValue"}) -public class CommandArgument implements Comparable, CloudKeyHolder { - - /** - * Pattern for command argument names - */ - private static final Pattern NAME_PATTERN = Pattern.compile("\\S+"); -// private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9\\-_]+"); - - /** - * A typed key representing this argument - */ - private final CloudKey key; - /** - * Indicates whether the argument is required - * or not. All arguments prior to any other required - * argument must also be required, such that the predicate - * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, - * where {c_0, ..., c_n-1} is the set of command arguments. - */ - private final boolean required; - /** - * The command argument name. This might be exposed - * to command senders and so should be chosen carefully. - */ - private final String name; - /** - * The parser that is used to parse the command input - * into the corresponding command type - */ - private final ArgumentParser parser; - /** - * Default value, will be empty if none was supplied - */ - private final String defaultValue; - /** - * The type that is produces by the argument's parser - */ - private final TypeToken valueType; - /** - * Suggestion provider - */ - private final BiFunction, String, List> suggestionsProvider; - /** - * Argument preprocessors that allows for extensions to exist argument types - * without having to update all parsers - */ - private final Collection, - Queue, ArgumentParseResult>> argumentPreprocessors; - - /** - * A description that will be used when registering this argument if no override is provided. - */ - private final ArgumentDescription defaultDescription; - - /** - * Whether the argument has been used before - */ - private boolean argumentRegistered = false; - - private Command owningCommand; - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - * @param defaultDescription Default description to use when registering - * @param argumentPreprocessors Argument preprocessors - * @since 1.4.0 - */ - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final TypeToken valueType, - final BiFunction, String, List> suggestionsProvider, - final ArgumentDescription defaultDescription, - final Collection, Queue, - ArgumentParseResult>> argumentPreprocessors - ) { - this.required = required; - this.name = Objects.requireNonNull(name, "Name may not be null"); - if (!NAME_PATTERN.asPredicate().test(name)) { - throw new IllegalArgumentException("Name must not include space character"); - } - this.parser = Objects.requireNonNull(parser, "Parser may not be null"); - this.defaultValue = defaultValue; - this.valueType = valueType; - this.suggestionsProvider = suggestionsProvider == null - ? buildDefaultSuggestionsProvider(this) - : suggestionsProvider; - this.defaultDescription = Objects.requireNonNull(defaultDescription, "Default description may not be null"); - this.argumentPreprocessors = new LinkedList<>(argumentPreprocessors); - this.key = SimpleCloudKey.of(this.name, this.valueType); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - * @param argumentPreprocessors Argument preprocessors - */ - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final TypeToken valueType, - final BiFunction, String, List> suggestionsProvider, - final Collection, Queue, - ArgumentParseResult>> argumentPreprocessors - ) { - this( - required, - name, - parser, - defaultValue, - valueType, - suggestionsProvider, - ArgumentDescription.empty(), - argumentPreprocessors - ); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - */ - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final TypeToken valueType, - final BiFunction, String, List> suggestionsProvider - ) { - this(required, name, parser, defaultValue, valueType, suggestionsProvider, Collections.emptyList()); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - * @param defaultDescription Default description to use when registering - * @since 1.4.0 - */ - - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final TypeToken valueType, - final BiFunction, String, List> suggestionsProvider, - final ArgumentDescription defaultDescription - ) { - this(required, name, parser, defaultValue, valueType, suggestionsProvider, defaultDescription, Collections.emptyList()); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - */ - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final Class valueType, - final BiFunction, - String, List> suggestionsProvider - ) { - this(required, name, parser, defaultValue, TypeToken.get(valueType), suggestionsProvider); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param defaultValue Default value used when no value is provided by the command sender - * @param valueType Type produced by the parser - * @param suggestionsProvider Suggestions provider - * @param defaultDescription Default description to use when registering - * @since 1.4.0 - */ - - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final String defaultValue, - final Class valueType, - final BiFunction, - String, List> suggestionsProvider, - final ArgumentDescription defaultDescription - ) { - this(required, name, parser, defaultValue, TypeToken.get(valueType), suggestionsProvider, defaultDescription); - } - - /** - * Construct a new command argument - * - * @param required Whether the argument is required - * @param name The argument name - * @param parser The argument parser - * @param valueType Type produced by the parser - */ - public CommandArgument( - final boolean required, - final String name, - final ArgumentParser parser, - final Class valueType - ) { - this(required, name, parser, "", valueType, null); - } - - private static BiFunction, String, - List> buildDefaultSuggestionsProvider(final CommandArgument argument) { - return new DelegatingSuggestionsProvider<>(argument.getName(), argument.getParser()); - } - - /** - * Create a new command argument - * - * @param clazz Argument class - * @param name Argument name - * @param Command sender type - * @param Argument Type. Used to make the compiler happy. - * @return Argument builder - */ - public static CommandArgument.Builder ofType( - final TypeToken clazz, - final String name - ) { - return new Builder<>(clazz, name); - } - - /** - * Create a new command argument - * - * @param clazz Argument class - * @param name Argument name - * @param Command sender type - * @param Argument Type. Used to make the compiler happy. - * @return Argument builder - */ - public static CommandArgument.Builder ofType( - final Class clazz, - final String name - ) { - return new Builder<>(TypeToken.get(clazz), name); - } - - @Override - public final CloudKey getKey() { - return this.key; - } - - /** - * Check whether the command argument is required - * - * @return {@code true} if the argument is required, {@code false} if not - */ - public boolean isRequired() { - return this.required; - } - - /** - * Get the command argument name; - * - * @return Argument name - */ - public String getName() { - return this.name; - } - - /** - * Get the parser that is used to parse the command input - * into the corresponding command type - * - * @return Command parser - */ - public ArgumentParser getParser() { - return this.parser; - } - - @Override - public final String toString() { - return String.format("%s{name=%s}", this.getClass().getSimpleName(), this.name); - } - - /** - * Register a new preprocessor. If all preprocessor has succeeding {@link ArgumentParseResult results} - * that all return {@code true}, the argument will be passed onto the parser. - *

- * It is important that the preprocessor doesn't pop any input. Instead, it should only peek. - * - * @param preprocessor Preprocessor - * @return {@code this} - */ - public CommandArgument addPreprocessor( - final BiFunction, Queue, - ArgumentParseResult> preprocessor - ) { - this.argumentPreprocessors.add(preprocessor); - return this; - } - - /** - * Preprocess command input. This will immediately forward any failed argument parse results. - * If none fails, a {@code true} result will be returned - * - * @param context Command context - * @param input Remaining command input. None will be popped - * @return Parsing error, or argument containing {@code true} - */ - public ArgumentParseResult preprocess( - final CommandContext context, - final Queue input - ) { - for (final BiFunction, Queue, - ArgumentParseResult> preprocessor : this.argumentPreprocessors) { - final ArgumentParseResult result = preprocessor.apply( - context, - input - ); - if (result.getFailure().isPresent()) { - return result; - } - } - return ArgumentParseResult.success(true); - } - - /** - * Get the owning command - * - * @return Owning command - */ - public Command getOwningCommand() { - return this.owningCommand; - } - - /** - * Set the owning command - * - * @param owningCommand Owning command - */ - public void setOwningCommand(final Command owningCommand) { - if (this.owningCommand != null) { - throw new IllegalStateException("Cannot replace owning command"); - } - this.owningCommand = owningCommand; - } - - /** - * Get the argument suggestions provider - * - * @return Suggestions provider - */ - public final BiFunction, String, - List> getSuggestionsProvider() { - return this.suggestionsProvider; - } - - /** - * Get the default description to use when registering and no other is provided. - * - * @return the default description - */ - public final ArgumentDescription getDefaultDescription() { - return this.defaultDescription; - } - - @Override - public final boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final CommandArgument that = (CommandArgument) o; - return this.isRequired() == that.isRequired() && Objects.equals(this.getName(), that.getName()); - } - - @Override - public final int hashCode() { - return Objects.hash(this.isRequired(), this.getName()); - } - - @Override - public final int compareTo(final CommandArgument o) { - if (this instanceof StaticArgument) { - if (o instanceof StaticArgument) { - return this.getName().compareTo(o.getName()); - } else { - return -1; - } - } else { - if (o instanceof StaticArgument) { - return 1; - } else { - return 0; - } - } - } - - /** - * Get the default value - * - * @return Default value - */ - public String getDefaultValue() { - return this.defaultValue; - } - - /** - * Check if the argument has a default value - * - * @return {@code true} if the argument has a default value, {@code false} if not - */ - public boolean hasDefaultValue() { - return !this.isRequired() - && !this.getDefaultValue().isEmpty(); - } - - /** - * Get the type of this argument's value - * - * @return Value type - */ - public TypeToken getValueType() { - return this.valueType; - } - - /** - * Create a copy of the command argument - * - * @return Copied argument - */ - public CommandArgument copy() { - CommandArgument.Builder builder = ofType(this.valueType, this.name); - builder = builder.withSuggestionsProvider(this.suggestionsProvider); - builder = builder.withParser(this.parser); - if (this.isRequired()) { - builder = builder.asRequired(); - } else if (this.defaultValue.isEmpty()) { - builder = builder.asOptional(); - } else { - builder = builder.asOptionalWithDefault(this.defaultValue); - } - builder = builder.withDefaultDescription(this.defaultDescription); - - return builder.build(); - } - - /** - * Check whether the argument has been used in a command - * - * @return {@code true} if the argument has been used in a command, else {@code false} - */ - public boolean isArgumentRegistered() { - return this.argumentRegistered; - } - - /** - * Indicate that the argument has been associated with a command - */ - public void setArgumentRegistered() { - this.argumentRegistered = true; - } - - - /** - * Mutable builder for {@link CommandArgument} instances - * - * @param Command sender type - * @param Argument value type - */ - public static class Builder { - - private final TypeToken valueType; - private final String name; - - private CommandManager manager; - private boolean required = true; - private ArgumentParser parser; - private String defaultValue = ""; - private BiFunction, String, List> suggestionsProvider; - private ArgumentDescription defaultDescription = ArgumentDescription.empty(); - - private final Collection, - String, ArgumentParseResult>> argumentPreprocessors = new LinkedList<>(); - - protected Builder( - final TypeToken valueType, - final String name - ) { - this.valueType = valueType; - this.name = name; - } - - protected Builder( - final Class valueType, - final String name - ) { - this(TypeToken.get(valueType), name); - } - - /** - * Set the command manager. Will be used to create a default parser - * if none was provided - * - * @param manager Command manager - * @return Builder instance - */ - public Builder manager(final CommandManager manager) { - this.manager = manager; - return this; - } - - /** - * Indicates that the argument is required. - * All arguments prior to any other required - * argument must also be required, such that the predicate - * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, - * where {c_0, ..., c_n-1} is the set of command arguments. - * - * @return Builder instance - */ - public Builder asRequired() { - this.required = true; - return this; - } - - /** - * Indicates that the argument is optional. - * All arguments prior to any other required - * argument must also be required, such that the predicate - * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, - * where {c_0, ..., c_n-1} is the set of command arguments. - * - * @return Builder instance - */ - public Builder asOptional() { - this.required = false; - return this; - } - - /** - * Indicates that the argument is optional. - * All arguments prior to any other required - * argument must also be required, such that the predicate - * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, - * where {c_0, ..., c_n-1} is the set of command arguments. - * - * @param defaultValue Default value that will be used if none was supplied - * @return Builder instance - */ - public Builder asOptionalWithDefault(final String defaultValue) { - this.defaultValue = defaultValue; - this.required = false; - return this; - } - - /** - * Set the argument parser - * - * @param parser Argument parser - * @return Builder instance - */ - public Builder withParser(final ArgumentParser parser) { - this.parser = Objects.requireNonNull(parser, "Parser may not be null"); - return this; - } - - /** - * Set the suggestions provider - * - * @param suggestionsProvider Suggestions provider - * @return Builder instance - */ - public Builder withSuggestionsProvider( - final BiFunction, - String, List> suggestionsProvider - ) { - this.suggestionsProvider = suggestionsProvider; - return this; - } - - /** - * Set the default description to be used for this argument. - * - *

The default description is used when no other description is provided for a certain argument.

- * - * @param defaultDescription The default description - * @return Builder instance - * @since 1.4.0 - */ - - public Builder withDefaultDescription( - final ArgumentDescription defaultDescription - ) { - this.defaultDescription = Objects.requireNonNull(defaultDescription, "Default description may not be null"); - return this; - } - - /** - * Construct a command argument from the builder settings - * - * @return Constructed argument - */ - public CommandArgument build() { - if (this.parser == null && this.manager != null) { - this.parser = this.manager.parserRegistry().createParser(this.valueType, ParserParameters.empty()) - .orElse(null); - } - if (this.parser == null) { - this.parser = (c, i) -> ArgumentParseResult - .failure(new UnsupportedOperationException("No parser was specified")); - } - if (this.suggestionsProvider == null) { - this.suggestionsProvider = new DelegatingSuggestionsProvider<>(this.name, this.parser); - } - return new CommandArgument<>( - this.required, - this.name, - this.parser, - this.defaultValue, - this.valueType, - this.suggestionsProvider, - this.defaultDescription - ); - } - - protected final String getName() { - return this.name; - } - - protected final boolean isRequired() { - return this.required; - } - - protected final ArgumentParser getParser() { - return this.parser; - } - - protected final String getDefaultValue() { - return this.defaultValue; - } - - protected final BiFunction, String, List> - getSuggestionsProvider() { - return this.suggestionsProvider; - } - - protected final ArgumentDescription getDefaultDescription() { - return this.defaultDescription; - } - - protected final TypeToken getValueType() { - return this.valueType; - } - } - - /** - * A variant of builders designed for subclassing, that returns a self type. - * - * @param sender type - * @param argument value type - * @param the subclass type - * @since 1.5.0 - */ - public abstract static class TypedBuilder> extends Builder { - - protected TypedBuilder( - final TypeToken valueType, - final String name - ) { - super(valueType, name); - } - - protected TypedBuilder( - final Class valueType, - final String name - ) { - super(valueType, name); - } - - @SuppressWarnings("unchecked") - protected final B self() { - return (B) this; - } - - /** - * {@inheritDoc} - */ - @Override - public B manager(final CommandManager manager) { - super.manager(manager); - return this.self(); - } - - /** - * {@inheritDoc} - */ - @Override - public B asRequired() { - super.asRequired(); - return this.self(); - } - - /** - * {@inheritDoc} - */ - @Override - public B asOptional() { - super.asOptional(); - return this.self(); - } - - /** - * {@inheritDoc} - */ - @Override - public B asOptionalWithDefault(final String defaultValue) { - super.asOptionalWithDefault(defaultValue); - return this.self(); - } - - /** - * {@inheritDoc} - */ - @Override - public B withParser(final ArgumentParser parser) { - super.withParser(parser); - return this.self(); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder withSuggestionsProvider( - final BiFunction, - String, List> suggestionsProvider - ) { - super.withSuggestionsProvider(suggestionsProvider); - return this.self(); - } - } -} diff --git a/src/main/java/snw/kookbc/impl/KBCClient.java b/src/main/java/snw/kookbc/impl/KBCClient.java index 979477a7..11bcd04f 100644 --- a/src/main/java/snw/kookbc/impl/KBCClient.java +++ b/src/main/java/snw/kookbc/impl/KBCClient.java @@ -32,10 +32,10 @@ import snw.jkook.plugin.UnknownDependencyException; import snw.jkook.util.Validate; import snw.kookbc.SharedConstants; -import snw.kookbc.impl.command.cloud.CloudCommandManagerImpl; -import snw.kookbc.impl.command.internal.CloudHelpCommand; +import snw.kookbc.impl.command.CommandManagerImpl; import snw.kookbc.impl.command.internal.HelpCommand; -import snw.kookbc.impl.command.internal.PluginsCommand; +import snw.kookbc.impl.command.litecommands.LiteKookFactory; +import snw.kookbc.impl.command.litecommands.internal.PluginsCommand; import snw.kookbc.impl.console.Console; import snw.kookbc.impl.entity.builder.EntityBuilder; import snw.kookbc.impl.entity.builder.MessageBuilder; @@ -129,7 +129,7 @@ public KBCClient( this.internalPlugin = new InternalPlugin(this); this.core.init(this); /*Cloud*/ - this.commandManager = Optional.ofNullable(commandManager).orElseGet(() -> CloudCommandManagerImpl::new).apply(this); + this.commandManager = Optional.ofNullable(commandManager).orElseGet(() -> CommandManagerImpl::new).apply(this); this.networkClient = Optional.ofNullable(networkClient).orElseGet(() -> c -> new NetworkClient(c, token)).apply(this); this.storage = Optional.ofNullable(storage).orElseGet(() -> EntityStorage::new).apply(this); this.entityBuilder = Optional.ofNullable(entityBuilder).orElseGet(() -> EntityBuilder::new).apply(this); @@ -516,24 +516,23 @@ protected void registerStopCommand() { } protected void registerPluginsCommand() { - new JKookCommand("plugins") - .setDescription("获取已安装到此 " + SharedConstants.IMPL_NAME + " 实例的插件列表。") - .setExecutor(new PluginsCommand(this)) - .register(getInternalPlugin()); +// new JKookCommand("plugins") +// .setDescription("获取已安装到此 " + SharedConstants.IMPL_NAME + " 实例的插件列表。") +// .setExecutor(new PluginsCommand(this)) +// .register(getInternalPlugin()); + LiteKookFactory.builder(getInternalPlugin()) + .commands(new PluginsCommand(this)) + .build() + ; } protected void registerHelpCommand() { - if (core.getCommandManager() instanceof CloudCommandManagerImpl) { - ((CloudCommandManagerImpl) core.getCommandManager()) - .registerCloudCommand(internalPlugin, new CloudHelpCommand(this)); - } else { - HelpCommand executor = new HelpCommand(this); - new JKookCommand("help") - .setDescription("获取此帮助列表。") - .executesUser(executor) - .executesConsole(executor) - .register(getInternalPlugin()); - } + HelpCommand executor = new HelpCommand(this); + new JKookCommand("help") + .setDescription("获取此帮助列表。") + .executesUser(executor) + .executesConsole(executor) + .register(getInternalPlugin()); this.core.getEventManager() .registerHandlers(this.internalPlugin, new UserClickButtonListener(this)); } diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudBasedCommandManager.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudBasedCommandManager.java deleted file mode 100644 index 4d1a7e39..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudBasedCommandManager.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.CloudCapability; -import cloud.commandframework.Command; -import cloud.commandframework.CommandManager; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.standard.StringArrayArgument; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.context.CommandContextFactory; -import cloud.commandframework.context.StandardCommandContextFactory; -import cloud.commandframework.exceptions.*; -import cloud.commandframework.execution.CommandExecutionCoordinator; -import cloud.commandframework.execution.CommandResult; -import cloud.commandframework.internal.CommandInputTokenizer; -import cloud.commandframework.internal.CommandRegistrationHandler; -import cloud.commandframework.meta.CommandMeta; -import cloud.commandframework.meta.SimpleCommandMeta; -import cloud.commandframework.services.State; -import io.leangen.geantyref.TypeToken; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.jetbrains.annotations.NotNull; -import snw.jkook.command.CommandException; -import snw.jkook.command.CommandSender; -import snw.jkook.command.ConsoleCommandSender; -import snw.jkook.command.JKookCommand; -import snw.jkook.entity.Guild; -import snw.jkook.entity.User; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.KBCClient; -import snw.kookbc.impl.command.cloud.exception.CommandPluginDisabledException; -import snw.kookbc.impl.command.cloud.parser.GuildArgumentParser; -import snw.kookbc.impl.command.cloud.parser.PluginArgumentParser; -import snw.kookbc.impl.command.cloud.parser.UserArgumentParser; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static snw.kookbc.impl.command.cloud.CloudConstants.*; - -/** - * @author huanmeng_qwq - */ -@SuppressWarnings("unused") -public class CloudBasedCommandManager extends CommandManager { - protected final KBCClient client; - private final CloudCommandManagerImpl parent; - private final CommandContextFactory commandContextFactory = new StandardCommandContextFactory<>(); - - CloudBasedCommandManager(KBCClient client, @NonNull CloudCommandManagerImpl parent) { - super(CommandExecutionCoordinator.simpleCoordinator(), CommandRegistrationHandler.nullCommandRegistrationHandler()); - this.client = client; - this.parent = parent; - registerCapability(CloudCapability.StandardCapabilities.ROOT_COMMAND_DELETION); - parserRegistry().registerParserSupplier(TypeToken.get(Plugin.class), p -> new PluginArgumentParser(client)); - parserRegistry().registerParserSupplier(TypeToken.get(User.class), p -> new UserArgumentParser(client)); - parserRegistry().registerParserSupplier(TypeToken.get(Guild.class), p -> new GuildArgumentParser(client)); - registerCommandPostProcessor(context -> { - Command<@NonNull CommandSender> command = context.getCommand(); - CommandContext<@NonNull CommandSender> commandContext = context.getCommandContext(); - commandContext.set(PLUGIN_KEY, command.getCommandMeta().get(PLUGIN_KEY).orElse(null)); - if (!(commandContext.getSender() instanceof ConsoleCommandSender)) { - logCommand(commandContext.getSender(), commandContext.getRawInputJoined()); - } - }); - } - - @Override - public boolean hasPermission(@NonNull CommandSender sender, @NonNull String permission) { - return true; - } - - @Override - public @NonNull CommandMeta createDefaultCommandMeta() { - return SimpleCommandMeta.empty(); - } - - public void registerJKookCommand(Plugin plugin, JKookCommand jKookCommand) { - StringArrayArgument args = StringArrayArgument.optional("args", (commandSenderCommandContext, s) -> Collections.emptyList()); - List commands = withPrefix(jKookCommand, jKookCommand.getRootName()); - commands.addAll(withPrefixes(jKookCommand, jKookCommand.getAliases())); - Iterator iterator = commands.iterator(); - if (!iterator.hasNext()) { - return; - } - List alias = new ArrayList<>(); - String mainCommand = iterator.next(); - while (iterator.hasNext()) { - alias.add(iterator.next()); - } - Collection prefixes = jKookCommand.getPrefixes(); - command( - commandBuilder( - mainCommand, - alias, - SimpleCommandMeta.builder() - .with(CommandMeta.DESCRIPTION, jKookCommand.getDescription()) - .with(PLUGIN_KEY, plugin) - .with(PREFIX_KEY, prefixes) - .with(ALIAS_KEY, alias) - .with(JKOOK_COMMAND_KEY, true) - .with(HELP_CONTENT_KEY, jKookCommand.getHelpContent()) - .build() - ) - .handler(new CloudWrappedCommandExecutionHandler(client, parent, jKookCommand)) - .argument(args) - .build() - ); - } - - public void unregisterJKookCommand(JKookCommand jKookCommand) { - for (String commandName : withPrefix(jKookCommand, jKookCommand.getRootName())) { - deleteRootCommand(commandName); - } - } - - protected List withPrefixes(JKookCommand jKookCommand, Collection commands) { - return commands.stream().flatMap(command -> withPrefix(jKookCommand, command).stream()).collect(Collectors.toList()); - } - - protected List withPrefix(JKookCommand jKookCommand, String cmd) { - Collection prefixes = jKookCommand.getPrefixes(); - ArrayList list = new ArrayList<>(prefixes.size()); - for (String prefix : prefixes) { - list.add(prefix + cmd); - } - return list; - } - - protected List removePrefix(String[] prefixes, String... commands) { - if (prefixes.length == 0) { - return Arrays.asList(commands); - } - List list = new ArrayList<>(commands.length); - for (String command : commands) { - boolean match = false; - for (String prefix : prefixes) { - if (command.startsWith(prefix)) { - match = true; - list.add(command.substring(prefix.length())); - } - } - if (!match) { - list.add(command); - } - } - return list; - } - - public boolean executeCommandNow(@NonNull CommandSender commandSender, @NonNull String input, Message message) throws CommandException { - AtomicReference unhandledException = new AtomicReference<>(); - AtomicBoolean foundCommand = new AtomicBoolean(true); - try { - executeCommand(commandSender, input, message) - .whenComplete((commandResult, throwable) -> handleThrowable(commandSender, message, unhandledException, foundCommand, throwable)).get(); - } catch (InterruptedException | ExecutionException ignored) { // impossible - } - if (unhandledException.get() != null) { - throw new CommandException("Something unexpected happened.", unhandledException.get()); - } - return foundCommand.get(); - } - - protected void handleThrowable(@NotNull CommandSender commandSender, Message message, AtomicReference unhandledException, AtomicBoolean foundCommand, Throwable throwable) { - if (throwable instanceof CompletionException) { - throwable = throwable.getCause(); - } - if (throwable instanceof CommandExecutionException) { - throwable = throwable.getCause(); - } - final Throwable finalThrowable = throwable; - if (throwable instanceof InvalidSyntaxException) { - handleException(commandSender, - InvalidSyntaxException.class, - (InvalidSyntaxException) throwable, (c, e) -> { - if (message != null) { - message.sendToSource("指令语法错误。正确的用法: " - + ((InvalidSyntaxException) finalThrowable) - .getCorrectSyntax()); - } else { - client.getCore().getLogger().info("指令语法错误。正确的用法: " - + ((InvalidSyntaxException) finalThrowable) - .getCorrectSyntax()); - } - } - ); - } else if (throwable instanceof InvalidCommandSenderException) { - handleException(commandSender, - InvalidCommandSenderException.class, - (InvalidCommandSenderException) throwable, (c, e) -> { - if (message != null) { - message.sendToSource(finalThrowable.getMessage()); - } else { - client.getCore().getLogger().info(finalThrowable.getMessage()); - } - } - ); - } else if (throwable instanceof NoPermissionException) { - handleException(commandSender, - NoPermissionException.class, - (NoPermissionException) throwable, (c, e) -> { - if (message != null) { - message.sendToSource("您没有执行此命令的权限"); - } else { - client.getCore().getLogger().info("您没有执行此命令的权限"); - } - } - ); - } else if (throwable instanceof NoSuchCommandException) { - handleException(commandSender, - NoSuchCommandException.class, - (NoSuchCommandException) throwable, (c, e) -> foundCommand.set(false) - ); - } else if (throwable instanceof ArgumentParseException) { - handleException(commandSender, - ArgumentParseException.class, - (ArgumentParseException) throwable, (c, e) -> { - if (message != null) { - message.sendToSource("无效的命令参数: " - + finalThrowable.getCause().getMessage()); - } else { - client.getCore().getLogger().info("无效的命令参数: " - + finalThrowable.getCause().getMessage()); - } - } - ); - } else if (throwable instanceof CommandPluginDisabledException) { - handleException(commandSender, - CommandPluginDisabledException.class, - (CommandPluginDisabledException) throwable, (c, e) -> { - if (message != null) { - message.sendToSource("无法执行命令: 注册此命令的插件现已被禁用。"); - } else { - client.getCore().getLogger().info("Unable to execute command: The owner plugin: ({}) of this command was disabled.", e.getPlugin() == null ? "null" : e.getPlugin().getDescription().getName()); - } - } - ); - } else if (throwable != null) { - if (message != null) { - message.sendToSource("尝试执行此命令时发生内部错误"); - } else { - client.getCore().getLogger().info("尝试执行此命令时发生内部错误"); - } - unhandledException.set(throwable); // provide the unhandled exception - } - } - - @Override - public final @NonNull CompletableFuture> executeCommand(@NonNull CommandSender commandSender, @NonNull String input) { - throw new UnsupportedOperationException(); - } - - @NonNull - public CompletableFuture> executeCommand(@NonNull CommandSender commandSender, @NonNull String input, Message message) { - return executeCommand(commandSender, input, message, commandSender instanceof ConsoleCommandSender); - } - - // prefix:full_command_name - public Map> collectCommands() { - LinkedHashMap> map = new LinkedHashMap<>(); - map.put("", new LinkedHashSet<>()); - for (Command command : commands()) { - map.get("").add(command.getArguments().get(0).getName()); - Collection prefixes = command.getCommandMeta().get(PREFIX_KEY).orElse(null); - if (prefixes == null) { - continue; - } - Collection aliases = command.getCommandMeta().get(ALIAS_KEY).orElse(Collections.emptyList()); - checkPrefix(map, prefixes.toArray(new String[0])); - for (String prefix : prefixes) { - map.get(prefix).addAll(aliases); - map.get(prefix).add(command.getArguments().get(0).getName()); - } - } - return map; - } - - private void checkPrefix(Map> map, String... prefixes) { - for (String prefix : prefixes) { - if (!map.containsKey(prefix)) { - map.put(prefix, new LinkedHashSet<>()); - } - } - } - - @NonNull - public CompletableFuture> executeCommand(@NonNull CommandSender commandSender, @NonNull String input, Message message, boolean ignorePrefix) { - final CommandContext context = commandContextFactory.create( - false, - commandSender, - this - ); - if (message != null) { - context.set(KOOK_MESSAGE_KEY, message); - } - if (commandSender instanceof ConsoleCommandSender) { - logCommand(commandSender, input); - } - final LinkedList inputQueue = new CommandInputTokenizer(input).tokenize(); - /* Store a copy of the input queue in the context */ - context.store("__raw_input__", new LinkedList<>(inputQueue)); - if (ignorePrefix && !inputQueue.isEmpty()) { - String command = inputQueue.getFirst(); - EACH: - for (Map.Entry> entry : collectCommands().entrySet()) { - String prefix = entry.getKey(); - LinkedHashSet commands = entry.getValue(); - for (String cmd : commands) { - if (cmd.substring(prefix.length()).equalsIgnoreCase(command)) { - command = cmd; - break EACH; - } - } - } - inputQueue.set(0, command); - } - try { - CompletableFuture> future = new CompletableFuture<>(); - if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) { - commandExecutionCoordinator().coordinateExecution(context, inputQueue).whenComplete(((commandSenderCommandResult, throwable) -> { - if (commandSenderCommandResult != null) { - future.complete(commandSenderCommandResult); - } else if (throwable != null) { - future.completeExceptionally(throwable); - } - })); - } - return future; - } catch (final Exception e) { - final CompletableFuture> future = new CompletableFuture<>(); - if (!future.completeExceptionally(e)) { - throw e; - } - return future; - } - } - - private void logCommand(@NotNull CommandSender commandSender, @NotNull String input) { - if (commandSender instanceof ConsoleCommandSender) { - client.getCore().getLogger().info( - "Console issued command: {}", - input - ); - } else if (commandSender instanceof User) { - client.getCore().getLogger().info( - "{}(User ID: {}) issued command: {}", - ((User) commandSender).getName(), - ((User) commandSender).getId(), - input - ); - } - } - - public void unregisterAll(Plugin plugin) { - List> list = commands().stream() - .filter(i -> - i.getCommandMeta().get(PLUGIN_KEY) - .orElseThrow( - () -> new IllegalStateException( - "Internal error: commands does not have plugin meta!" - ) - ) - == plugin - ) - .map(i -> i.getArguments().get(0)).collect(Collectors.toList()); - for (CommandArgument i : list) { - deleteRootCommand(i.getName()); - } - } - - public List getCommandsInfo() { - List result = new ArrayList<>(); - for (Command command : commands()/*.stream().filter(i -> i.getCommandMeta().get(PLUGIN_KEY).isPresent()).collect(Collectors.toList())*/) { - String[] prefixes = command.getCommandMeta().get(PREFIX_KEY).map(e -> e.toArray(new String[0])).orElse(new String[0]); - String[] aliases = command.getCommandMeta().get(ALIAS_KEY).map(e -> e.toArray(new String[0])).orElse(new String[0]); - String rootName = removePrefix(prefixes, command.getArguments().get(0).getName()).get(0); - String syntax = rootName; - String[] finalAliases = removePrefix(prefixes, aliases).stream().distinct().filter(e -> !e.equals(rootName)).toArray(String[]::new); - Boolean jKookCommand = command.getCommandMeta().getOrDefault(JKOOK_COMMAND_KEY, false); - if (!jKookCommand) { - syntax = commandSyntaxFormatter().apply(command.getArguments(), null); - } - result.add(new CloudCommandInfo( - command.getCommandMeta().get(PLUGIN_KEY).orElse(null), - rootName, - syntax, - finalAliases, - prefixes, - command.getCommandMeta().get(CommandMeta.DESCRIPTION).orElse(""), - command.getCommandMeta().get(HELP_CONTENT_KEY).orElse(""), - jKookCommand, - command.isHidden() - ) - ); - } - return result; - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandExecutor.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandExecutor.java deleted file mode 100644 index 218f1d0d..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandExecutor.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.exceptions.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import snw.jkook.command.CommandExecutor; -import snw.jkook.command.CommandSender; -import snw.jkook.command.JKookCommand; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; - -import java.util.concurrent.CompletionException; - -/** - * @author huanmeng_qwq - */ -public class CloudCommandExecutor implements CommandExecutor { - private final Plugin plugin; - private final CloudBasedCommandManager manager; - private final JKookCommand root; - private final String rootName; - - public CloudCommandExecutor(@NotNull Plugin plugin, @NotNull CloudBasedCommandManager manager, @NotNull JKookCommand root, @NotNull String rootName) { - this.plugin = plugin; - this.manager = manager; - this.root = root; - this.rootName = rootName; - } - - @Override - public void onCommand(@NotNull CommandSender sender, @NotNull Object[] objects, @Nullable Message message) { - final StringBuilder builder = new StringBuilder(rootName); - for (final Object obj : objects) { - builder.append(" ").append(obj); - } - manager.executeCommand(sender, builder.toString(), message) - .whenComplete((commandResult, throwable) -> { - if (throwable instanceof CompletionException) { - throwable = throwable.getCause(); - } - final Throwable finalThrowable = throwable; - if (throwable instanceof InvalidSyntaxException) { - manager.handleException(sender, - InvalidSyntaxException.class, - (InvalidSyntaxException) throwable, (c, e) -> - replay(message, "指令语法错误。正确的用法: " - + root.getPrefixes().iterator().next() - + ((InvalidSyntaxException) finalThrowable) - .getCorrectSyntax()) - ); - } else if (throwable instanceof InvalidCommandSenderException) { - manager.handleException(sender, - InvalidCommandSenderException.class, - (InvalidCommandSenderException) throwable, (c, e) -> - replay(message, e.getRequiredSender().equals(CommandSender.class) - ? "该指令只能由KOOK 用户执行" - : "该指令只能由控制台执行") - ); - } else if (throwable instanceof NoPermissionException) { - manager.handleException(sender, - NoPermissionException.class, - (NoPermissionException) throwable, (c, e) -> - replay(message, "你没有权限执行该指令") - ); - }/* else if (throwable instanceof NoSuchCommandException) { - manager.handleException(sender, - NoSuchCommandException.class, - (NoSuchCommandException) throwable, (c, e) -> - replay(message, "Unknown command. Type \"/help\" for help.") - ); - }*/ else if (throwable instanceof ArgumentParseException) { - manager.handleException(sender, - ArgumentParseException.class, - (ArgumentParseException) throwable, (c, e) -> - replay(message, "无效的命令参数: " - + finalThrowable.getCause().getMessage()) - ); - } else if (throwable instanceof CommandExecutionException) { - manager.handleException(sender, - CommandExecutionException.class, - (CommandExecutionException) throwable, (c, e) -> { - replay(message, "尝试执行此命令时发生内部错误"); - plugin.getLogger().error( - "Exception executing command handler", finalThrowable.getCause() - ); - } - ); - } else if (throwable != null) { - replay(message, "尝试执行此命令时发生内部错误"); - plugin.getLogger().error("An unhandled exception was thrown during command execution", - throwable - ); - } - }).join(); - } - - public void replay(@Nullable Message message, String content) { - if (message != null) { - message.reply(content); - } else { - plugin.getLogger().info(content); - } - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandInfo.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandInfo.java deleted file mode 100644 index 00284607..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandInfo.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package snw.kookbc.impl.command.cloud; - -import snw.jkook.plugin.Plugin; - -import java.util.Arrays; - -/** - * @author huanmeng_qwq - */ -public class CloudCommandInfo { - private final Plugin owningPlugin; - private final String rootName; - private final String syntax; - private final String[] aliases; - private final String[] prefixes; - private final String description; - private final String helpContent; - private final boolean isJKookCommand; - private final boolean hidden; - - public CloudCommandInfo(Plugin owningPlugin, String rootName, String syntax, String[] aliases, String[] prefixes, String description, String helpContent, boolean isJKookCommand, boolean hidden) { - this.owningPlugin = owningPlugin; - this.rootName = rootName; - this.syntax = syntax; - this.aliases = aliases; - this.prefixes = prefixes; - this.description = description; - this.helpContent = helpContent; - this.isJKookCommand = isJKookCommand; - this.hidden = hidden; - } - - public Plugin owningPlugin() { - return owningPlugin; - } - - public String rootName() { - return rootName; - } - - public String syntax() { - return syntax; - } - - public String[] aliases() { - return aliases; - } - - public String[] prefixes() { - return prefixes; - } - - public String description() { - return description; - } - - public String helpContent() { - return helpContent; - } - - public boolean isJKookCommand() { - return isJKookCommand; - } - - @Override - public String toString() { - return "CloudCommandInfo{" + - "owningPlugin=" + owningPlugin.getDescription().getName() + - ", rootName='" + rootName + '\'' + - ", syntax='" + syntax + '\'' + - ", aliases=" + Arrays.toString(aliases) + - ", prefixes=" + Arrays.toString(prefixes) + - ", description='" + description + '\'' + - ", helpContent='" + helpContent + '\'' + - ", isJKookCommand=" + isJKookCommand + - ", hidden=" + hidden + - '}'; - } - - public boolean hidden() { - return hidden; - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandManagerImpl.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandManagerImpl.java deleted file mode 100644 index 3e283b0f..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandManagerImpl.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.annotations.AnnotationParser; -import cloud.commandframework.arguments.parser.ParserParameters; -import cloud.commandframework.arguments.parser.StandardParameters; -import cloud.commandframework.meta.CommandMeta; -import cloud.commandframework.meta.SimpleCommandMeta; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import snw.jkook.command.CommandException; -import snw.jkook.command.CommandSender; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.KBCClient; -import snw.kookbc.impl.command.CommandManagerImpl; - -import java.util.List; -import java.util.function.Function; - -import static snw.kookbc.impl.command.cloud.CloudConstants.PLUGIN_KEY; - -/** - * @author huanmeng_qwq - */ -public class CloudCommandManagerImpl extends CommandManagerImpl { - private final CloudBasedCommandManager manager; - - public CloudCommandManagerImpl(KBCClient client) { - this(client, new CloudCommandMap()); - getCommandMap().init(this); - } - - public CloudCommandManagerImpl(KBCClient client, CloudCommandMap commandMap) { - super(client, commandMap); - this.manager = new CloudBasedCommandManager(client, this); - } - - @Override - public boolean executeCommand(CommandSender sender, String cmdLine, Message msg) throws CommandException { - if (cmdLine.isEmpty()) { - client.getCore().getLogger().debug("Received empty command!"); - return false; - } - - long startTimeStamp = System.currentTimeMillis(); // debug - - boolean result; - try { - result = getCloudCommandManager().executeCommandNow(sender, cmdLine, msg); - } catch (Throwable e) { - client.getCore().getLogger().debug("The execution of command line '{}' is FAILED, time elapsed: {}ms", cmdLine, System.currentTimeMillis() - startTimeStamp); - // Why Throwable? We need to keep the client safe. - // it is easy to understand. NoClassDefError? NoSuchMethodError? - // It is OutOfMemoryError? nothing matters lol. - throw new CommandException("Something unexpected happened.", e); - } - // Do not put this in the try statement because we don't know if the logging system will throw an exception. - client.getCore().getLogger().debug("The execution of command line \"{}\" is done, time elapsed: {}ms", cmdLine, System.currentTimeMillis() - startTimeStamp); - return result; - } - - public CloudBasedCommandManager getCloudCommandManager() { - return manager; - } - - public void registerCloudCommands(@NotNull AnnotationParser annotationParser, @NotNull Plugin plugin) { - annotationParser.parse(plugin.getClass().getClassLoader()); - } - - public void registerCloudCommands(@NotNull CloudBasedCommandManager commandManager, @NotNull Plugin plugin, @Nullable Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper) { - Function wrapped = wrap(plugin, metaMapper); - registerCloudCommands(CloudCommandBuilder.createParser(commandManager, wrapped), plugin); - } - - public void registerCloudCommands(@NotNull Plugin plugin, @Nullable Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper) { - registerCloudCommands(getCloudCommandManager(), plugin, metaMapper); - } - - public void registerCloudCommands(@NotNull Plugin plugin) { - registerCloudCommands(plugin, null); - } - - public void registerCloudCommand(@NotNull Plugin plugin, @NotNull CloudBasedCommandManager commandManager, @Nullable Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper, @NotNull Object instance) { - CloudCommandBuilder.createParser(commandManager, wrap(plugin, metaMapper)).parse(instance); - } - - public void registerCloudCommand(@NotNull AnnotationParser annotationParser, @NotNull Object instance) { - annotationParser.parse(instance); - } - - public void registerCloudCommand(@NotNull Plugin plugin, @Nullable Function<@NonNull ParserParameters, @Nullable CommandMeta> metaMapper, @NotNull Object instance) { - registerCloudCommand(plugin, getCloudCommandManager(), metaMapper, instance); - } - - public void registerCloudCommand(@NotNull Plugin plugin, @NotNull Object instance) { - registerCloudCommand(plugin, (Function) null, instance); - } - - public void registerCloudCommand(@NotNull Plugin plugin, @NotNull CloudBasedCommandManager commandManager, @NotNull Object instance) { - registerCloudCommand(plugin, commandManager, null, instance); - } - - @Override - public CloudCommandMap getCommandMap() { - return (CloudCommandMap) super.getCommandMap(); - } - - protected static Function wrap(Plugin plugin, Function origin) { - return p -> { - SimpleCommandMeta.Builder builder = SimpleCommandMeta.builder() - .with(PLUGIN_KEY, plugin) - .with(CommandMeta.DESCRIPTION, p.get(StandardParameters.DESCRIPTION, "")); - if (origin != null) { - builder.with(origin.apply(p)); - } - return builder.build(); - }; - } - - public List getCommandsInfo() { - return getCloudCommandManager().getCommandsInfo(); - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandMap.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandMap.java deleted file mode 100644 index e189c042..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandMap.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package snw.kookbc.impl.command.cloud; - -import snw.jkook.command.JKookCommand; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.command.SimpleCommandMap; - -import java.util.List; - -/** - * @author huanmeng_qwq - */ -public class CloudCommandMap extends SimpleCommandMap { - protected CloudCommandManagerImpl parent; - - public void init(CloudCommandManagerImpl parent) { - if (this.parent != null) { - throw new IllegalStateException("Already initialized"); - } - this.parent = parent; - } - - public void register(Plugin plugin, JKookCommand command) { - super.register(plugin, command); - this.parent.getCloudCommandManager().registerJKookCommand(plugin, command); - } - - public void unregister(JKookCommand command) { - super.unregister(command); - this.parent.getCloudCommandManager().unregisterJKookCommand(command); - } - - public void unregisterAll(Plugin plugin) { - super.unregisterAll(plugin); - this.parent.getCloudCommandManager().unregisterAll(plugin); - } - - protected Plugin getOwnerOfCommand(JKookCommand command) { - return getView(false).get(command.getRootName()).getPlugin(); - } - - public List getCommandsInfo() { - return parent.getCommandsInfo(); - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudConstants.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudConstants.java deleted file mode 100644 index e04627fd..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudConstants.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.keys.CloudKey; -import cloud.commandframework.keys.SimpleCloudKey; -import cloud.commandframework.meta.CommandMeta; -import io.leangen.geantyref.TypeToken; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; - -import java.util.Collection; - -/** - * @author huanmeng_qwq - */ -public final class CloudConstants { - public static final CommandMeta.Key PLUGIN_KEY = CommandMeta.Key.of(Plugin.class, "kookbc_plugin"); - public static final CommandMeta.Key> PREFIX_KEY = CommandMeta.Key.of(new TypeToken>() { - }, "kookbc_prefixes"); - public static final CommandMeta.Key> ALIAS_KEY = CommandMeta.Key.of(new TypeToken>() { - }, "kookbc_aliases"); - public static final CommandMeta.Key JKOOK_COMMAND_KEY = CommandMeta.Key.of(Boolean.class, "is_jkook_command"); - public static final CommandMeta.Key HELP_CONTENT_KEY = CommandMeta.Key.of(String.class, "jkook_help_content"); - public static final CloudKey KOOK_MESSAGE_KEY = SimpleCloudKey.of("kook_message", TypeToken.get(Message.class)); - - private CloudConstants() { - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudKookCommandBuilder.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudKookCommandBuilder.java deleted file mode 100644 index 4fc42cd9..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudKookCommandBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.Command; -import cloud.commandframework.CommandManager; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.StaticArgument; -import cloud.commandframework.meta.CommandMeta; -import org.jetbrains.annotations.Nullable; -import snw.jkook.command.CommandSender; -import snw.jkook.command.JKookCommand; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.command.CommandMap; -import snw.kookbc.impl.command.WrappedCommand; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * @author huanmeng_qwq, SNWCreations - */ -public interface CloudKookCommandBuilder { - - @SuppressWarnings("unchecked") - @Nullable - static JKookCommand build(Plugin plugin, CloudBasedCommandManager manager, Command command, CommandMap commandMap) { - final CommandArgument commandArgument = command.getArguments().get(0); - boolean overwrite = manager.getSetting(CommandManager.ManagerSettings.OVERRIDE_EXISTING_COMMANDS); - Map wrappedCommandMap = commandMap.getView(false); - - String rootName = commandArgument.getName(); - List names = new ArrayList<>(); - names.add(rootName); - names.addAll( - ((StaticArgument) commandArgument).getAlternativeAliases() - ); - - names.removeIf(name -> wrappedCommandMap.containsKey(name) && !overwrite); - - if (names.isEmpty()) { - return null; - } - - JKookCommand kookCommand = new JKookCommand(names.remove(0)); - for (String name : names) { - kookCommand.addAlias(name); - } - kookCommand.setExecutor(new CloudCommandExecutor(plugin, manager, kookCommand, rootName)); - kookCommand.setDescription(command.getCommandMeta().get(CommandMeta.DESCRIPTION).orElse(null)); - - return kookCommand; - } - -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudWrappedCommandExecutionHandler.java b/src/main/java/snw/kookbc/impl/command/cloud/CloudWrappedCommandExecutionHandler.java deleted file mode 100644 index 01c06667..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudWrappedCommandExecutionHandler.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package snw.kookbc.impl.command.cloud; - -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.execution.CommandExecutionHandler; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.jetbrains.annotations.Nullable; -import snw.jkook.command.CommandSender; -import snw.jkook.command.ConsoleCommandSender; -import snw.jkook.command.JKookCommand; -import snw.jkook.entity.User; -import snw.jkook.message.Message; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.KBCClient; -import snw.kookbc.impl.command.CommandManagerImpl; -import snw.kookbc.impl.command.UnknownArgumentException; -import snw.kookbc.impl.command.cloud.exception.CommandPluginDisabledException; - -import java.util.*; - -import static snw.kookbc.impl.command.cloud.CloudConstants.KOOK_MESSAGE_KEY; -import static snw.kookbc.impl.command.cloud.CloudConstants.PLUGIN_KEY; -import static snw.kookbc.util.Util.toEnglishNumOrder; - -/** - * @author huanmeng_qwq - */ -public class CloudWrappedCommandExecutionHandler implements CommandExecutionHandler { - - protected final KBCClient client; - - private final CommandManagerImpl parent; - private final JKookCommand commandObject; - - public CloudWrappedCommandExecutionHandler(KBCClient client, CommandManagerImpl parent, JKookCommand commandObject) { - this.client = client; - this.parent = parent; - this.commandObject = commandObject; - } - - @Override - public void execute(@NonNull CommandContext commandContext) { - @SuppressWarnings("unchecked") - String[] rawInput = ((List) commandContext.get("__raw_input__")).toArray(new String[0]); - - JKookCommand command = commandObject; - - CommandSender sender = commandContext.getSender(); - Message message = commandContext.getOrDefault(KOOK_MESSAGE_KEY, null); - List list = new ArrayList<>(Arrays.asList(rawInput)); - if (!list.isEmpty()) { - list.remove(0); // remove head - } - Collection sub = command.getSubcommands(); - if (!sub.isEmpty()) { // if the command have subcommand, expect true - client.getCore().getLogger().debug("The subcommand does exists. Attempting to search the final command."); - while (!list.isEmpty()) { - // get the first argument, so we got "a" - String subName = list.get(0); - client.getCore().getLogger().debug("Got temp subcommand root name: {}", subName); - - boolean found = false; // expect true - // then get the command - for (JKookCommand s : sub) { - // if the root name equals to the sub name - if (Objects.equals(s.getRootName(), subName)) { // expect true - client.getCore().getLogger().debug("Got valid subcommand: {}", subName); // debug - // then remove the first argument - list.remove(0); // "a" was removed, so we have "b" in next round - command = s; // got "a" subcommand - found = true; // found - sub = command.getSubcommands(); // update the search target, so we can search deeply - break; // it's not necessary to continue - } - } - if (!found) { // if the subcommand is not found - client.getCore().getLogger().debug("No subcommand matching current command root name. We will attempt to execute the command currently found."); // debug - // then we can regard the actualCommand as the final result to be executed - break; // exit the while loop - } - } - } - Plugin plugin = commandContext.getOptional(PLUGIN_KEY).orElse(null); - if (plugin == null || !plugin.isEnabled()) { - throw new CommandPluginDisabledException(new RuntimeException("Plugin is disabled"), commandContext, plugin); - } - - Object[] arguments; - try { - arguments = parent.processArguments(command, list); - } catch (NoSuchElementException e) { - reply("执行命令失败: 参数不足。", "Unable to execute command: No enough arguments.", sender, message); - return; - } catch (UnknownArgumentException e) { - reply( - "执行命令失败: 无法解析第 " + e.argIndex() + " 个参数。", - "Unable to execute command: unable to parse the " + toEnglishNumOrder(e.argIndex()) + " argument.", - sender, message - ); - return; - } - - if (sender instanceof User && command.getUserCommandExecutor() != null) { - command.getUserCommandExecutor().onCommand((User) sender, arguments, message); - } else if (sender instanceof ConsoleCommandSender && command.getConsoleCommandExecutor() != null) { - command.getConsoleCommandExecutor().onCommand((ConsoleCommandSender) sender, arguments); - } else if (command.getExecutor() != null) { - command.getExecutor().onCommand(sender, arguments, message); - } else { - reply( - "执行命令失败: 此命令已注册,但它是一个空壳,没有可用的命令逻辑。", - "No executor was registered for provided command line.", - sender, message); - } - } - - private void reply(String content, String contentForConsole, CommandSender sender, @Nullable Message message) { - if (sender instanceof ConsoleCommandSender) { - client.getCore().getLogger().info(contentForConsole); - } else if (sender instanceof User) { - // contentForConsole should be null at this time - if (message != null) { - message.reply(content); - } else { - ((User) sender).sendPrivateMessage(content); - } - } - } - -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/exception/CommandPluginDisabledException.java b/src/main/java/snw/kookbc/impl/command/cloud/exception/CommandPluginDisabledException.java deleted file mode 100644 index 981fd70b..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/exception/CommandPluginDisabledException.java +++ /dev/null @@ -1,33 +0,0 @@ -package snw.kookbc.impl.command.cloud.exception; - -import cloud.commandframework.context.CommandContext; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import snw.jkook.plugin.Plugin; - -/** - * 2023/7/23
- * KookBC
- * - * @author huanmeng_qwq - */ -public class CommandPluginDisabledException extends IllegalArgumentException { - private static final long serialVersionUID = 5973116780767261548L; - - private final CommandContext commandContext; - private final Plugin plugin; - - public CommandPluginDisabledException(@NonNull Throwable cause, @Nullable CommandContext commandContext, Plugin plugin) { - super(cause); - this.commandContext = commandContext; - this.plugin = plugin; - } - - public Plugin getPlugin() { - return plugin; - } - - public CommandContext getCommandContext() { - return commandContext; - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/parser/GuildArgumentParser.java b/src/main/java/snw/kookbc/impl/command/cloud/parser/GuildArgumentParser.java deleted file mode 100644 index b399521c..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/parser/GuildArgumentParser.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud.parser; - -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.context.CommandContext; -import org.checkerframework.checker.nullness.qual.NonNull; -import snw.jkook.command.CommandException; -import snw.jkook.command.CommandSender; -import snw.jkook.entity.Guild; -import snw.kookbc.impl.KBCClient; - -import java.util.Queue; - -/** - * @author huanmeng_qwq - */ -public class GuildArgumentParser implements @NonNull ArgumentParser { - private final KBCClient client; - - public GuildArgumentParser(KBCClient client) { - this.client = client; - } - - @Override - public @NonNull ArgumentParseResult<@NonNull Guild> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull Queue<@NonNull String> inputQueue) { - final String input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new cloud.commandframework.exceptions.parsing.NoInputProvidedException(GuildArgumentParser.class, commandContext)); - } - try { - Guild guild = client.getCore().getHttpAPI().getGuild(input); - if (guild == null) { - return ArgumentParseResult.failure(new CommandException("Guild not found")); - } - inputQueue.remove(); - return ArgumentParseResult.success(guild); - } catch (final Exception e) { - return ArgumentParseResult.failure(new CommandException("Guild not found")); - } - } -} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/parser/PluginArgumentParser.java b/src/main/java/snw/kookbc/impl/command/cloud/parser/PluginArgumentParser.java deleted file mode 100644 index d53ab766..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/parser/PluginArgumentParser.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud.parser; - -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import org.checkerframework.checker.nullness.qual.NonNull; -import snw.jkook.command.CommandException; -import snw.jkook.command.CommandSender; -import snw.jkook.plugin.Plugin; -import snw.kookbc.impl.KBCClient; - -import java.util.Arrays; -import java.util.List; -import java.util.Queue; -import java.util.stream.Collectors; - -/** - * @author huanmeng_qwq - */ -public class PluginArgumentParser implements ArgumentParser { - private final KBCClient client; - - public PluginArgumentParser(KBCClient client) { - this.client = client; - } - - @Override - public @NonNull ArgumentParseResult<@NonNull Plugin> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull Queue<@NonNull String> inputQueue) { - final String input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(PluginArgumentParser.class, commandContext)); - } - try { - Plugin plugin = Arrays.stream(client.getCore().getPluginManager().getPlugins()) - .filter(p -> p.getDescription().getName().equalsIgnoreCase(input)) - .findFirst() - .orElse(null); - if (plugin == null) { - return ArgumentParseResult.failure(new CommandException("Plugin not found")); - } - inputQueue.remove(); - return ArgumentParseResult.success(plugin); - } catch (final Exception e) { - return ArgumentParseResult.failure(e); - } - } - - @Override - public @NonNull List<@NonNull String> suggestions(@NonNull CommandContext commandContext, @NonNull String input) { - return Arrays.stream(client.getCore().getPluginManager().getPlugins()).map(plugin -> plugin.getDescription().getName()).collect(Collectors.toList()); - } -} \ No newline at end of file diff --git a/src/main/java/snw/kookbc/impl/command/cloud/parser/UserArgumentParser.java b/src/main/java/snw/kookbc/impl/command/cloud/parser/UserArgumentParser.java deleted file mode 100644 index 8c561179..00000000 --- a/src/main/java/snw/kookbc/impl/command/cloud/parser/UserArgumentParser.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.cloud.parser; - -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import org.checkerframework.checker.nullness.qual.NonNull; -import snw.jkook.command.CommandException; -import snw.jkook.command.CommandSender; -import snw.jkook.entity.User; -import snw.kookbc.impl.KBCClient; - -import java.util.Queue; - -/** - * @author huanmeng_qwq - */ -public class UserArgumentParser implements ArgumentParser { - private final KBCClient client; - - public UserArgumentParser(KBCClient client) { - this.client = client; - } - - @Override - public @NonNull ArgumentParseResult<@NonNull User> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull Queue<@NonNull String> inputQueue) { - String input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(UserArgumentParser.class, commandContext)); - } - if (input.startsWith("(met)")) { - input = input.substring(5); - } - if (input.endsWith("(met)")) { - input = input.substring(0, input.length() - 5); - } - try { - User user = client.getCore().getHttpAPI().getUser(input); - if (user == null) { - return ArgumentParseResult.failure(new CommandException("User not found")); - } - inputQueue.remove(); - return ArgumentParseResult.success(user); - } catch (final Exception e) { - return ArgumentParseResult.failure(new CommandException("User not found")); - } - } -} diff --git a/src/main/java/snw/kookbc/impl/command/internal/CloudHelpCommand.java b/src/main/java/snw/kookbc/impl/command/internal/CloudHelpCommand.java deleted file mode 100644 index 34f6ed1c..00000000 --- a/src/main/java/snw/kookbc/impl/command/internal/CloudHelpCommand.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. - * Copyright (C) 2022 - 2023 KookBC contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package snw.kookbc.impl.command.internal; - -import cloud.commandframework.annotations.Argument; -import cloud.commandframework.annotations.CommandDescription; -import cloud.commandframework.annotations.CommandMethod; -import cloud.commandframework.annotations.Flag; -import cloud.commandframework.annotations.processing.CommandContainer; -import cloud.commandframework.annotations.specifier.Quoted; -import org.jetbrains.annotations.Nullable; -import snw.jkook.command.CommandSender; -import snw.jkook.entity.User; -import snw.jkook.message.Message; -import snw.jkook.message.PrivateMessage; -import snw.jkook.message.component.card.CardBuilder; -import snw.jkook.message.component.card.Size; -import snw.jkook.message.component.card.Theme; -import snw.jkook.message.component.card.element.ButtonElement; -import snw.jkook.message.component.card.element.MarkdownElement; -import snw.jkook.message.component.card.element.PlainTextElement; -import snw.jkook.message.component.card.module.*; -import snw.kookbc.SharedConstants; -import snw.kookbc.impl.KBCClient; -import snw.kookbc.impl.command.cloud.CloudCommandInfo; -import snw.kookbc.impl.command.cloud.CloudCommandManagerImpl; -import snw.kookbc.impl.command.cloud.annotations.CommandPrefix; -import snw.kookbc.util.Util; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import static snw.kookbc.impl.command.internal.HelpCommand.EMPTY_PLAIN_TEXT_ELEMENT; -import static snw.kookbc.impl.command.internal.HelpCommand.HELP_VALUE_HEADER; - -/** - * @author huanmeng_qwq - */ -@CommandContainer -@CommandPrefix("/") -@CommandMethod("help") -public class CloudHelpCommand { - - private KBCClient client; - - public CloudHelpCommand(KBCClient client) { - this.client = client; - } - - // 这里是因为构建时会判断是否会有空构造器,否则会报错。 - public CloudHelpCommand() { - } - - @CommandMethod("[command]") - @CommandDescription("获取此帮助列表。") - public void consoleHelp(CommandSender sender, Message message, @Argument("command") @Quoted @Nullable String command, @Flag("force") boolean force) { - - String messageType; - if (message instanceof PrivateMessage) { - messageType = "PM"; - } else { - messageType = "CM"; - } - - if (sender instanceof User) { - List content = Util.listCloudCommandsHelp(this.client, force); - CloudCommandInfo specificCommand = Util.findSpecificCloudCommand(this.client, command); - CardBuilder finalBuilder; - if (content.isEmpty() && specificCommand == null) { - finalBuilder = new CardBuilder() - .setTheme(Theme.DANGER) - .setSize(Size.LG) - .addModule(new HeaderModule("找不到命令")); - } else if (specificCommand != null) { - finalBuilder = new CardBuilder() - .setTheme(Theme.SUCCESS) - .setSize(Size.LG) - .addModule(new HeaderModule("命令帮助")) - .addModule(DividerModule.INSTANCE) - .addModule(new SectionModule(new MarkdownElement( - String.format("**命令**: %s", specificCommand.syntax()) - ))) - .addModule(new SectionModule(new MarkdownElement( - String.format("**别称**: %s", String.join(" ", specificCommand.aliases())) - ))) - .addModule(new SectionModule(new MarkdownElement( - String.format("**可用前缀**: %s", String.join(" ", specificCommand.prefixes())) - ))) - .addModule(new SectionModule(new MarkdownElement( - Util.limit( - String.format("**简介**: %s", - (Util.isBlank(specificCommand.description())) - ? "此命令没有简介。" - : specificCommand.description() - ), - 4997 - ) - ))); - if (!Util.isBlank(specificCommand.helpContent())) { - finalBuilder.addModule(new SectionModule(new MarkdownElement( - Util.limit(String.format("**详细帮助信息**:\n%s", specificCommand.helpContent()), 4997) - ))); - } - } else { - int totalPages = content.size() % 5 == 0 ? content.size() / 5 : content.size() / 5 + 1; - CardBuilder builder = new CardBuilder() - .setTheme(Theme.SUCCESS) - .setSize(Size.LG) - .addModule(new HeaderModule(String.format("命令帮助 (1/%d)", totalPages))) - .addModule(DividerModule.INSTANCE); - content.removeIf(IT -> IT.startsWith("(/)stop:")); - if (content.size() <= 5) { - content.stream() - .map(SectionModule::new) - .forEachOrdered(builder::addModule); - } else { - content.stream() - .limit(5L) - .map(SectionModule::new) - .forEachOrdered(builder::addModule); - builder.addModule(DividerModule.INSTANCE) - .addModule(new ActionGroupModule( - Arrays.asList( - new ButtonElement( - Theme.PRIMARY, - HELP_VALUE_HEADER + "{\"page\": 0, \"current\": 1, \"force\": " + force + ",\"messageType\": " + messageType + "}", // Placeholder - ButtonElement.EventType.NO_ACTION, - new PlainTextElement("上一页") - ), - new ButtonElement(Theme.SECONDARY, "", EMPTY_PLAIN_TEXT_ELEMENT), // Placeholder - new ButtonElement(Theme.SECONDARY, "", EMPTY_PLAIN_TEXT_ELEMENT), // Placeholder - new ButtonElement( - Theme.PRIMARY, - HELP_VALUE_HEADER + "{\"page\": 2, \"current\": 1, \"force\": " + force + ",\"messageType\": " + messageType + "}", - ButtonElement.EventType.RETURN_VAL, - new PlainTextElement("下一页") - ) - ) - - )); - } - finalBuilder = builder; - } - if (client.getConfig().getBoolean("allow-help-ad", true)) { - finalBuilder.addModule(DividerModule.INSTANCE) - .addModule(new ContextModule( - Collections.singletonList( - new MarkdownElement( - String.format( - "由 [%s](%s) v%s 驱动 - %s API %s", - SharedConstants.IMPL_NAME, - SharedConstants.REPO_URL, - SharedConstants.IMPL_VERSION, - SharedConstants.SPEC_NAME, - client.getCore().getAPIVersion() - ) - ) - ) - )); - } - message.sendToSource(finalBuilder.build()); - } else { - List content = buildHelpContent(command, force); - if (content.isEmpty()) { - client.getCore().getLogger().info("Commands is empty."); - } else { - for (String line : content) { - client.getCore().getLogger().info(line); - } - } - } - } - - private List buildHelpContent(@Nullable String target, boolean force) { - List commands = ((CloudCommandManagerImpl) client.getCore().getCommandManager()).getCommandsInfo(); - if (target != null && !target.isEmpty()) { - commands = commands.stream() - .filter(command -> target.equalsIgnoreCase("all") || command.rootName().equalsIgnoreCase(target) || - command.syntax().equalsIgnoreCase(target) || - Arrays.stream(command.aliases()) - .anyMatch(alias -> alias.equalsIgnoreCase(target)) - ) - .collect(Collectors.toList()); - } - if (!force) { - commands = commands.stream().filter(command -> command.owningPlugin() != null && command.owningPlugin().isEnabled()).collect(Collectors.toList()); - } - List result = new LinkedList<>(); - result.add("-------- 命令帮助 --------"); - if (commands.size() > 1) { - for (CloudCommandInfo command : commands) { - result.add( - String.format("(%s)%s: %s", - String.join(" ", - command.prefixes()), - command.syntax(), - (Util.isBlank(command.description())) ? "此命令没有简介。" : command.description() - ) - ); - } - result.add(""); // the blank line as the separator - result.add("注: 在每条命令帮助的开头,括号中用空格隔开的字符为此命令的前缀。"); - result.add("如 \"(/ .)blah\" 即 \"/blah\", \".blah\" 为同一条命令。"); - } else if (commands.size() == 1) { - CloudCommandInfo command = commands.get(0); - result.add(String.format("命令: %s", command.syntax())); - if (command.aliases().length > 0) { - result.add(String.format("别称: %s", String.join(" ", command.aliases()))); - } - result.add(String.format("可用前缀: %s", String.join(" ", command.prefixes()))); - result.add( - String.format("简介: %s", - (Util.isBlank(command.description())) - ? "此命令没有简介。" - : command.description() - ) - ); - if (!Util.isBlank(command.helpContent())) { - result.add("详细帮助信息:"); - result.add(command.helpContent()); - } - } else { - return Collections.emptyList(); - } - result.add("-------------------------"); - return result; - } -} diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/KookLitePlatform.java b/src/main/java/snw/kookbc/impl/command/litecommands/KookLitePlatform.java new file mode 100644 index 00000000..4c50b1b1 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/KookLitePlatform.java @@ -0,0 +1,77 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.command.CommandRoute; +import dev.rollczi.litecommands.meta.Meta; +import dev.rollczi.litecommands.meta.MetaKey; +import dev.rollczi.litecommands.meta.MetaType; +import dev.rollczi.litecommands.platform.AbstractPlatform; +import dev.rollczi.litecommands.platform.PlatformInvocationListener; +import dev.rollczi.litecommands.platform.PlatformSuggestionListener; +import org.jetbrains.annotations.NotNull; +import snw.jkook.command.CommandExecutor; +import snw.jkook.command.CommandSender; +import snw.jkook.command.JKookCommand; +import snw.jkook.plugin.Plugin; +import snw.kookbc.impl.command.CommandMap; +import snw.kookbc.impl.command.WrappedCommand; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class KookLitePlatform extends AbstractPlatform { + public static final MetaKey> PREFIX = MetaKey.of("kookbc_prefix", MetaType.set(), new HashSet<>(Collections.singletonList("/"))); + + private final Plugin plugin; + private final CommandMap commandMap; + + protected KookLitePlatform(@NotNull LiteKookSettings settings, Plugin plugin, CommandMap commandMap) { + super(settings); + this.plugin = plugin; + this.commandMap = commandMap; + } + + @Override + protected void hook(CommandRoute commandRoute, PlatformInvocationListener platformInvocationListener, PlatformSuggestionListener platformSuggestionListener) { + for (String label : commandRoute.names()) { + List desc = commandRoute.meta().get(Meta.DESCRIPTION); + Set prefixes = commandRoute.meta().get(PREFIX); + // List perms = commandRoute.meta().get(Meta.PERMISSIONS); + JKookCommand command = new JKookCommand(label, prefixes) + .setDescription(String.join("\n", desc)) + .setExecutor(new LiteKookCommandExecutor(plugin.getCore(), settings, commandRoute, label, platformInvocationListener, platformSuggestionListener)); + commandMap.register(plugin, command); + } + } + + @Override + protected void unhook(CommandRoute commandRoute) { + for (WrappedCommand wrappedCommand : commandMap.getView(false).values()) { + CommandExecutor executor = wrappedCommand.getCommand().getExecutor(); + if (executor instanceof LiteKookCommandExecutor) { + if (commandRoute.isNameOrAlias(wrappedCommand.getCommand().getRootName())) { + commandMap.unregister(wrappedCommand.getCommand()); + } + } + } + } +} diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/KookSender.java b/src/main/java/snw/kookbc/impl/command/litecommands/KookSender.java new file mode 100644 index 00000000..a3687654 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/KookSender.java @@ -0,0 +1,57 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.identifier.Identifier; +import dev.rollczi.litecommands.platform.AbstractPlatformSender; +import snw.jkook.command.CommandSender; +import snw.jkook.entity.User; + +class KookSender extends AbstractPlatformSender { + + private final CommandSender handle; + + public KookSender(CommandSender handle) { + this.handle = handle; + } + + @Override + public String getName() { + if (this.handle instanceof User) { + return ((User) this.handle).getName(); + } + + return handle.getClass().getSimpleName(); + } + + @Override + public Identifier getIdentifier() { + if (this.handle instanceof User) { + return Identifier.of(((User) this.handle).getId()); + } + + return Identifier.CONSOLE; + } + + @Override + public boolean hasPermission(String permission) { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookCommandExecutor.java b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookCommandExecutor.java new file mode 100644 index 00000000..a7fe1702 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookCommandExecutor.java @@ -0,0 +1,79 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.argument.parser.input.ParseableInput; +import dev.rollczi.litecommands.argument.suggester.input.SuggestionInput; +import dev.rollczi.litecommands.command.CommandRoute; +import dev.rollczi.litecommands.invocation.Invocation; +import dev.rollczi.litecommands.invocation.InvocationContext; +import dev.rollczi.litecommands.platform.PlatformInvocationListener; +import dev.rollczi.litecommands.platform.PlatformSuggestionListener; +import org.jetbrains.annotations.Nullable; +import snw.jkook.Core; +import snw.jkook.command.CommandExecutor; +import snw.jkook.command.CommandSender; +import snw.jkook.message.Message; + +import java.util.Arrays; + +public class LiteKookCommandExecutor implements CommandExecutor { + private final Core core; + private final LiteKookSettings settings; + private final CommandRoute commandSection; + private final String label; + private final PlatformInvocationListener executeListener; + private final PlatformSuggestionListener suggestionListener; + + public LiteKookCommandExecutor(Core core, LiteKookSettings settings, CommandRoute commandSection, String label, PlatformInvocationListener executeListener, PlatformSuggestionListener suggestionListener) { + this.core = core; + this.settings = settings; + this.commandSection = commandSection; + this.label = label; + this.executeListener = executeListener; + this.suggestionListener = suggestionListener; + } + + @Override + public void onCommand(CommandSender commandSender, Object[] objects, @Nullable Message message) { + ParseableInput input = ParseableInput.raw(Arrays.stream(objects).map(Object::toString).toArray(String[]::new)); + KookSender platformSender = new KookSender(commandSender); + InvocationContext invocationContext = createContext(message); + Invocation invocation = new Invocation<>(commandSender, platformSender, this.commandSection.getName(), this.label, input, invocationContext); + + this.executeListener.execute(invocation, input); + } + + public Iterable getSuggestions(CommandSender sender, String[] args) { + SuggestionInput input = SuggestionInput.raw(args); + KookSender platformSender = new KookSender(sender); + InvocationContext invocationContext = createContext(null); + Invocation invocation = new Invocation<>(sender, platformSender, this.commandSection.getName(), this.label, input, invocationContext); + + return this.suggestionListener.suggest(invocation, input) + .asMultiLevelList(); + } + + private InvocationContext createContext(@Nullable Message message) { + InvocationContext.Builder builder = InvocationContext.builder(); + builder.put(Core.class, core) + .put(Message.class, message); + return builder.build(); + } +} diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookFactory.java b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookFactory.java new file mode 100644 index 00000000..472112bc --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookFactory.java @@ -0,0 +1,54 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.LiteCommandsBuilder; +import dev.rollczi.litecommands.LiteCommandsFactory; +import snw.jkook.Core; +import snw.jkook.command.CommandSender; +import snw.jkook.command.ConsoleCommandSender; +import snw.jkook.entity.User; +import snw.jkook.message.Message; +import snw.jkook.plugin.Plugin; +import snw.kookbc.impl.command.CommandManagerImpl; +import snw.kookbc.impl.command.litecommands.tools.KookMessageContextual; +import snw.kookbc.impl.command.litecommands.tools.KookOnlyConsoleContextual; +import snw.kookbc.impl.command.litecommands.tools.KookOnlyUserContextual; + +public class LiteKookFactory { + private LiteKookFactory() { + } + + public static > B builder(Plugin plugin) { + return builder(plugin, new LiteKookSettings()); + } + + @SuppressWarnings("unchecked") + public static > B builder(Plugin plugin, LiteKookSettings liteBungeeSettings) { + return (B) new WrappedLiteCommandsBuilder<>( + LiteCommandsFactory.builder(CommandSender.class, new KookLitePlatform(liteBungeeSettings, plugin, ((CommandManagerImpl) plugin.getCore().getCommandManager()).getCommandMap())) + .bind(Core.class, plugin::getCore) + .context(Message.class, new KookMessageContextual()) + .context(User.class, new KookOnlyUserContextual<>("只有用户才能执行该命令")) + .context(ConsoleCommandSender.class, new KookOnlyConsoleContextual<>("只有后台才能执行该命令")) + + .result(String.class, new StringHandler()) + ); + } +} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandHelpContent.java b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookSettings.java similarity index 65% rename from src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandHelpContent.java rename to src/main/java/snw/kookbc/impl/command/litecommands/LiteKookSettings.java index 74965d7c..5d65d58a 100644 --- a/src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandHelpContent.java +++ b/src/main/java/snw/kookbc/impl/command/litecommands/LiteKookSettings.java @@ -16,18 +16,19 @@ * along with this program. If not, see . */ -package snw.kookbc.impl.command.cloud.annotations; +package snw.kookbc.impl.command.litecommands; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import dev.rollczi.litecommands.platform.PlatformSettings; -/** - * @author huanmeng_qwq - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -public @interface CommandHelpContent { - String[] value() default {}; +public class LiteKookSettings implements PlatformSettings { + private boolean nativePermissions = false; + + public LiteKookSettings nativePermissions(boolean nativePermissions) { + this.nativePermissions = nativePermissions; + return this; + } + + boolean isNativePermissions() { + return this.nativePermissions; + } } diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/StringHandler.java b/src/main/java/snw/kookbc/impl/command/litecommands/StringHandler.java new file mode 100644 index 00000000..10cc17c0 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/StringHandler.java @@ -0,0 +1,46 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.handler.result.ResultHandler; +import dev.rollczi.litecommands.handler.result.ResultHandlerChain; +import dev.rollczi.litecommands.invocation.Invocation; +import snw.jkook.command.CommandSender; +import snw.jkook.command.ConsoleCommandSender; +import snw.jkook.entity.User; +import snw.jkook.message.Message; + +class StringHandler implements ResultHandler { + + @Override + public void handle(Invocation invocation, String result, ResultHandlerChain chain) { + CommandSender sender = invocation.sender(); + Message message = invocation.context().get(Message.class).orElse(null); + if (sender instanceof User) { + if (message != null) { + message.reply(result); + } + } else if (sender instanceof ConsoleCommandSender) { + ((ConsoleCommandSender) sender).getLogger().info("The execution result of command {}: {}", invocation.name(), result); + } else { + throw new IllegalStateException("Unknown sender type: " + sender.getClass().getName()); + } + } + +} \ No newline at end of file diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/WrappedLiteCommandsBuilder.java b/src/main/java/snw/kookbc/impl/command/litecommands/WrappedLiteCommandsBuilder.java new file mode 100644 index 00000000..2dfae9ea --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/WrappedLiteCommandsBuilder.java @@ -0,0 +1,339 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands; + +import dev.rollczi.litecommands.LiteCommands; +import dev.rollczi.litecommands.LiteCommandsBuilder; +import dev.rollczi.litecommands.LiteCommandsProvider; +import dev.rollczi.litecommands.annotations.LiteCommandsAnnotations; +import dev.rollczi.litecommands.argument.ArgumentKey; +import dev.rollczi.litecommands.argument.parser.Parser; +import dev.rollczi.litecommands.argument.resolver.ArgumentResolverBase; +import dev.rollczi.litecommands.argument.suggester.Suggester; +import dev.rollczi.litecommands.bind.BindProvider; +import dev.rollczi.litecommands.configurator.LiteConfigurator; +import dev.rollczi.litecommands.context.ContextProvider; +import dev.rollczi.litecommands.editor.Editor; +import dev.rollczi.litecommands.extension.LiteExtension; +import dev.rollczi.litecommands.extension.annotations.AnnotationsExtension; +import dev.rollczi.litecommands.handler.exception.ExceptionHandler; +import dev.rollczi.litecommands.handler.result.ResultHandler; +import dev.rollczi.litecommands.invalidusage.InvalidUsageHandler; +import dev.rollczi.litecommands.message.InvokedMessage; +import dev.rollczi.litecommands.message.Message; +import dev.rollczi.litecommands.message.MessageKey; +import dev.rollczi.litecommands.permission.MissingPermissionsHandler; +import dev.rollczi.litecommands.platform.PlatformSettings; +import dev.rollczi.litecommands.platform.PlatformSettingsConfigurator; +import dev.rollczi.litecommands.processor.LiteBuilderProcessor; +import dev.rollczi.litecommands.programmatic.LiteCommand; +import dev.rollczi.litecommands.programmatic.LiteCommandsProgrammatic; +import dev.rollczi.litecommands.reflect.type.TypeRange; +import dev.rollczi.litecommands.scheduler.Scheduler; +import dev.rollczi.litecommands.schematic.SchematicFormat; +import dev.rollczi.litecommands.schematic.SchematicGenerator; +import dev.rollczi.litecommands.scope.Scope; +import dev.rollczi.litecommands.suggestion.SuggestionResult; +import dev.rollczi.litecommands.validator.Validator; +import dev.rollczi.litecommands.wrapper.Wrapper; +import snw.kookbc.impl.command.litecommands.annotations.prefix.PrefixAnnotationResolver; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +public class WrappedLiteCommandsBuilder> implements LiteCommandsBuilder { + private LiteCommandsBuilder delegate; + + public WrappedLiteCommandsBuilder(LiteCommandsBuilder delegate) { + this.delegate = delegate; + } + + @Override + public B settings(PlatformSettingsConfigurator configurator) { + return delegate.settings(configurator); + } + + @Override + public B commands(LiteCommandsProvider commandsProvider) { + return delegate.commands(commandsProvider); + } + + @Override + public B commands(Object... commands) { + List> providers = new ArrayList<>(); + Collection> programmatic = new ArrayList<>(); + List> classes = new ArrayList<>(); + List instances = new ArrayList<>(); + + for (Object command : commands) { + if (command instanceof LiteCommandsProvider) { + providers.add((LiteCommandsProvider) command); + continue; + } + + if (command instanceof LiteCommand) { + programmatic.add((LiteCommand) command); + continue; + } + + if (command instanceof Class) { + classes.add((Class) command); + continue; + } + + instances.add(command); + } + + for (LiteCommandsProvider provider : providers) { + this.commands(provider); + } + + if (!programmatic.isEmpty()) { + this.commands(LiteCommandsProgrammatic.of(programmatic)); + } + + if (!classes.isEmpty() || !instances.isEmpty()) { + LiteCommandsAnnotations commandsAnnotations = LiteCommandsAnnotations.create(); + commandsAnnotations.getAnnotationProcessorService().register(new PrefixAnnotationResolver<>()); + this.commands(commandsAnnotations + .load(instances.toArray(new Object[0])) + .loadClasses(classes.toArray(new Class[0])) + ); + } + + return self(); + } + + @Override + public B argumentParser(Class type, Parser parser) { + return delegate.argumentParser(type, parser); + } + + @Override + public B argumentParser(Class type, ArgumentKey key, Parser parser) { + return delegate.argumentParser(type, key, parser); + } + + @Override + public B argumentParser(TypeRange type, ArgumentKey key, Parser parser) { + return delegate.argumentParser(type, key, parser); + } + + @SuppressWarnings("unchecked") + protected B self() { + return ((B) delegate); + } + + @Override + public B argumentSuggestion(Class type, SuggestionResult suggestion) { + return delegate.argumentSuggestion(type, suggestion); + } + + @Override + public B argumentSuggestion(Class type, ArgumentKey key, SuggestionResult suggestion) { + return delegate.argumentSuggestion(type, key, suggestion); + } + + @Override + public B argumentSuggestion(TypeRange type, ArgumentKey key, SuggestionResult suggestion) { + return delegate.argumentSuggestion(type, key, suggestion); + } + + @Override + public B argumentSuggester(Class type, Suggester suggester) { + return delegate.argumentSuggester(type, suggester); + } + + @Override + public B argumentSuggester(Class type, ArgumentKey key, Suggester suggester) { + return delegate.argumentSuggester(type, key, suggester); + } + + @Override + public B argumentSuggester(TypeRange type, ArgumentKey key, Suggester suggester) { + return delegate.argumentSuggester(type, key, suggester); + } + + @Override + public B argument(Class type, ArgumentResolverBase resolver) { + return delegate.argument(type, resolver); + } + + @Override + public B argument(Class type, ArgumentKey key, ArgumentResolverBase resolver) { + return delegate.argument(type, key, resolver); + } + + @Override + public B argument(TypeRange type, ArgumentResolverBase resolver) { + return delegate.argument(type, resolver); + } + + @Override + public B argument(TypeRange type, ArgumentKey key, ArgumentResolverBase resolver) { + return delegate.argument(type, key, resolver); + } + + @Override + public B context(Class on, ContextProvider bind) { + return delegate.context(on, bind); + } + + @Override + public B bind(Class on, BindProvider bindProvider) { + return delegate.bind(on, bindProvider); + } + + @Override + public B bind(Class on, Supplier bind) { + return delegate.bind(on, bind); + } + + @Override + public B bindUnsafe(Class on, Supplier bind) { + return delegate.bindUnsafe(on, bind); + } + + @Override + public B scheduler(Scheduler scheduler) { + return delegate.scheduler(scheduler); + } + + @Override + public B message(MessageKey key, Message message) { + return delegate.message(key, message); + } + + @Override + public B message(MessageKey key, InvokedMessage message) { + return delegate.message(key, message); + } + + @Override + public B message(MessageKey key, T message) { + return delegate.message(key, message); + } + + @Override + public B editorGlobal(Editor editor) { + return delegate.editorGlobal(editor); + } + + @Override + public B editor(Scope scope, Editor editor) { + return delegate.editor(scope, editor); + } + + @Override + public B validatorGlobal(Validator validator) { + return delegate.validatorGlobal(validator); + } + + @Override + public B validator(Scope scope, Validator validator) { + return delegate.validator(scope, validator); + } + + @Override + public B result(Class resultType, ResultHandler handler) { + return delegate.result(resultType, handler); + } + + @Override + public B resultUnexpected(ResultHandler handler) { + return delegate.resultUnexpected(handler); + } + + @Override + public B exception(Class exceptionType, ExceptionHandler handler) { + return delegate.exception(exceptionType, handler); + } + + @Override + public B exceptionUnexpected(ExceptionHandler handler) { + return delegate.exceptionUnexpected(handler); + } + + @Override + public B missingPermission(MissingPermissionsHandler handler) { + return delegate.missingPermission(handler); + } + + @Override + public B invalidUsage(InvalidUsageHandler handler) { + return delegate.invalidUsage(handler); + } + + @Override + public B wrapper(Wrapper wrapper) { + return delegate.wrapper(wrapper); + } + + @Override + public B schematicGenerator(SchematicGenerator schematicGenerator) { + return delegate.schematicGenerator(schematicGenerator); + } + + @Override + public B schematicGenerator(SchematicFormat format) { + return delegate.schematicGenerator(format); + } + + @Override + public B selfProcessor(LiteBuilderProcessor processor) { + return delegate.selfProcessor(processor); + } + + @Override + public B preProcessor(LiteBuilderProcessor preProcessor) { + return delegate.preProcessor(preProcessor); + } + + @Override + public B postProcessor(LiteBuilderProcessor postProcessor) { + return delegate.postProcessor(postProcessor); + } + + @Override + public B extension(LiteExtension extension) { + return delegate.extension(extension); + } + + @Override + public > B extension(E extension, LiteConfigurator configurator) { + return delegate.extension(extension, configurator); + } + + @Override + public B annotations(LiteConfigurator> configurator) { + return delegate.annotations(configurator); + } + + @Override + public LiteCommands build() { + return delegate.build(); + } + + @Override + public LiteCommands build(boolean register) { + return delegate.build(register); + } +} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandPrefix.java b/src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/Prefix.java similarity index 84% rename from src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandPrefix.java rename to src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/Prefix.java index 24de1852..0860d215 100644 --- a/src/main/java/snw/kookbc/impl/command/cloud/annotations/CommandPrefix.java +++ b/src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/Prefix.java @@ -16,18 +16,16 @@ * along with this program. If not, see . */ -package snw.kookbc.impl.command.cloud.annotations; +package snw.kookbc.impl.command.litecommands.annotations.prefix; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * @author huanmeng_qwq - */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -public @interface CommandPrefix { - String[] value() default {}; +@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE}) +public @interface Prefix { + + String[] value() default {"/"}; } diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/PrefixAnnotationResolver.java b/src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/PrefixAnnotationResolver.java new file mode 100644 index 00000000..00a5bec7 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/annotations/prefix/PrefixAnnotationResolver.java @@ -0,0 +1,37 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands.annotations.prefix; + +import dev.rollczi.litecommands.annotations.AnnotationInvoker; +import dev.rollczi.litecommands.annotations.AnnotationProcessor; +import snw.kookbc.impl.command.litecommands.KookLitePlatform; + +import java.util.Arrays; +import java.util.HashSet; + +public class PrefixAnnotationResolver implements AnnotationProcessor { + @Override + public AnnotationInvoker process(AnnotationInvoker invoker) { + return invoker.on(Prefix.class, (annotation, metaHolder) -> { + metaHolder.meta().put(KookLitePlatform.PREFIX, new HashSet<>(Arrays.asList(annotation.value()))); + }); + } + +} + diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/internal/PluginsCommand.java b/src/main/java/snw/kookbc/impl/command/litecommands/internal/PluginsCommand.java new file mode 100644 index 00000000..066ea481 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/internal/PluginsCommand.java @@ -0,0 +1,55 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands.internal; + +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.description.Description; +import dev.rollczi.litecommands.annotations.execute.Execute; +import snw.jkook.command.CommandSender; +import snw.jkook.command.ConsoleCommandSender; +import snw.jkook.plugin.Plugin; +import snw.kookbc.impl.KBCClient; + +import java.util.Arrays; +import java.util.stream.Collectors; + +@Command(name = "plugins") +@Description({"获取已安装到此 KookBC 实例的插件列表。"}) +public class PluginsCommand { + private final KBCClient client; + + public PluginsCommand(KBCClient client) { + this.client = client; + } + + @Execute + String execute(@Context CommandSender sender) { + Plugin[] plugins = client.getCore().getPluginManager().getPlugins(); + return String.format("%s (%d): %s", + (sender instanceof ConsoleCommandSender) ? "Installed and running plugins" : "已安装并正在运行的插件", + plugins.length, + String.join(", ", + Arrays.stream(plugins) + .map(plugin -> plugin.getDescription().getName()) + .collect(Collectors.toSet()) + ) + ); + } +} diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/internal/completer/LiteCommandsCompleter.java b/src/main/java/snw/kookbc/impl/command/litecommands/internal/completer/LiteCommandsCompleter.java new file mode 100644 index 00000000..5bc6decb --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/internal/completer/LiteCommandsCompleter.java @@ -0,0 +1,86 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands.internal.completer; + +import dev.rollczi.litecommands.util.StringUtil; +import org.jline.reader.Candidate; +import org.jline.reader.Completer; +import org.jline.reader.LineReader; +import org.jline.reader.ParsedLine; +import snw.jkook.command.JKookCommand; +import snw.kookbc.impl.KBCClient; +import snw.kookbc.impl.command.CommandManagerImpl; +import snw.kookbc.impl.command.WrappedCommand; +import snw.kookbc.impl.command.litecommands.LiteKookCommandExecutor; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class LiteCommandsCompleter implements Completer { + private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); + private final KBCClient client; + + public LiteCommandsCompleter(KBCClient client) { + this.client = client; + } + + @Override + public void complete(LineReader reader, ParsedLine line, List candidates) { + String input = line.line(); + String rootName = input; + // end of char is blank + String[] args = {""}; + int index = input.indexOf(" "); + if (index != -1) { + rootName = input.substring(0, index); + String argLine = input.substring(index + 1); + args = PATTERN_ON_SPACE.split(argLine, -1); + } + List suggestions = suggestions(rootName, args); + for (String suggestion : suggestions) { + candidates.add(new Candidate(suggestion)); + } + } + + public List suggestions(String rootName, String[] args) { + List result = new ArrayList<>(); + for (WrappedCommand wrappedCommand : ((CommandManagerImpl) client.getCommandManager()).getCommandMap().getView(false).values()) { + JKookCommand command = wrappedCommand.getCommand(); + if (rootName.isEmpty()) { + result.add(command.getRootName()); + result.addAll(command.getAliases()); + continue; + } + if (command.getRootName().equalsIgnoreCase(rootName) || command.getAliases().stream().anyMatch(alias -> alias.equalsIgnoreCase(rootName))) { + if (command.getExecutor() instanceof LiteKookCommandExecutor) { + Iterable suggestions = ((LiteKookCommandExecutor) command.getExecutor()).getSuggestions(client.getCore().getConsoleCommandSender(), args); + for (String suggestion : suggestions) { + result.add(suggestion); + } + } + } else if (StringUtil.startsWithIgnoreCase(command.getRootName(), rootName)) { + result.add(command.getRootName()); + } else if (command.getAliases().stream().anyMatch(alias -> StringUtil.startsWithIgnoreCase(alias, rootName))) { + result.add(command.getRootName()); + } + } + return result; + } +} diff --git a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandBuilder.java b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookMessageContextual.java similarity index 54% rename from src/main/java/snw/kookbc/impl/command/cloud/CloudCommandBuilder.java rename to src/main/java/snw/kookbc/impl/command/litecommands/tools/KookMessageContextual.java index 73ae8143..557cbec7 100644 --- a/src/main/java/snw/kookbc/impl/command/cloud/CloudCommandBuilder.java +++ b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookMessageContextual.java @@ -15,24 +15,25 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package snw.kookbc.impl.command.cloud; -import cloud.commandframework.annotations.AnnotationParser; -import cloud.commandframework.arguments.parser.ParserParameters; -import cloud.commandframework.meta.CommandMeta; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.jetbrains.annotations.NotNull; +package snw.kookbc.impl.command.litecommands.tools; + +import dev.rollczi.litecommands.context.ContextProvider; +import dev.rollczi.litecommands.context.ContextResult; +import dev.rollczi.litecommands.invocation.Invocation; import snw.jkook.command.CommandSender; +import snw.jkook.message.Message; -import java.util.function.Function; +import java.util.Optional; -/** - * @author huanmeng_qwq - */ -public interface CloudCommandBuilder { +public class KookMessageContextual implements ContextProvider { + @Override + public ContextResult provide(Invocation invocation) { + Optional message = invocation.context().get(Message.class); + if (message.isPresent()) { + return ContextResult.ok(message::get); + } - static @NotNull AnnotationParser createParser(@NotNull CloudBasedCommandManager commandManager, - @NotNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper) { - return new AnnotationParser<>(commandManager, CommandSender.class, metaMapper); + return ContextResult.ok(() -> null); } } diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyConsoleContextual.java b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyConsoleContextual.java new file mode 100644 index 00000000..dcce2269 --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyConsoleContextual.java @@ -0,0 +1,50 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands.tools; + +import dev.rollczi.litecommands.context.ContextProvider; +import dev.rollczi.litecommands.context.ContextResult; +import dev.rollczi.litecommands.invocation.Invocation; +import snw.jkook.command.CommandSender; +import snw.jkook.command.ConsoleCommandSender; + +import java.util.function.Supplier; + +public class KookOnlyConsoleContextual implements ContextProvider { + + private final Supplier onlyConsoleMessage; + + public KookOnlyConsoleContextual(Supplier onlyConsoleMessage) { + this.onlyConsoleMessage = onlyConsoleMessage; + } + + public KookOnlyConsoleContextual(MESSAGE onlyConsoleMessage) { + this(() -> onlyConsoleMessage); + } + + @Override + public ContextResult provide(Invocation invocation) { + if (invocation.sender() instanceof ConsoleCommandSender) { + return ContextResult.ok(() -> (ConsoleCommandSender) invocation.sender()); + } + + return ContextResult.error(onlyConsoleMessage.get()); + } + +} \ No newline at end of file diff --git a/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyUserContextual.java b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyUserContextual.java new file mode 100644 index 00000000..801376db --- /dev/null +++ b/src/main/java/snw/kookbc/impl/command/litecommands/tools/KookOnlyUserContextual.java @@ -0,0 +1,50 @@ +/* + * KookBC -- The Kook Bot Client & JKook API standard implementation for Java. + * Copyright (C) 2022 - 2023 KookBC contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package snw.kookbc.impl.command.litecommands.tools; + +import dev.rollczi.litecommands.context.ContextProvider; +import dev.rollczi.litecommands.context.ContextResult; +import dev.rollczi.litecommands.invocation.Invocation; +import snw.jkook.command.CommandSender; +import snw.jkook.entity.User; + +import java.util.function.Supplier; + +public class KookOnlyUserContextual implements ContextProvider { + + private final Supplier onlyUserMessage; + + public KookOnlyUserContextual(Supplier onlyUserMessage) { + this.onlyUserMessage = onlyUserMessage; + } + + public KookOnlyUserContextual(MESSAGE onlyUserMessage) { + this(() -> onlyUserMessage); + } + + @Override + public ContextResult provide(Invocation invocation) { + if (invocation.sender() instanceof User) { + return ContextResult.ok(() -> (User) invocation.sender()); + } + + return ContextResult.error(onlyUserMessage.get()); + } + +} \ No newline at end of file diff --git a/src/main/java/snw/kookbc/impl/console/Console.java b/src/main/java/snw/kookbc/impl/console/Console.java index 419ef84b..b0583891 100644 --- a/src/main/java/snw/kookbc/impl/console/Console.java +++ b/src/main/java/snw/kookbc/impl/console/Console.java @@ -23,6 +23,7 @@ import org.jline.reader.LineReaderBuilder; import snw.kookbc.SharedConstants; import snw.kookbc.impl.KBCClient; +import snw.kookbc.impl.command.litecommands.internal.completer.LiteCommandsCompleter; import java.nio.file.Paths; @@ -64,6 +65,6 @@ protected LineReader buildReader(LineReaderBuilder builder) { if (client.getConfig().getBoolean("save-console-history", true)) { builder.variable("history-file", Paths.get(".console_history")); } - return super.buildReader(builder); + return super.buildReader(builder.completer(new LiteCommandsCompleter(client))); } } diff --git a/src/main/java/snw/kookbc/impl/event/internal/UserClickButtonListener.java b/src/main/java/snw/kookbc/impl/event/internal/UserClickButtonListener.java index 08e65ead..a21baa58 100644 --- a/src/main/java/snw/kookbc/impl/event/internal/UserClickButtonListener.java +++ b/src/main/java/snw/kookbc/impl/event/internal/UserClickButtonListener.java @@ -16,7 +16,6 @@ import snw.jkook.message.component.card.module.*; import snw.kookbc.SharedConstants; import snw.kookbc.impl.KBCClient; -import snw.kookbc.impl.command.cloud.CloudCommandManagerImpl; import snw.kookbc.util.Util; import java.util.Arrays; @@ -49,12 +48,7 @@ public void event(UserClickButtonEvent event) { return; } - List content; - if (client.getCore().getCommandManager() instanceof CloudCommandManagerImpl) { - content = Util.listCloudCommandsHelp(this.client, force); - } else { - content = Util.listCommandsHelp(this.client); - } + List content = Util.listCommandsHelp(this.client); MultipleCardComponent finalComponent; if (content.isEmpty()) { finalComponent = new CardBuilder() diff --git a/src/main/java/snw/kookbc/launcher/Launcher.java b/src/main/java/snw/kookbc/launcher/Launcher.java index 6fd09733..c0bd071a 100644 --- a/src/main/java/snw/kookbc/launcher/Launcher.java +++ b/src/main/java/snw/kookbc/launcher/Launcher.java @@ -1,11 +1,5 @@ package snw.kookbc.launcher; -/** - * 2023/7/17
- * KookBC
- * - * @author huanmeng_qwq - */ public abstract class Launcher { private static Launcher launcher; diff --git a/src/main/java/snw/kookbc/util/Util.java b/src/main/java/snw/kookbc/util/Util.java index 289d8adb..f5a2be97 100644 --- a/src/main/java/snw/kookbc/util/Util.java +++ b/src/main/java/snw/kookbc/util/Util.java @@ -27,8 +27,6 @@ import snw.kookbc.impl.KBCClient; import snw.kookbc.impl.command.CommandManagerImpl; import snw.kookbc.impl.command.WrappedCommand; -import snw.kookbc.impl.command.cloud.CloudCommandInfo; -import snw.kookbc.impl.command.cloud.CloudCommandManagerImpl; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -173,19 +171,6 @@ public static List listCommandsHelp(KBCClient client) { return result; } - public static List listCloudCommandsHelp(KBCClient client, boolean force) { - List commandsInfo = ((CloudCommandManagerImpl) client.getCore().getCommandManager()).getCommandsInfo(); - - List result = new LinkedList<>(); - for (CloudCommandInfo command : commandsInfo) { - if (!force && (command.owningPlugin() == null || !command.owningPlugin().isEnabled() || command.hidden())) { - continue; - } - insertCommandHelpContent(result, command.syntax(), Arrays.asList(command.prefixes()), command.description()); - } - return result; - } - private static void insertCommandHelpContent(List result, String rootName, Collection prefixes, String description) { result.add( limit( @@ -213,23 +198,6 @@ public static JKookCommand findSpecificCommand(KBCClient client, String name) { } } - public static CloudCommandInfo findSpecificCloudCommand(KBCClient client, String name) { - List commandsInfo = ((CloudCommandManagerImpl) client.getCore().getCommandManager()).getCommandsInfo(); - if (!isBlank(name) && !name.equalsIgnoreCase("all")) { - return commandsInfo.stream() - .filter(info -> - info.rootName().equalsIgnoreCase(name) || - info.syntax().equalsIgnoreCase(name) || - Arrays.stream(info.aliases()) - .anyMatch( - alias -> alias.equalsIgnoreCase(name) - ) - ).findFirst().orElse(null); - } else { - return null; - } - } - public static String limit(String original, int maxLength) { if (maxLength < 0 || original.length() <= maxLength) return original;