Skip to content

Commit

Permalink
allow components from different classes in reply [#147]
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaktushose committed Jan 11, 2025
1 parent a4ca474 commit 5eb4b6f
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,34 +124,34 @@ public void updateGuildCommands() {
updater.updateGuildCommands();
}

/// Gets a [`Button`][com.github.kaktushose.jda.commands.annotations.interactions.Button] based on the definition id
/// and transforms it into a JDA [Button].
/// Gets a [`Button`][com.github.kaktushose.jda.commands.annotations.interactions.Button] based on the method name
/// and the given class and transforms it into a JDA [Button].
///
/// The button will be [`Runtime`]({@docRoot}/index.html#runtime-concept-heading) independent. This may be useful if you want to send a message without
/// using the framework.
/// The button will be [`Runtime`]({@docRoot}/index.html#runtime-concept-heading) independent.
/// This may be useful if you want to send a message without using the framework.
///
/// @param button the name of the button in the format `FullClassNameWithPackage.method``
/// @return the JDA [Button]
@NotNull
public Button getButton(@NotNull String button) {
var id = String.valueOf(button.replaceAll("\\.", "").hashCode());
public Button getButton(@NotNull Class<?> origin, @NotNull String button) {
var id = String.valueOf((origin.getName() + button).hashCode());
var definition = interactionRegistry.find(ButtonDefinition.class, false, it -> it.definitionId().equals(id));
return definition.toJDAEntity(CustomId.independent(definition.definitionId()));
}

/// Gets a [StringSelectMenu] or [EntitySelectMenu] based on the definition id and transforms it into a JDA [SelectMenu].
/// Gets a [StringSelectMenu] or [EntitySelectMenu] based on the method name and the given class and transforms it
/// into a JDA [SelectMenu].
///
/// The select menu will be [`Runtime`]({@docRoot}/index.html#runtime-concept-heading) independent. This may be useful if you want to send a component
/// without using the framework.
/// The select menu will be [`Runtime`]({@docRoot}/index.html#runtime-concept-heading) independent.
/// This may be useful if you want to send a component without using the framework.
///
/// @param origin the [Class] of the method
/// @param menu the name of the button in the format `FullClassNameWithPackage.method``
/// @return the JDA [SelectMenu]
@SuppressWarnings("unchecked")
@NotNull
public SelectMenu getSelectMenu(@NotNull String menu) {
var id = String.valueOf(menu.replaceAll("\\.", "").hashCode());
public SelectMenu getSelectMenu(@NotNull Class<?> origin, @NotNull String menu) {
var id = String.valueOf((origin.getName() + menu).hashCode());
var definition = interactionRegistry.find(SelectMenuDefinition.class, false, it -> it.definitionId().equals(id));
return (SelectMenu) definition.toJDAEntity(CustomId.independent(definition.definitionId()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.kaktushose.jda.commands.annotations.interactions.EntitySelectMenu;
import com.github.kaktushose.jda.commands.annotations.interactions.StringSelectMenu;
import com.github.kaktushose.jda.commands.definitions.features.CustomIdJDAEntity;
import com.github.kaktushose.jda.commands.definitions.interactions.CustomId;
import com.github.kaktushose.jda.commands.definitions.interactions.InteractionDefinition;
import com.github.kaktushose.jda.commands.definitions.interactions.InteractionRegistry;
Expand All @@ -18,10 +19,12 @@
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.interactions.components.ActionComponent;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.interactions.components.selections.SelectMenu;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -82,13 +85,25 @@ public void removeComponents() {
/// The button will be linked to the current [`Runtime`]({@docRoot}/index.html#runtime-concept-heading).
/// This may be useful if you want to send a component without using the framework.
///
/// @param button the name of the button
/// @param button the name of the button defining method
/// @return the JDA [Button]
@NotNull
public Button getButton(@NotNull String button) {
var id = String.valueOf(("%s%s".formatted(definition.clazzDescription().name(), button)).hashCode());
var definition = registry.find(ButtonDefinition.class, false, it -> it.definitionId().equals(id));
return definition.toJDAEntity(new CustomId(runtimeId(), definition.definitionId()));
return getComponent(button, null, ButtonDefinition.class);
}

/// Gets a [`Button`][com.github.kaktushose.jda.commands.annotations.interactions.Button] based on the method name
/// and the given class and transforms it into a JDA [Button].
///
/// The button will be [`runtime`]({@docRoot}/index.html#runtime-concept-heading)-independent.
/// This may be useful if you want to send a component without using the framework.
///
/// @param origin the [Class] of the method
/// @param button the name of the button defining method
/// @return the JDA [Button]
@NotNull
public Button getButton(@NotNull Class<?> origin, @NotNull String button) {
return getComponent(button, null, ButtonDefinition.class);
}

/// Gets a [StringSelectMenu] or [EntitySelectMenu] based on the method name and transforms it into a JDA [SelectMenu].
Expand All @@ -100,9 +115,29 @@ public Button getButton(@NotNull String button) {
/// @return the JDA [SelectMenu]
@NotNull
public SelectMenu getSelectMenu(@NotNull String menu) {
var id = String.valueOf(("%s%s".formatted(definition.clazzDescription().name(), menu)).hashCode());
var definition = registry.find(SelectMenuDefinition.class, false, it -> it.definitionId().equals(id));
return (SelectMenu) definition.toJDAEntity(new CustomId(runtimeId(), definition.definitionId()));
return getComponent(menu, null, SelectMenuDefinition.class);
}

/// Gets a [StringSelectMenu] or [EntitySelectMenu] based on the method name and transforms it into a JDA [SelectMenu].
///
/// The select menu will be [`runtime`]({@docRoot}/index.html#runtime-concept-heading)-independent.
/// This may be useful if you want to send a component without using the framework.
///
/// @param origin the [Class] of the method
/// @param menu the name of the select menu
/// @return the JDA [SelectMenu]
@NotNull
public SelectMenu getSelectMenu(@NotNull Class<?> origin, @NotNull String menu) {
return getComponent(menu, origin, SelectMenuDefinition.class);
}

@NotNull
@SuppressWarnings("unchecked")
private <C extends ActionComponent, E extends CustomIdJDAEntity<?>> C getComponent(@NotNull String component, @Nullable Class<?> origin, @NotNull Class<E> type) {
var className = origin == null ? definition.clazzDescription().name() : origin.getName();
var id = String.valueOf((className + component).hashCode());
var definition = registry.find(type, false, it -> it.definitionId().equals(id));
return (C) definition.toJDAEntity(new CustomId(runtimeId(), definition.definitionId()));
}

/// Entry point for configuring a reply.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.github.kaktushose.jda.commands.annotations.interactions.Button;
import com.github.kaktushose.jda.commands.annotations.interactions.EntitySelectMenu;
import com.github.kaktushose.jda.commands.annotations.interactions.StringSelectMenu;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/// Represents a component, namely [Button], [StringSelectMenu] or [EntitySelectMenu], that should be added to a reply.
///
Expand All @@ -23,9 +25,9 @@
/// event.with().components(Components.of(true, false, "onButton")).reply();
/// }
///```
public record Component(boolean enabled, boolean independent, String name) {
public record Component(boolean enabled, boolean independent, @NotNull String name, @Nullable Class<?> origin) {

/// Adds enabled, runtime-bound [Component]s to the reply.
/// Adds an enabled, runtime-bound [Component] to the reply.
///
/// @param component the name of the method that represents the component
public static Component enabled(String component) {
Expand All @@ -39,7 +41,7 @@ public static Component disabled(String component) {
return of(false, false, component);
}

/// Adds enabled, runtime-independent [Component]s to the reply.
/// Adds an enabled, runtime-independent [Component] to the reply.
///
/// Every component interaction will create a new [`Runtime`]({@docRoot}/index.html#runtime-concept-heading). Furthermore, the component cannot expire and
/// will always get executed, even after a bot restart.
Expand All @@ -49,12 +51,40 @@ public static Component independent(String component) {
return of(true, true, component);
}

/// Adds an enabled [Component] to the reply that is defined in a different class. This [Component] will always be
/// runtime-independent.
///
/// @param origin the [Class] the `component` is defined in
/// @param component the name of the method that represents the component
public static Component enabled(Class<?> origin, String component) {
return of(true, origin, component);
}

/// Adds a disabled [Component] to the reply that is defined in a different class. This [Component] will always be
/// runtime-independent.
///
/// @param origin the [Class] the `component` is defined in
/// @param component the name of the method that represents the component
public static Component disabled(Class<?> origin, String component) {
return of(false, origin, component);
}

/// Adds [Component]s with the passed configuration to the reply.
///
/// @param enabled whether the [Component] should be enabled or disabled
/// @param independent whether the [Component] should be runtime-bound or independent
/// @param component the name of the method that represents the component
/// @param component the name of the method that represents the component
public static Component of(boolean enabled, boolean independent, String component) {
return new Component(enabled, independent, component);
return new Component(enabled, independent, component, null);
}

/// Adds [Component]s with the passed configuration to the reply.
///
/// @param enabled whether the [Component] should be enabled or disabled
/// @param origin the [Class] the `component` is defined in
/// @param component the name of the method that represents the component
public static Component of(boolean enabled, Class<?> origin, String component) {
return new Component(enabled, true, component, origin);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ public ComponentReply components(@NotNull String... components) {
public ComponentReply components(@NotNull Component... components) {
List<ItemComponent> items = new ArrayList<>();
for (Component component : components) {
var definitionId = String.valueOf((definition.methodDescription().declaringClass().getName() + component.name()).hashCode());
var className = component.origin() == null
? definition.methodDescription().declaringClass().getName()
: component.origin().getName();
var definitionId = String.valueOf((className + component.name()).hashCode());
var definition = registry.find(ComponentDefinition.class, false, it ->
it.definitionId().equals(definitionId)
);
Expand Down

0 comments on commit 5eb4b6f

Please sign in to comment.