jda.wiki Open in urlscan Pro
2606:50c0:8003::153  Public Scan

URL: https://jda.wiki/using-jda/using-restaction/
Submission: On September 23 via api from US — Scanned from CA

Form analysis 2 forms found in the DOM

<form class="md-header__option" data-md-component="palette">
  <input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="purple" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette"
    id="__palette_0">
  <label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path
        d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31">
      </path>
    </svg>
  </label>
  <input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="purple" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
  <label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden="">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path
        d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z">
      </path>
    </svg>
  </label>
</form>

Name: search

<form class="md-search__form" name="search">
  <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required="">
  <label class="md-search__icon md-icon" for="__search">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"></path>
    </svg>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"></path>
    </svg>
  </label>
  <nav class="md-search__options" aria-label="Search">
    <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
        <path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
      </svg>
    </button>
  </nav>
</form>

Text Content

Skip to content

JDA Wiki
Using RestAction

Type to start searching
JDA
 * v5.1.1
 * 4.3k
 * 739

 * Getting Started
 * Setup
 * Using JDA
 * Contributing
 * Javadocs

JDA Wiki
JDA
 * v5.1.1
 * 4.3k
 * 739

 * Getting Started
   Getting Started
    * JDA
    * Using Events
    * List of Events
    * FAQ
    * Migration V3 -> V4
    * Migration V4 -> V5

 * Setup
   Setup
    * IntelliJ IDEA Setup
    * Eclipse Setup
    * Netbeans Setup
    * Logging (SLF4J)

 * Using JDA
   Using JDA
    * Getting Started
    * Making a Music Bot
    * Using RestAction Using RestAction
      Table of contents
       * AuditLog Reasons
       * Using queue()
       * Using submit()
       * Using complete()
       * Using completeAfter, submitAfter and queueAfter
   
    * Interactions
    * Paginating Entities
    * Gateway Intents and Member Cache Policy
    * Webhooks
    * Managers and Moderation
    * Using New Features
    * Troubleshooting

 * Contributing
   Contributing
    * Contributing
    * Structure Guide
    * Repository Structure

 * Javadocs


WHAT IS A RESTACTION?¶

If you understand RestAction you understand JDA.

In JDA 3.0 we introduced the new RestAction class which basically is a terminal
between the JDA user and the Discord REST API.
The RestAction is a step between specifying what the user wants to do and
executing it, it allows the user to specify how JDA should deal with their
Request.

However this only works if you actually tell the RestAction to do something.
That is why we recommend checking out whether or not something in JDA returns a
RestAction. If that is the case you have to execute it using one of the
RestAction execution operations:

queue(), queue(Consumer), queue(Consumer, Consumer) These operations are
asynchronous and will not execute within the same Thread.
This means that you cannot use procedural logic when you use queue(), unless you
use the callback Consumers.
Only similar requests are internally executed in sequence such as sending
messages in the same channel or adding reactions to the same message. submit()
Provides request future to cancel tasks later and avoid callback hell.
complete() This operation will block the current Thread until the request has
been finished and will return the response type.

Note

We recommend using queue() or submit() when possible as blocking the current
Thread can cause downtime and will use more resources.

Since 4.1.1 you can use a few RestAction operators to avoid callback hell with
queue:

 * map Convert the result of the RestAction to a different value
 * flatMap Chain another RestAction on the result
 * delay Delay the element of the previous step

JavaDocs: https://docs.jda.wiki/net/dv8tion/jda/api/requests/RestAction.html


AUDITLOG REASONS¶

Some operations return a special RestAction implementation called
AuditableRestAction.
This extension allows to set a reason field for that action.

Example

public class ModerationUtil
{
    public static void deleteMessage(Message message, String reason)
    {
        message.delete().reason(reason).queue();
    }

    public static void ban(Guild guild, User user, String reason)
    {
        guild.ban(user, 7, reason).queue();
    }
}



USING QUEUE()¶

The most common way to execute a RestAction is by simply calling .queue() after
the operation:

public void sendMessage(MessageChannel channel, String message) 
{
    channel.sendMessage(message).queue();
}




This will always simply execute the RestAction<Message> which was returned by
MessageChannel.sendMessage(String).
Note that this might happen after calling sendMessage(MessageChannel, String)
because queue() is asynchronous!

> You: Why can't I access the Message that was sent with queue()?
> Minn: Use the success callback!

Is one of the common conversations we had when people started using JDA 3.0.

> You: What does that mean?

A success callback is what we call the primary Consumer that can be passed to a
queue() statement:

