Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions application/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jib {
}
container {
mainClass = 'org.togetherjava.tjbot.BootstrapLauncher'
setCreationTime(java.time.Instant.now().toString())
setCreationTime(Instant.now().toString())
}
}

Expand All @@ -45,7 +45,7 @@ shadowJar {
dependencies {
implementation project(':database')

implementation 'net.dv8tion:JDA:4.4.0_352'
implementation 'net.dv8tion:JDA:5.0.0-alpha.5'

implementation 'org.apache.logging.log4j:log4j-core:2.16.0'
runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.16.0'
Expand All @@ -71,4 +71,4 @@ dependencies {

application {
mainClass = 'org.togetherjava.tjbot.BootstrapLauncher'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* Main class of the application. Use {@link #main(String[])} to start an instance of it.
* <p>
* New commands can be created by implementing
* {@link net.dv8tion.jda.api.events.interaction.SlashCommandEvent} or extending
* {@link net.dv8tion.jda.api.events.interaction.SlashCommandInteractionEvent} or extending
* {@link org.togetherjava.tjbot.commands.SlashCommandAdapter}. They can then be registered in
* {@link Features}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package org.togetherjava.tjbot.commands;

import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import org.jetbrains.annotations.NotNull;
import org.togetherjava.tjbot.commands.componentids.ComponentId;
import org.togetherjava.tjbot.commands.componentids.ComponentIdGenerator;
import org.togetherjava.tjbot.commands.componentids.Lifespan;

import java.util.List;

/**
* Represents a Discord command.
* <p>
* All commands have to implement this interface. For convenience, there is a
* {@link BotCommandAdapter} available that implemented most methods already. A new command can then
* be registered by adding it to {@link Features}.
* <p>
* <p>
* Commands can either be visible globally in Discord or just to specific guilds. Some
* configurations can be made via {@link CommandData}, which is then to be returned by
* {@link #getData()} where the system will then pick it up from.
* <p>
* After registration, the system will notify a command whenever one of its corresponding command
* method, buttons ({@link #onButtonClick(ButtonInteractionEvent, List)}) or menus
* ({@link #onSelectionMenu(SelectMenuInteractionEvent, List)}) have been triggered.
* <p>
* <p>
* Some example commands are available in {@link org.togetherjava.tjbot.commands.basic}.
*/
public interface BotCommand extends Feature {

/**
* Gets the name of the command.
* <p>
* After registration of the command, the name must not change anymore.
*
* @return the name of the command
*/
@NotNull
String getName();


/**
* Gets the type of this command.
* <p>
* After registration of the command, the type must not change anymore.
*
* @return the type of the command
*/
@NotNull
Command.Type getType();


/**
* Gets the visibility of this command.
* <p>
* After registration of the command, the visibility must not change anymore.
*
* @return the visibility of the command
*/
@NotNull
CommandVisibility getVisibility();

/**
* Gets the command data belonging to this command.
* <p>
* See {@link net.dv8tion.jda.api.interactions.commands.build.Commands} for details on how to
* create and configure instances of it.
* <p>
* <p>
* This method may be called multiple times, implementations must not create new data each time
* but instead configure it once beforehand. The core system will automatically call this method
* to register the command to Discord.
*
* @return the command data of this command
*/
@NotNull
CommandData getData();


/**
* Buttons can be attached below messages, and all buttons should provide the styling Discord
* provides. This means, red only for destructive actions, green only for successful etc. For
* examples on how they look, see
* <a href="https://support.discord.com/hc/en-us/articles/1500012250861-Bots-Buttons">Discord's
* support article</a>.
*
* Triggered by the core system when a button corresponding to this implementation (based on
* {@link #getData()}) has been clicked.
* <p>
* This method may be called multi-threaded. In particular, there are no guarantees that it will
* be executed on the same thread repeatedly or on the same thread that other event methods have
* been called on.
* <p>
* Details are available in the given event and the event also enables implementations to
* respond to it.
* <p>
* This method will be called in a multi-threaded context and the event may not be hold valid
* forever.
*
* @param event the event that triggered this
* @param args the arguments transported with the button, see
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for details on how these are
* created
*/
void onButtonClick(@NotNull ButtonInteractionEvent event, @NotNull List<String> args);

/**
* Select menus can be attached below messages, just like Buttons. They allow you to configure a
* certain set of options, that the user can choose. For examples on how they look, see <a href=
* "https://support.discord.com/hc/en-us/articles/4403375759255-Bots-Select-Menus">Discord's
* support article.</a>
*
* Triggered by the core system when a selection menu corresponding to this implementation
* (based on {@link #getData()}) has been clicked.
* <p>
* This method may be called multi-threaded. In particular, there are no guarantees that it will
* be executed on the same thread repeatedly or on the same thread that other event methods have
* been called on.
* <p>
* Details are available in the given event and the event also enables implementations to
* respond to it.
* <p>
* This method will be called in a multi-threaded context and the event may not be hold valid
* forever.
*
* @param event the event that triggered this
* @param args the arguments transported with the selection menu, see
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for details on how these are
* created
*/
void onSelectionMenu(@NotNull SelectMenuInteractionEvent event, @NotNull List<String> args);

/**
* Triggered by the core system during its setup phase. It will provide the command a component
* id generator through this method, which can be used to generate component ids, as used for
* button or selection menus.
*
* <p>
* The component ID has to be a UUID-string (see {@link java.util.UUID}), which is associated to
* a specific database entry, containing meta information about the command being executed. Such
* a database entry can be created and a UUID be obtained by using
* {@link ComponentIdGenerator#generate(ComponentId, Lifespan)}, as provided by the instance
* given to this method during system setup. The required {@link ComponentId} instance accepts
* optional extra arguments, which, if provided, can be picked up during the corresponding event
* (see {@link #onButtonClick(ButtonInteractionEvent, List)},
* {@link #onSelectionMenu(SelectMenuInteractionEvent, List)}).
* <p>
* Alternatively, if {@link BotCommandAdapter} has been extended, it also offers a handy
* {@link BotCommandAdapter#generateComponentId(String...)} method to ease the flow.
* <p>
* See <a href="https://github.com/Together-Java/TJ-Bot/wiki/Component-IDs">Component-IDs</a> on
* our Wiki for more details and examples of how to use component IDs.
*
*
* @param generator the provided component id generator
*/
void acceptComponentIdGenerator(@NotNull ComponentIdGenerator generator);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package org.togetherjava.tjbot.commands;

import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.togetherjava.tjbot.commands.componentids.ComponentId;
import org.togetherjava.tjbot.commands.componentids.ComponentIdGenerator;
import org.togetherjava.tjbot.commands.componentids.Lifespan;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
* Adapter implementation of a {@link BotCommand}. The minimal setup only requires implementation of
* their respective command method. A new command can then be registered by adding it to
* {@link Features}.
* <p>
* Further, {@link #onButtonClick(ButtonInteractionEvent, List)} and
* {@link #onSelectionMenu(SelectMenuInteractionEvent, List)} can be overridden if desired. The
* default implementation is empty, the adapter will not react to such events.
* <p>
* <p>
* The adapter manages some getters for you, you've to create the {@link CommandData} yourself. See
* {@link #BotCommandAdapter(CommandData, CommandVisibility)}} for more info on that. Minimal
* modifications can be done on the {@link CommandData} returned by {@link #getData()}.
* <p>
* <p>
* If implementations want to add buttons or selection menus, it is highly advised to use component
* IDs generated by {@link #generateComponentId(String...)}, which will automatically create IDs
* that are valid per {@link SlashCommand#onSlashCommand(SlashCommandInteractionEvent)}.
* <p>
* <p>
* Some example commands are available in {@link org.togetherjava.tjbot.commands.basic}.
* Registration of commands can be done in {@link Features}.
*/
public abstract class BotCommandAdapter implements BotCommand {
private final String name;
private final Command.Type type;
private final CommandVisibility visibility;
private final CommandData data;
private ComponentIdGenerator componentIdGenerator;

/**
* Creates a new adapter with the given data.
*
* @param data the data for this command
* @param visibility the visibility of the command
*/
protected BotCommandAdapter(@NotNull CommandData data, @NotNull CommandVisibility visibility) {
this.data = Objects.requireNonNull(data, "The data shouldn't be null");
this.visibility = Objects.requireNonNull(visibility, "The visibility shouldn't be null");

this.name = data.getName();
this.type = data.getType();
}

@Override
public final @NotNull String getName() {
return name;
}

@NotNull
@Override
public Command.Type getType() {
return type;
}

@Override
public final @NotNull CommandVisibility getVisibility() {
return visibility;
}

@Override
public @NotNull CommandData getData() {
return data;
}

@Override
@Contract(mutates = "this")
public final void acceptComponentIdGenerator(@NotNull ComponentIdGenerator generator) {
componentIdGenerator =
Objects.requireNonNull(generator, "The given generator cannot be null");
}

@SuppressWarnings("NoopMethodInAbstractClass")
@Override
public void onButtonClick(@NotNull ButtonInteractionEvent event, @NotNull List<String> args) {
// Adapter does not react by default, subclasses may change this behavior
}

@SuppressWarnings("NoopMethodInAbstractClass")
@Override
public void onSelectionMenu(@NotNull SelectMenuInteractionEvent event,
@NotNull List<String> args) {
// Adapter does not react by default, subclasses may change this behavior
}

/**
* Helper method to generate component IDs that are considered valid per
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)}.
* <p>
* They can be used to create buttons or selection menus and transport additional data
* throughout the event (e.g. the user id who created the button dialog).
* <p>
* IDs generated by this method have a regular lifespan, meaning that they might get evicted and
* expire after not being used for a long time. Use
* {@link #generateComponentId(Lifespan, String...)} to set other lifespans, if desired.
*
* @param args the extra arguments that should be part of the ID
* @return the generated component ID
*/
@SuppressWarnings("OverloadedVarargsMethod")
protected final @NotNull String generateComponentId(@NotNull String... args) {
return generateComponentId(Lifespan.REGULAR, args);
}

/**
* Helper method to generate component IDs that are considered valid per
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)}.
* <p>
* They can be used to create buttons or selection menus and transport additional data
* throughout the event (e.g. the user id who created the button dialog).
*
* @param lifespan the lifespan of the component id, controls when an id that was not used for a
* long time might be evicted and expire
* @param args the extra arguments that should be part of the ID
* @return the generated component ID
*/
@SuppressWarnings({"OverloadedVarargsMethod", "WeakerAccess"})
protected final @NotNull String generateComponentId(@NotNull Lifespan lifespan,
@NotNull String... args) {
return componentIdGenerator.generate(new ComponentId(getName(), Arrays.asList(args)),
lifespan);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Visibility of a slash command, i.e. in which context it can be used by users.
*/
public enum SlashCommandVisibility {
public enum CommandVisibility {
/**
* The command can be used within the context of a guild.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public enum Features {
features.add(new RoleSelectCommand());
features.add(new TopHelpersCommand(database));

// Message context commands

// User context commands

// Mixtures
features.add(new FreeCommand());

Expand Down
Loading