-
Notifications
You must be signed in to change notification settings - Fork 1
Use buttons and button callbacks
When using buttons, we strongly recommend you to use our SlashButton
implementation of a the JDA Button
. Doing so allows you to use some few utilities we made to simplify the use of buttons.
Overall, the SlashButton
is used to same way as the regular JDA Button
but with a few differences and additions:
- instead of defining an id for the button when creating it, you bind the button to a button callback
- the method
SlashButton#withData(String)
allows you to set a payload to the button, which can be retrieved in the callback
As for the Slash Command callbacks, a button callback is simply done by adding a @Slash.Button
annotation to a method.
@Slash.Tag("ping_command")
@Slash.Command(
name = "ping",
description = "Check if the application is online"
)
public class PingCommand {
@Slash.Handler()
public void callback(SlashCommandEvent event) {
event.deferReply()
.setContent("pong!")
.addActionRow(
SlashButton.primary("ping.refresh", "Refresh")
)
.queue();
}
@Slash.Button("ping.refresh")
public void onRefresh(ButtonClickEvent event) {
event.deferEdit()
.setContent("pong! (refreshed)")
.queue();
}
}
The value of the @Slash.Button
callback is the name use to bind buttons to the callback.
A few things to be aware of:
- the method must only have one parameter, being of the
ButtonClickEvent
class type - the method must be public
- the name of a button callback must be unique (for the whole application)
When using payloads, we recommend using the Buffer.Writer
class to write the payload and the Buffer.Reader
to read the payload. These 2 tools make sure that you don't write (or read) a payload with more than 96 characters, which is the maximum size for the payload.
Note: The maximum size for the button payload is of 96 characters and not 100 characters as the 4 first characters are used to bind the button to its callback.
The Buffer.Writer
works the following way:
-
new Buffer.Writer()
orBuffer.Writer#create()
to create a new instance - when writing data with the
Buffer.Writer#write(int, ?)
you need to specify the maximum size allocated to the data
And the Buffer.Reader
:
-
new Buffer.Reader(String)
orBuffer.Reader#of(String)
to create a new instance, where the string is the button's id - then call
Buffer.Reader#read(int)
to read the next n characters and get the data in the wanted type
Here is an updated version of the previous example with the use of the Buffer.Writer
and Buffer.Reader
classes to create payloads.
Note: Payloads are persistent meaning that as long as the code related to the button hasn't been changed, the interaction will always work even after restarting the bot!
@Slash.Tag("ping_command")
@Slash.Command(
name = "ping",
description = "Check if the application is online"
)
public class PingCommand {
@Slash.Handler()
public void callback(SlashCommandEvent event) {
final Buffer.Writer writer = Buffer.Writer.create();
writer.write(18, event.getUser().getId()); // write the author id to restrict who can click on the button
writer.write(8, 0); // write the refresh counter
event.deferReply()
.setContent("pong!")
.addActionRow(
SlashButton.primary("ping.refresh", "Refresh")
.withData(writer.toString())
)
.queue();
}
@Slash.Button("ping.refresh")
public void onRefresh(ButtonClickEvent event) {
final Buffer.Reader reader = Buffer.Reader.of(event.getComponentId());
final Buffer.Writer writer = Buffer.Writer.create();
final String authorId = reader.read(18).asString(); // get the author id
int counter = reader.read(8).asUnsignedInt(); // get the refresh counter
// cancel if the user who clicked on the button isn't the author
if (!event.getUser().getId().equals(authorId)) {
return;
}
counter++; // increment the refresh counter
writer.write(18, authorId); // write the author id
writer.write(8, counter); // write the refresh counter
event.deferEdit()
.setContent("pong! (refreshed " + counter + " time" + (counter == 1 ? "": "s") + ")")
.setActionRow(
SlashButton.primary("ping.refresh", "Refresh")
.withData(writer.toString())
)
.queue();
}
}
Compared to regular button payloads, button sessions can hold a lot more data in exchange of not being persistent anymore. A Session
will automatically timeout after 1 minute (unless specified) and will be destroyed if the bot is restarted.
When creating the Session
, you can customise the timeout and its action:
-
Session#create()
- create a session which expires after 1 minute -
Session#create(long, TimeUnit)
- create a session which expires after the chosen time -
Session#create(long, TimeUnit, InteractionHook, BiConsumer<InteractionHook, Session>)
- create a session which expires after the chosen time and triggers an action on timeout
The Session
class extends the DataObject
one, so use it to store the variables you need. When creating the SlashButton
set as its data either:
-
Session#getUuid()
- to bind the session to the button -
Session#store(Consumer<DataObject>)
- to bind the session to the button in addition of setting more variables, or overwriting variables, for this button only
Note: Using the
Session#store(Consumer<DataObject>)
helps you to set variables for some buttons only. For example, you could have 2 buttons asking if the person is a male or female and have the variablegender=male
orgender=female
set depending on the button clicked.
When retrieving a Session
, you can either:
-
Session#load(String)
- load a session and destroy it afterwards -
Session#renew(String)
- load a session and reset its timeout
Taking the previous example, using a Session
would look like:
@Slash.Tag("ping_command")
@Slash.Command(
name = "ping",
description = "Check if the application is online"
)
public class PingCommand {
private final long timeout = 15L;
private final TimeUnit unit = TimeUnit.SECONDS;
private final BiConsumer<InteractionHook, Session> action = (hook, session) -> {
hook.editOriginal("pong! (timed out)")
.setActionRow(
SlashButton.primary("ping.refresh", "Refresh")
.asDisabled()
)
.queue();
};
@Slash.Handler()
public void callback(SlashCommandEvent event) {
final Session session = Session.create(timeout, unit, event.getHook(), action);
session.put("author_id", event.getUser().getId()) // store the author id
.put("counter", 0); // store the refresh counter
event.deferReply()
.setContent("pong!")
.addActionRow(
SlashButton.primary("ping.refresh", "Refresh")
.withData(session.getUuid())
)
.queue();
}
@Slash.Button("ping.refresh")
public void onRefresh(ButtonClickEvent event) {
final Session session = Session.renew(event.getComponentId());
// cancel if the user who clicked on the button isn't the author
if (session == null || !event.getUser().getId().equals(session.getString("author_id"))) {
return;
}
int counter = session.getUnsignedInt("counter");
counter++; // increment the refresh counter
session.put("counter", counter); // updated the refresh counter
event.deferEdit()
.setContent("pong! (refreshed " + counter + " time" + (counter == 1 ? "": "s") + ")")
.setActionRow(
SlashButton.primary("ping.refresh", "Refresh")
.withData(session.getUuid())
)
.queue();
}
}
Slash Commands by Robin Mercier • azzerial.net