public void sendAndLog(MessageChannel channel, String message) 
{
    channel.sendMessage(message).queue(new Consumer<Message>()
    {
        @Override
        public void accept(Message t)
        {
            System.out.printf("Sent Message %s\n", t);
        }
    });
}




Here we used an inline implementation of Consumer<Message> that handles the
response of a REST Request.
The method Consumer.accept(Message) is automatically called once the response
has been received by the JDA Requester.

> Minn: But that looks really ugly...
> You: Yeah but it works!!

Since JDA requires you to use Java 1.8 we can use one of the new features:
Lambda Expressions

public void sendAndLog(MessageChannel channel, String message)
{
    // Here we use a lambda expressions which names the callback parameter -response- and uses that as a reference
    // in the callback body -System.out.printf("Sent Message %s\n", response)-
    Consumer<Message> callback = (response) -> System.out.printf("Sent Message %s\n", response);
    channel.sendMessage(message).queue(callback); // ^ calls that
}




> You: Wow that looks so much better!
> Minn: Yes, please learn more about lambda expressions: lambda quickstart

Example: Sending a Private Message



public void sendPrivateMessage(User user, String content)
{
    // openPrivateChannel provides a RestAction<PrivateChannel> 
    // which means it supplies you with the resulting channel
    user.openPrivateChannel().queue((channel) ->
    {
        // value is a parameter for the `accept(T channel)` method of our callback.
        // here we implement the body of that method, which will be called later by JDA automatically.
        channel.sendMessage(content).queue();
        // here we access the enclosing scope variable -content-
        // which was provided to sendPrivateMessage(User, String) as a parameter
    });
}


Since this only calls a single method in the callback you can use the short
form:

public void sendPrivateMessage(User user, String content)
{
    // notice that we are not placing a semicolon (;) in the callback this time!
    user.openPrivateChannel().queue( (channel) -> channel.sendMessage(content).queue() );
}





USING SUBMIT()¶

Sometimes execution needs to be cancelled if it isn't required anymore. This can
be challenging to do if you use queue() or complete().
In submit() JDA will provide a CompletableFuture (aka promise) which allows the
cancellation of a request.

If you don't need to use the CompletableFuture you may use queue() instead!

Example

public void setTestingChannel(TextChannel channel)
{
    channel.getManager().setName("testing-channel").queue( (v) ->
        channel.sendMessage("Update Channel").queue( (m) ->
            m.delete().queueAfter(30, TimeUnit.SECONDS, (t) ->
                logChannel.sendMessage("Deleted Response in %s", channel).queue()
            )
        )
    );
}

// turns into
public void setTestingChannel(TextChannel channel)
{
    channel.getManager().setName("testing-channel").submit()                   // CompletableFuture<Void>
        .thenCompose((v) -> channel.sendMessage("Update Channel").submit()) // CompletableFuture<Message>
        .thenCompose((m) -> m.delete().submitAfter(30, TimeUnit.SECONDS))   // CompletableFuture<Void>
        .thenCompose((v) -> logChannel.sendMessage("Deleted Response in %s", channel).submit())
        .whenComplete((s, error) -> {
            // this will be called for every termination (success/failure)
            // if the result is successful the error will be null
            // otherwise you should handle the error here to prevent it from being eaten and never printed
            if (error != null) error.printStackTrace();
        });
}


Note

You can do the same with RestAction#flatMap in 4.1.1

public class RateLimitListener extends ListenerAdapter
{
    private final long guildId;
    private final long userId;
    private final Queue<RequestFuture<Void>> tasks = new LinkedList<>();

    public RateLimitListener(Guild guild, User user)
    {
        guildId = guild.getIdLong();
        userId = user.getIdLong();
        // only store IDs as JDA objects can be disposed by cache invalidation
        //when disposed the entity is not usable anymore, since we only need the id this is good enough
    }

    @Override
    public void onGuildMessageReceived(GuildMessageReceivedEvent event)
    {
        if (event.getAuthor().getIdLong() != userId)
            return; // ignore other users
        if (event.getGuild().getIdLong() != guildId)
            return; // ignore other guilds
        RequestFuture<Void> task = event.getMessage().delete().submit();
        tasks.add(task); // add task to cancel queue in case user gets banned
        task.thenRun(() -> tasks.remove(task)); // remove once completed
    }

    @Override
    public void onGuildBan(GuildBanEvent event)
    {
        if (event.getUser().getIdLong() != userId)
            return; // ignore other users
        if (event.getGuild().getIdLong() != guildId)
            return; // ignore other guilds

        // stop deleting messages for banned user
        RequestFuture<Void> current;
        while ((current = tasks.poll()) != null)
            current.cancel(true); 
        tasks.clear();

        // remove this as listener, our task has completed!
        event.getJDA().removeEventListener(this);
    }
}



