diff --git a/bom/README.md b/bom/README.md
new file mode 100644
index 0000000..723a30d
--- /dev/null
+++ b/bom/README.md
@@ -0,0 +1,96 @@
+# CommandsAPI BOM (Bill of Materials)
+
+This module provides a BOM (Bill of Materials) for CommandsAPI, making it easier to manage consistent versions across all CommandsAPI modules.
+
+## Usage
+
+### Gradle (Kotlin DSL)
+
+```kotlin
+dependencies {
+ // Import the BOM
+ implementation(platform("fr.traqueur.commands:bom:VERSION"))
+
+ // Then add dependencies without specifying versions
+ implementation("fr.traqueur.commands:core")
+ implementation("fr.traqueur.commands:platform-spigot")
+ implementation("fr.traqueur.commands:platform-velocity")
+ implementation("fr.traqueur.commands:platform-jda")
+ implementation("fr.traqueur.commands:annotations-addon")
+}
+```
+
+### Gradle (Groovy DSL)
+
+```groovy
+dependencies {
+ // Import the BOM
+ implementation platform('fr.traqueur.commands:bom:VERSION')
+
+ // Then add dependencies without specifying versions
+ implementation 'fr.traqueur.commands:core'
+ implementation 'fr.traqueur.commands:platform-spigot'
+ implementation 'fr.traqueur.commands:platform-velocity'
+ implementation 'fr.traqueur.commands:platform-jda'
+ implementation 'fr.traqueur.commands:annotations-addon'
+}
+```
+
+### Maven
+
+```xml
+
+
+
+ fr.traqueur.commands
+ bom
+ VERSION
+ pom
+ import
+
+
+
+
+
+
+
+ fr.traqueur.commands
+ core
+
+
+ fr.traqueur.commands
+ platform-spigot
+
+
+ fr.traqueur.commands
+ platform-velocity
+
+
+ fr.traqueur.commands
+ platform-jda
+
+
+ fr.traqueur.commands
+ annotations-addon
+
+
+```
+
+## Benefits
+
+Using the BOM provides several advantages:
+
+1. **Version Consistency**: All CommandsAPI modules will use compatible versions
+2. **Simplified Dependency Management**: No need to specify versions for each module
+3. **Easier Updates**: Update all modules by changing only the BOM version
+4. **Reduced Conflicts**: Ensures all modules work together correctly
+
+## Available Modules
+
+The BOM manages versions for the following modules:
+
+- `core` - Core functionality and API
+- `platform-spigot` - Spigot/Bukkit platform support
+- `platform-velocity` - Velocity proxy platform support
+- `platform-jda` - JDA (Discord) platform support
+- `annotations-addon` - Annotation-based command registration
\ No newline at end of file
diff --git a/bom/build.gradle b/bom/build.gradle
new file mode 100644
index 0000000..cc95d7b
--- /dev/null
+++ b/bom/build.gradle
@@ -0,0 +1,77 @@
+plugins {
+ id 'java-platform'
+ id 'maven-publish'
+}
+
+description = 'CommandsAPI BOM (Bill of Materials)'
+
+javaPlatform {
+ allowDependencies()
+}
+
+dependencies {
+ constraints {
+ api(project(':core'))
+ api(project(':spigot'))
+ api(project(':velocity'))
+ api(project(':jda'))
+ api(project(':annotations-addon'))
+ }
+}
+
+publishing {
+ repositories {
+ maven {
+ def repository = System.getProperty('repository.name', 'snapshots')
+ def repoType = repository.toLowerCase()
+
+ name = "groupez${repository.capitalize()}"
+ url = uri("https://repo.groupez.dev/${repoType}")
+
+ credentials {
+ username = findProperty("${name}Username") ?: System.getenv('MAVEN_USERNAME')
+ password = findProperty("${name}Password") ?: System.getenv('MAVEN_PASSWORD')
+ }
+
+ authentication {
+ create("basic", BasicAuthentication)
+ }
+ }
+ }
+
+ publications {
+ create('maven', MavenPublication) {
+ from components.javaPlatform
+
+ groupId = rootProject.group.toString()
+ artifactId = 'bom'
+ version = rootProject.version.toString()
+
+ pom {
+ name = 'CommandsAPI BOM'
+ description = 'CommandsAPI Bill of Materials - Manages consistent versions across CommandsAPI modules'
+ url = 'https://github.com/Traqueur-dev/CommandsAPI'
+
+ licenses {
+ license {
+ name = 'MIT License'
+ url = 'https://opensource.org/licenses/MIT'
+ }
+ }
+
+ developers {
+ developer {
+ id = 'traqueur'
+ name = 'Traqueur'
+ }
+ }
+
+ scm {
+ connection = 'scm:git:git://github.com/Traqueur-dev/CommandsAPI.git'
+ developerConnection = 'scm:git:ssh://github.com/Traqueur-dev/CommandsAPI.git'
+ url = 'https://github.com/Traqueur-dev/CommandsAPI'
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d2e5a4b..f6300d2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,8 +7,10 @@ allprojects {
group = 'fr.traqueur.commands'
version = property('version')
- apply {
- plugin 'java-library'
+ if (project.name != 'bom') {
+ apply {
+ plugin 'java-library'
+ }
}
ext.classifier = System.getProperty('archive.classifier')
@@ -27,7 +29,7 @@ allprojects {
}
subprojects {
- if (project.name.contains('test-')) {
+ if (project.name.contains('test-') || project.name == 'bom') {
return;
}
diff --git a/core/src/main/java/fr/traqueur/commands/api/CommandManager.java b/core/src/main/java/fr/traqueur/commands/api/CommandManager.java
index a3782e2..3dcab35 100644
--- a/core/src/main/java/fr/traqueur/commands/api/CommandManager.java
+++ b/core/src/main/java/fr/traqueur/commands/api/CommandManager.java
@@ -49,7 +49,7 @@ public abstract class CommandManager {
/**
* The argument converters registered in the command manager.
*/
- private final Map> typeConverters;
+ private final Map, ArgumentConverter.Wrapper>> typeConverters;
/**
* The tab completer registered in the command manager.
@@ -202,7 +202,7 @@ public void unregisterCommand(Command command, boolean subcommands) {
* @param The type of the argument.
*/
public void registerConverter(Class typeClass, ArgumentConverter converter) {
- this.typeConverters.put(typeClass.getSimpleName().toLowerCase(), new ArgumentConverter.Wrapper<>(typeClass, converter));
+ this.typeConverters.put(typeClass, new ArgumentConverter.Wrapper<>(typeClass, converter));
}
/**
@@ -251,8 +251,8 @@ public Map>> getCompleters() {
* @param type The type to check.
* @return true if a TabCompleter is registered for this type.
*/
- public boolean hasTabCompleterForType(String type) {
- ArgumentConverter.Wrapper> wrapper = this.typeConverters.get(type.toLowerCase());
+ public boolean hasTabCompleterForType(Class> type) {
+ ArgumentConverter.Wrapper> wrapper = this.typeConverters.get(type);
return wrapper != null && wrapper.converter() instanceof TabCompleter;
}
@@ -263,8 +263,8 @@ public boolean hasTabCompleterForType(String type) {
* @return The TabCompleter for this type, or null if none exists.
*/
@SuppressWarnings("unchecked")
- public TabCompleter getTabCompleterForType(String type) {
- ArgumentConverter.Wrapper> wrapper = this.typeConverters.get(type.toLowerCase());
+ public TabCompleter getTabCompleterForType(Class> type) {
+ ArgumentConverter.Wrapper> wrapper = this.typeConverters.get(type);
if (wrapper != null && wrapper.converter() instanceof TabCompleter) {
return (TabCompleter) wrapper.converter();
}
@@ -411,7 +411,7 @@ private void addCompletionsForLabel(String[] labelParts) {
private void addCompletionForArgs(String label, int commandSize, List> args) {
for (int i = 0; i < args.size(); i++) {
Argument arg = args.get(i);
- String type = arg.type().key();
+ Class> type = arg.type().key();
ArgumentConverter.Wrapper> entry = this.typeConverters.get(type);
TabCompleter argConverter = arg.tabCompleter();
if (argConverter != null) {
@@ -465,9 +465,18 @@ public CommandInvoker getInvoker() {
*/
private void registerInternalConverters() {
this.registerConverter(String.class, (s) -> s);
+
+ // Register both primitive and wrapper types for DefaultArgumentParser (Spigot/Velocity).
+ // JDA's ArgumentParser handles primitives internally, but text-based platforms need explicit registration.
+ // Wrapper types (Integer.class, Long.class, etc.) are registered for compatibility with wrapper usage.
+ // Primitive types (int.class, long.class, etc.) are registered to support primitive method parameters.
this.registerConverter(Boolean.class, new BooleanArgument<>());
+ this.registerConverter(boolean.class, new BooleanArgument<>());
this.registerConverter(Integer.class, new IntegerArgument());
+ this.registerConverter(int.class, new IntegerArgument());
this.registerConverter(Double.class, new DoubleArgument());
+ this.registerConverter(double.class, new DoubleArgument());
this.registerConverter(Long.class, new LongArgument());
+ this.registerConverter(long.class, new LongArgument());
}
}
diff --git a/core/src/main/java/fr/traqueur/commands/api/arguments/Argument.java b/core/src/main/java/fr/traqueur/commands/api/arguments/Argument.java
index a287103..85d8862 100644
--- a/core/src/main/java/fr/traqueur/commands/api/arguments/Argument.java
+++ b/core/src/main/java/fr/traqueur/commands/api/arguments/Argument.java
@@ -43,7 +43,7 @@ public Argument(String name, ArgumentType type) {
}
public String canonicalName() {
- return this.name + ":" + this.type.key();
+ return this.name + ":" + this.type.key().getSimpleName().toLowerCase();
}
diff --git a/core/src/main/java/fr/traqueur/commands/api/arguments/ArgumentType.java b/core/src/main/java/fr/traqueur/commands/api/arguments/ArgumentType.java
index 55c958d..6ece82e 100644
--- a/core/src/main/java/fr/traqueur/commands/api/arguments/ArgumentType.java
+++ b/core/src/main/java/fr/traqueur/commands/api/arguments/ArgumentType.java
@@ -9,7 +9,7 @@ static ArgumentType of(Class> clazz) {
return new Simple(clazz);
}
- String key();
+ Class> key();
/**
* Check if this is the infinite type.
@@ -23,8 +23,8 @@ default boolean isInfinite() {
record Simple(Class> clazz) implements ArgumentType {
@Override
- public String key() {
- return clazz.getSimpleName().toLowerCase();
+ public Class> key() {
+ return clazz;
}
}
@@ -33,8 +33,8 @@ record Infinite() implements ArgumentType {
public static final Infinite INSTANCE = new Infinite();
@Override
- public String key() {
- return "infinite";
+ public Class> key() {
+ return fr.traqueur.commands.api.arguments.Infinite.class;
}
}
diff --git a/core/src/main/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParser.java b/core/src/main/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParser.java
index 1abfdfc..9f66e4a 100644
--- a/core/src/main/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParser.java
+++ b/core/src/main/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParser.java
@@ -20,10 +20,10 @@ public class DefaultArgumentParser implements ArgumentParser> typeConverters;
+ private final Map, ArgumentConverter.Wrapper>> typeConverters;
private final Logger logger;
- public DefaultArgumentParser(Map> typeConverters, Logger logger) {
+ public DefaultArgumentParser(Map, ArgumentConverter.Wrapper>> typeConverters, Logger logger) {
this.typeConverters = typeConverters;
this.logger = logger;
}
@@ -80,7 +80,7 @@ public ParseResult parse(Command command, String[] rawArgs) {
}
private ParseResult parseSingle(Arguments arguments, Argument arg, String input) {
- String typeKey = arg.type().key();
+ Class> typeKey = arg.type().key();
ArgumentConverter.Wrapper> wrapper = typeConverters.get(typeKey);
if (wrapper == null) {
@@ -88,7 +88,7 @@ private ParseResult parseSingle(Arguments arguments, Argument arg, String inp
ParseError.Type.TYPE_NOT_FOUND,
arg.name(),
input,
- "No converter for type: " + typeKey
+ "No converter for type: " + typeKey.getSimpleName()
));
}
diff --git a/core/src/test/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParserTest.java b/core/src/test/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParserTest.java
index fb2fcc4..d76a38e 100644
--- a/core/src/test/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParserTest.java
+++ b/core/src/test/java/fr/traqueur/commands/impl/parsing/DefaultArgumentParserTest.java
@@ -22,16 +22,16 @@ class DefaultArgumentParserTest {
@BeforeEach
void setUp() {
- Map> converters = new HashMap<>();
- converters.put("string", new ArgumentConverter.Wrapper<>(String.class, s -> s));
- converters.put("integer", new ArgumentConverter.Wrapper<>(Integer.class, s -> {
+ Map, ArgumentConverter.Wrapper>> converters = new HashMap<>();
+ converters.put(String.class, new ArgumentConverter.Wrapper<>(String.class, s -> s));
+ converters.put(Integer.class, new ArgumentConverter.Wrapper<>(Integer.class, s -> {
try {
return Integer.valueOf(s);
} catch (NumberFormatException e) {
return null;
}
}));
- converters.put("double", new ArgumentConverter.Wrapper<>(Double.class, s -> {
+ converters.put(Double.class, new ArgumentConverter.Wrapper<>(Double.class, s -> {
try {
return Double.valueOf(s);
} catch (NumberFormatException e) {
diff --git a/gradle.properties b/gradle.properties
index f81469e..8656a1e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1 @@
-version=5.0.0
\ No newline at end of file
+version=5.0.1
\ No newline at end of file
diff --git a/jda/src/main/java/fr/traqueur/commands/jda/JDAPlatform.java b/jda/src/main/java/fr/traqueur/commands/jda/JDAPlatform.java
index 7d97aeb..341b29d 100644
--- a/jda/src/main/java/fr/traqueur/commands/jda/JDAPlatform.java
+++ b/jda/src/main/java/fr/traqueur/commands/jda/JDAPlatform.java
@@ -7,6 +7,8 @@
import fr.traqueur.commands.api.resolver.SenderResolver;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.Permission;
+import net.dv8tion.jda.api.entities.*;
+import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.*;
@@ -288,20 +290,56 @@ private boolean hasTabCompleter(Argument arg) {
* @param type The type string.
* @return The corresponding OptionType.
*/
- private OptionType mapToOptionType(String type) {
- return switch (type.toLowerCase()) {
- case "integer", "int", "long" -> OptionType.INTEGER;
- case "boolean" -> OptionType.BOOLEAN;
- case "user", "member" -> OptionType.USER;
- case "role" -> OptionType.ROLE;
- case "channel", "guildchannelunion" -> OptionType.CHANNEL;
- case "double", "float" -> OptionType.NUMBER;
- case "attachment" -> OptionType.ATTACHMENT;
- case "mentionable" -> OptionType.MENTIONABLE;
+ private OptionType mapToOptionType(Class> type) {
+ return switch (type) {
+
+ // INTEGER
+ case Class> t when t == int.class
+ || t == Integer.class
+ || t == long.class
+ || t == Long.class
+ -> OptionType.INTEGER;
+
+ // BOOLEAN
+ case Class> t when t == boolean.class
+ || t == Boolean.class
+ -> OptionType.BOOLEAN;
+
+ // USER / MEMBER
+ case Class> t when User.class.isAssignableFrom(t)
+ || Member.class.isAssignableFrom(t)
+ -> OptionType.USER;
+
+ // ROLE
+ case Class> t when Role.class.isAssignableFrom(t)
+ -> OptionType.ROLE;
+
+ // CHANNEL (tous types de channels)
+ case Class> t when GuildChannel.class.isAssignableFrom(t)
+ -> OptionType.CHANNEL;
+
+ // NUMBER
+ case Class> t when t == double.class
+ || t == Double.class
+ || t == float.class
+ || t == Float.class
+ -> OptionType.NUMBER;
+
+ // ATTACHMENT
+ case Class> t when Message.Attachment.class.isAssignableFrom(t)
+ -> OptionType.ATTACHMENT;
+
+ // MENTIONABLE
+ case Class> t when IMentionable.class.isAssignableFrom(t)
+ -> OptionType.MENTIONABLE;
+
default -> OptionType.STRING;
};
}
+
+
+
/**
* Synchronize all registered commands with Discord globally.
* This may take up to 1 hour to update.
diff --git a/settings.gradle b/settings.gradle
index 749e16c..564065b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,6 @@
rootProject.name = 'CommandsAPI'
+include 'bom'
include 'velocity-test-plugin'
include 'spigot-test-plugin'
include 'spigot'