feat(core): add InteractionProcessors and InteractionExecutors and improve Autocomplete Interaction
All checks were successful
github-mirror / push-github (push) Successful in 1m45s
Build / Gradle-Build (push) Successful in 37s
Test / Gradle-Test (push) Successful in 46s

This commit is contained in:
2025-04-06 00:04:17 +02:00
parent ec17952375
commit 991d1c047b
34 changed files with 344 additions and 164 deletions

View File

@@ -5,7 +5,9 @@ import java.util.List;
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
import discord4j.core.event.domain.interaction.ComponentInteractionEvent;
import discord4j.core.event.domain.interaction.InteractionCreateEvent;
import discord4j.core.object.command.ApplicationCommandInteractionOption;
import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData;
import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType;
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
@@ -79,24 +81,25 @@ public class Discord4JContextObjectProvider implements ContextObjectProvider {
@Override
public Object getInteractionContextObject(Object context, Class<?> type) {
ComponentInteractionEvent componentInteractionEvent = (ComponentInteractionEvent) context;
InteractionCreateEvent interactionEvent = (InteractionCreateEvent) context;
switch (type.getName()) {
case "discord4j.core.object.entity.channel.MessageChannel":
return componentInteractionEvent.getInteraction().getChannel().block();
return interactionEvent.getInteraction().getChannel().block();
case "discord4j.core.object.entity.Guild":
return componentInteractionEvent.getInteraction().getGuild().block();
return interactionEvent.getInteraction().getGuild().block();
case "discord4j.core.object.entity.Member":
return componentInteractionEvent.getInteraction().getMember().orElse(null);
return interactionEvent.getInteraction().getMember().orElse(null);
case "discord4j.core.object.entity.User":
return componentInteractionEvent.getInteraction().getUser();
return interactionEvent.getInteraction().getUser();
default:
return null;
}
return null;
}
@Override
public Object getAutocompleteFocusedOption(Object context) {
ChatInputAutoCompleteEvent interaction = (ChatInputAutoCompleteEvent) context;
return getOptionValue(interaction.getFocusedOption());
public AutocompleteOptionData getAutocompleteFocusedOption(Object context) {
ApplicationCommandInteractionOption option = ((ChatInputAutoCompleteEvent) context).getFocusedOption();
return new AutocompleteOptionData(option.getName(), getOptionValue(option));
}
}

View File

@@ -55,10 +55,10 @@ public class Discord4JConverterSpec implements CommandConverter.Spec<Application
.name(option.name())
.description(option.description())
.required(option.required())
.autocomplete(option.autocomplete())
.minLength(Double.valueOf(option.range().min()).intValue())
.autocomplete(option.autocompletes().length > 0)
.minLength((int) option.range().min())
.minValue(option.range().min())
.maxLength(Double.valueOf(option.range().max()).intValue())
.maxLength((int)option.range().max())
.maxValue(option.range().max())
.choices(choices)
.build();

View File

@@ -1,14 +1,16 @@
package net.tomatentum.marinara.wrapper.discord4j;
import java.util.List;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import org.slf4j.Logger;
import discord4j.core.GatewayDiscordClient;
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
import discord4j.core.event.domain.interaction.InteractionCreateEvent;
import discord4j.core.object.command.ApplicationCommandInteractionOption;
import discord4j.core.object.command.ApplicationCommandOption.Type;
import discord4j.discordjson.json.ApplicationCommandOptionChoiceData;
import discord4j.discordjson.json.ApplicationCommandRequest;
import net.tomatentum.marinara.util.LoggerUtil;
@@ -22,13 +24,12 @@ import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.ButtonIdent
import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.SlashCommandIdentifierConverter;
public class Discord4JWrapper extends LibraryWrapper {
public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> SUB_FILTER = (i) ->
public static final UnaryOperator<List<ApplicationCommandInteractionOption>> SUB_FILTER = i ->
i.stream()
.filter(o -> o.getType().equals(Type.SUB_COMMAND) || o.getType().equals(Type.SUB_COMMAND_GROUP))
.toList();
public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> ARG_FILTER = (i) ->
public static final UnaryOperator<List<ApplicationCommandInteractionOption>> ARG_FILTER = i ->
i.stream()
.filter(o -> !o.getType().equals(Type.SUB_COMMAND) && !o.getType().equals(Type.SUB_COMMAND_GROUP))
.toList();
@@ -71,4 +72,15 @@ public class Discord4JWrapper extends LibraryWrapper {
return this.contextObjectProvider;
}
@Override
public void respondAutocomplete(Object context, List<Object> options) {
if (context instanceof ChatInputAutoCompleteEvent event) {
List<ApplicationCommandOptionChoiceData> choices = options.stream()
.filter(ApplicationCommandOptionChoiceData.class::isInstance)
.map(o -> (ApplicationCommandOptionChoiceData)o)
.toList();
event.respondWithSuggestions(choices);
}
}
}

View File

@@ -1,53 +1,39 @@
package net.tomatentum.marinara.wrapper.discord4j.identifierconverter;
import java.util.List;
import java.util.Optional;
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
import discord4j.core.object.command.ApplicationCommandInteractionOption;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier;
import net.tomatentum.marinara.registry.InteractionEntry;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper;
public class AutocompleteIdentifierConverter implements IdentifierProvider.Converter<ChatInputAutoCompleteEvent> {
@Override
public InteractionIdentifier convert(ChatInputAutoCompleteEvent context, InteractionRegistry registry) {
public InteractionIdentifier convert(ChatInputAutoCompleteEvent context) {
List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions());
String commandName = context.getCommandName();
InteractionIdentifier ident;
if (!options.isEmpty()) {
List<ApplicationCommandInteractionOption> sub_options = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions());
if (!sub_options.isEmpty())
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
List<ApplicationCommandInteractionOption> subOptions = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions());
if (!subOptions.isEmpty())
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName,
options.getFirst().getName(),
sub_options.getFirst().getName());
subOptions.getFirst().getName());
else
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName,
options.getFirst().getName());
}else
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName);
Optional<InteractionEntry> entry = registry.findFor(ident);
if (entry.isPresent() && entry.get().identifier() instanceof SlashCommandIdentifier) {
SlashCommandIdentifier sIdent = (SlashCommandIdentifier) entry.get().identifier();
return InteractionIdentifier.builder()
.type(InteractionType.AUTOCOMPLETE)
.name(sIdent.autocompleteRef()[0])
.build();
}
return null;
}
}