USING COMPLETE()¶

The complete() operation is simply for your convenience. It will block the
Thread that you call it on which means it will not be able to continue with
other tasks in the meantime.
If you don't use the return value or don't need the request to be completed
before continuing with other operations it is recommended to use queue()
instead!

Example



public void setTestingChannel(TextChannel channel)
{
    channel.getManager().setName("testing-channel").queue( (v) ->
        channel.sendMessage("Update Channel").queue( (m) ->
            m.delete().queueAfter(30, TimeUnit.SECONDS, (t) ->
                logChannel.sendMessage("Deleted Response in %s", channel).queue()
            )
        )
    );
}

public void setTestingChannelBlocking(TextChannel channel)
{
    channel.getManager().setName("testing-channel").complete();
    Message m = channel.sendMessage("Update Channel").complete();
    m.delete().completeAfter(30, TimeUnit.SECONDS);
    logChannel.sendMessage("Deleted Response in %s", channel).queue();
    // note how we used queue in the end because we don't need it sequenced anymore.
}


This is called a callback hell

public Message sendAndLog(MessageChannel channel, String message)
{
    Message response = channel.sendMessage(message).complete();
    System.out.printf("Sent Message %s\n", response);
    return response;
}


public PermissionOverride getOverride(Channel channel, Member member)
{
    final PermissionOverride override = channel.getPermissionOverride(member);

    if (override == null)
        return channel.createPermissionOverride(member).complete();

    return override;
}


You can do this asynchronously by using a CompletableFuture:



public CompletableFuture<PermissionOverride> getOverride(Channel channel, Member member)
{
    final PermissionOverride override = channel.getPermissionOverride(member);

    if (override == null)
        return channel.createPermissionOverride(member).submit();

    return CompletableFuture.completedFuture(override);
}

getOverride(channel, member).thenAccept(override -> ...);



USING COMPLETEAFTER, SUBMITAFTER AND QUEUEAFTER¶

These three methods are also known under the term of Planned Execution as they
use a ScheduledExecutorService to schedule calls to either complete or queue.

There are three possible ways to plan a RestAction execution

completeAfter(long, TimeUnit) Blocks and executes on the current Thread, similar
to complete()! Similar to using Thread.join() this will block until the action
has completed. submitAfter(long, TimeUnit), submitAfter(long, TimeUnit,
ScheduledExecutorService) Creates a DelayedCompletableFuture<T> which will hold
the response type as its generic value. This means using get() on the returned
Future will cause the current thread to block and await the execution of the
RestAction and receive the response type. queueAfter(long, TimeUnit),
queueAfter(long, TimeUnit, Consumer<T>), queueAfter(long, TimeUnit, Consumer<T>,
Consumer<Throwable>) Schedules the RestAction execution to be started after the
specified delay, this will not block the thread and handle the execution in the
background. You can optionally provide a ScheduledExecutorService to any of the
queueAfter operations as the last argument.

When no ScheduledExecutorService is provided, these operations will use the
default internal JDA ScheduledExecutorService that is also used to execute queue
callback consumers.

Example completeAfter

public Message waitForEdit(Message message)
{
    return message.editMessage("5 Minutes are over").completeAfter(5, TimeUnit.MINUTES);
}


Example queueAfter

public void remind(User user, String reminder, long delay, TimeUnit unit)
{
    user.openPrivateChannel().queue(
        (channel) -> channel.sendMessage(reminder).queueAfter(delay, unit)
    );
}

public void remindAlternate(User user, String reminder, long delay, TimeUnit unit)
{
    user.openPrivateChannel().queueAfter(delay, unit,
        (channel) -> channel.sendMessage(reminder).queue()
    );
}


Example submitAfter

private Map<String, DelayedCompletableFuture<Message>> tasks = new HashMap<>();

public ScheduledFuture<Message> sendWithTask(MessageChannel channel, String message)
{
    DelayedCompletable<Message> task = channel.sendMessage(message).submitAfter(5, TimeUnit.SECONDS);
    return task;
}

public void doSomething(MessageChannel channel, String message)
throws Exception
{
    tasks.add(channel.getId(), sendWithTask(channel, message));
    for (DelayedCompletable<Message> task : tasks.values())
    {
        // non-blocking alternative is `thenAccept`
        System.out.printf("Task completed: %s\n", task.get());
    }
}



RESOURCES

 * Javadocs
 * GitHub (JDA)
 * GitHub (Wiki)
 * Discord API Docs


COMMUNITY

 * Discord Server
 * Open Collective

Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and Wiki
Contributors