View File

@@ -3,13 +3,12 @@ package net.tomatentum.marinara.wrapper.discord4j.identifierconverter;
import discord4j.core.event.domain.interaction.ButtonInteractionEvent;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
public class ButtonIdentifierConverter implements IdentifierProvider.Converter<ButtonInteractionEvent> {
@Override
public InteractionIdentifier convert(ButtonInteractionEvent context, InteractionRegistry registry) {
public InteractionIdentifier convert(ButtonInteractionEvent context) {
return InteractionIdentifier.builder().name(context.getCustomId()).type(InteractionType.BUTTON).build();
}

View File

@@ -6,14 +6,13 @@ import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
import discord4j.core.object.command.ApplicationCommandInteractionOption;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper;
public class SlashCommandIdentifierConverter implements IdentifierProvider.Converter<ChatInputInteractionEvent> {
@Override
public InteractionIdentifier convert(ChatInputInteractionEvent context, InteractionRegistry registry) {
public InteractionIdentifier convert(ChatInputInteractionEvent context) {
List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions());
String commandName = context.getCommandName();

View File

@@ -18,10 +18,10 @@ import net.tomatentum.marinara.Marinara;
import net.tomatentum.marinara.wrapper.LibraryWrapper;
import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper;
public class AutoCompleteTest {
class AutoCompleteTest {
@Test
public void testAutocomplete() {
void testAutocomplete() {
ApplicationCommandInteractionOption optionMock = mock();
ChatInputAutoCompleteEvent autoCompleteEventMock = mock();

View File

@@ -6,23 +6,35 @@ import java.util.Collections;
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
import discord4j.discordjson.json.ApplicationCommandOptionChoiceData;
import net.tomatentum.marinara.interaction.InteractionHandler;
import net.tomatentum.marinara.interaction.annotation.AutoComplete;
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand;
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption;
import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType;
public class TestAutocomplete implements InteractionHandler {
@SlashCommand(name = "test")
@SlashCommand(
name = "test",
options = @SlashCommandOption(
name = "foo",
type = SlashCommandOptionType.STRING,
autocompletes = @AutoComplete("testAuto")
)
)
@AutoComplete("testAuto")
public void exec(ChatInputInteractionEvent context) {
}
@AutoComplete("testAuto")
public void autocomplete(ChatInputAutoCompleteEvent context, String value) {
public ApplicationCommandOptionChoiceData[] autocomplete(ChatInputAutoCompleteEvent context, String value) {
System.out.println("Success!");
assertEquals(value, "test");
context.respondWithSuggestions(Collections.emptyList());
assertEquals("test", value);
return new ApplicationCommandOptionChoiceData[]{
ApplicationCommandOptionChoiceData.builder().name("TestValue").value("test").build()
};
}
}

View File

@@ -2,10 +2,12 @@ package net.tomatentum.marinara.wrapper.javacord;
import org.javacord.api.interaction.AutocompleteInteraction;
import org.javacord.api.interaction.ButtonInteraction;
import org.javacord.api.interaction.InteractionBase;
import org.javacord.api.interaction.SlashCommandInteraction;
import org.javacord.api.interaction.SlashCommandInteractionOption;
import org.javacord.api.interaction.SlashCommandOptionType;
import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData;
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
public class JavacordContextObjectProvider implements ContextObjectProvider {
@@ -16,14 +18,14 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
return null;
SlashCommandInteraction interaction = (SlashCommandInteraction) context;
if (!interaction.getArguments().isEmpty())
return getOptionValue(interaction.getOptionByName(optionName).get());
return getOptionValue(interaction.getOptionByName(optionName).orElse(null));
SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst();
if (!subCommandOption.getOptions().isEmpty())
subCommandOption = subCommandOption.getOptions().getFirst();
return getOptionValue(subCommandOption.getOptionByName(optionName).get());
return getOptionValue(subCommandOption.getOptionByName(optionName).orElse(null));
}
private Object getOptionValue(SlashCommandInteractionOption option) {
@@ -89,7 +91,7 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
@Override
public Object getInteractionContextObject(Object context, Class<?> type) {
ButtonInteraction button = (ButtonInteraction) context;
InteractionBase button = (InteractionBase) context;
switch (type.getName()) {
case "org.javacord.api.entity.channel.TextChannel":
return button.getChannel().orElse(null);
@@ -102,9 +104,9 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
}
@Override
public Object getAutocompleteFocusedOption(Object context) {
AutocompleteInteraction interaction = (AutocompleteInteraction) context;
return getOptionValue(interaction.getFocusedOption());
public AutocompleteOptionData getAutocompleteFocusedOption(Object context) {
SlashCommandInteractionOption option = ((AutocompleteInteraction) context).getFocusedOption();
return new AutocompleteOptionData(option.getName(), getOptionValue(option));
}
}

View File

@@ -50,10 +50,10 @@ public class JavacordConverterSpec implements CommandConverter.Spec<SlashCommand
.setName(option.name())
.setDescription(option.description())
.setRequired(option.required())
.setAutocompletable(option.autocomplete())
.setMinLength(Double.valueOf(option.range().min()).longValue())
.setAutocompletable(option.autocompletes().length > 0)
.setMinLength((long) option.range().min())
.setDecimalMinValue(option.range().min())
.setMaxLength(Double.valueOf(option.range().max()).longValue())
.setMaxLength((long) option.range().max())
.setDecimalMaxValue(option.range().max())
.setChoices(choices)
.build();

View File

@@ -1,7 +1,11 @@
package net.tomatentum.marinara.wrapper.javacord;
import java.util.List;
import org.javacord.api.DiscordApi;
import org.javacord.api.interaction.AutocompleteInteraction;
import org.javacord.api.interaction.SlashCommandBuilder;
import org.javacord.api.interaction.SlashCommandOptionChoice;
import org.slf4j.Logger;
import net.tomatentum.marinara.wrapper.CommandConverter;
@@ -27,7 +31,7 @@ public class JavacordWrapper extends LibraryWrapper {
if (api != null) {
this.commandRegisterer = CommandRegisterer.of(new JavacordRegistererStrategy(api), converter);
api.addInteractionCreateListener((e) -> handleInteraction(e.getInteraction()));
api.addInteractionCreateListener(e -> handleInteraction(e.getInteraction()));
}else
logger.warn("DiscordApi was null so no Events were subscribed to.");
logger.info("Javacord wrapper loaded!");
@@ -52,4 +56,15 @@ public class JavacordWrapper extends LibraryWrapper {
return contextObjectProvider;
}
@Override
public void respondAutocomplete(Object context, List<Object> options) {
if (context instanceof AutocompleteInteraction interaction) {
List<SlashCommandOptionChoice> choices = options.stream()
.filter(SlashCommandOptionChoice.class::isInstance)
.map(o -> (SlashCommandOptionChoice)o)
.toList();
interaction.respondWithChoices(choices);
}
}
}

View File

@@ -1,53 +1,39 @@
package net.tomatentum.marinara.wrapper.javacord.identifierconverter;
import java.util.List;
import java.util.Optional;
import org.javacord.api.interaction.AutocompleteInteraction;
import org.javacord.api.interaction.SlashCommandInteractionOption;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier;
import net.tomatentum.marinara.registry.InteractionEntry;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
public class AutocompleteIdentifierConverter implements IdentifierProvider.Converter<AutocompleteInteraction> {
@Override
public InteractionIdentifier convert(AutocompleteInteraction context, InteractionRegistry registry) {
public InteractionIdentifier convert(AutocompleteInteraction context) {
List<SlashCommandInteractionOption> options = context.getOptions();
String commandName = context.getCommandName();
InteractionIdentifier ident;
if (!options.isEmpty()) {
List<SlashCommandInteractionOption> sub_options = context.getOptions().getFirst().getOptions();
if (!sub_options.isEmpty())
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
List<SlashCommandInteractionOption> subOptions = context.getOptions().getFirst().getOptions();
if (!subOptions.isEmpty())
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName,
options.getFirst().getName(),
sub_options.getFirst().getName());
subOptions.getFirst().getName());
else
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName,
options.getFirst().getName());
}else
ident = InteractionIdentifier.createHierarchy(
InteractionType.COMMAND,
return InteractionIdentifier.createHierarchy(
InteractionType.AUTOCOMPLETE,
commandName);
Optional<InteractionEntry> entry = registry.findFor(ident);
if (entry.isPresent() && entry.get().identifier() instanceof SlashCommandIdentifier) {
SlashCommandIdentifier sIdent = (SlashCommandIdentifier) entry.get().identifier();
return InteractionIdentifier.builder()
.type(InteractionType.AUTOCOMPLETE)
.name(sIdent.autocompleteRef()[0])
.build();
}
return null;
}
}

View File

@@ -4,13 +4,12 @@ import org.javacord.api.interaction.ButtonInteraction;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
public class ButtonIdentifierConverter implements IdentifierProvider.Converter<ButtonInteraction> {
@Override
public InteractionIdentifier convert(ButtonInteraction context, InteractionRegistry registry) {
public InteractionIdentifier convert(ButtonInteraction context) {
return InteractionIdentifier.builder().name(context.getCustomId()).type(InteractionType.BUTTON).build();
}

View File

@@ -7,13 +7,12 @@ import org.javacord.api.interaction.SlashCommandInteractionOption;
import net.tomatentum.marinara.interaction.InteractionType;
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
import net.tomatentum.marinara.registry.InteractionRegistry;
import net.tomatentum.marinara.wrapper.IdentifierProvider;
public class SlashCommandIdentifierConverter implements IdentifierProvider.Converter<SlashCommandInteraction> {
@Override
public InteractionIdentifier convert(SlashCommandInteraction context, InteractionRegistry registry) {
public InteractionIdentifier convert(SlashCommandInteraction context) {
List<SlashCommandInteractionOption> options = context.getOptions();
String commandName = context.getCommandName();
if (!options.isEmpty()) {

View File

@@ -6,17 +6,27 @@ import java.util.Collections;
import org.javacord.api.event.interaction.SlashCommandCreateEvent;
import org.javacord.api.interaction.AutocompleteInteraction;
import org.javacord.api.interaction.SlashCommandInteraction;
import net.tomatentum.marinara.interaction.InteractionHandler;
import net.tomatentum.marinara.interaction.annotation.AutoComplete;
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand;
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption;
import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType;
public class TestAutocomplete implements InteractionHandler {
@SlashCommand(name = "test")
@SlashCommand(
name = "test",
options = @SlashCommandOption(
name = "foo",
type = SlashCommandOptionType.STRING,
autocompletes = @AutoComplete("testAuto")
)
)
@AutoComplete("testAuto")
public void exec(SlashCommandCreateEvent context) {
public void exec(SlashCommandInteraction context) {
}
@AutoComplete("testAuto")