Java Discord API - A comprehensive Java library for building Discord bots and applications
—
Powerful asynchronous request system with automatic rate-limit handling, chaining capabilities, error handling, and execution control for Discord API interactions.
The foundation of JDA's asynchronous request system providing multiple execution patterns.
/**
* Represents an asynchronous Discord API request that can be executed in various ways.
* @param <T> The type that will be returned when this RestAction is executed
*/
interface RestAction<T> {
/** Execute asynchronously without callbacks */
void queue();
/** Execute asynchronously with success callback */
void queue(Consumer<? super T> success);
/** Execute asynchronously with success and failure callbacks */
void queue(Consumer<? super T> success, Consumer<? super Throwable> failure);
/** Execute and return CompletableFuture */
CompletableFuture<T> submit();
/** Execute synchronously (blocking) */
T complete() throws RuntimeException;
/** Execute synchronously with timeout */
T complete(long timeout, TimeUnit unit) throws RuntimeException, TimeoutException;
/** Execute after delay */
RestAction<T> queueAfter(long delay, TimeUnit unit);
RestAction<T> queueAfter(long delay, TimeUnit unit, ScheduledExecutorService executor);
/** Execute after delay with callbacks */
void queueAfter(long delay, TimeUnit unit, Consumer<? super T> success);
void queueAfter(long delay, TimeUnit unit, Consumer<? super T> success, Consumer<? super Throwable> failure);
/** Execute after delay and return future */
CompletableFuture<T> submitAfter(long delay, TimeUnit unit);
CompletableFuture<T> submitAfter(long delay, TimeUnit unit, ScheduledExecutorService executor);
/** Execute after delay, blocking */
T completeAfter(long delay, TimeUnit unit);
/** Transform result with function */
<U> RestAction<U> map(Function<? super T, ? extends U> mapper);
/** Chain with another RestAction */
<U> RestAction<U> flatMap(Function<? super T, ? extends RestAction<U>> flatMapper);
/** Handle errors and provide fallback */
RestAction<T> onErrorMap(Function<? super Throwable, ? extends T> mapper);
RestAction<T> onErrorMap(Predicate<? super Throwable> condition, Function<? super Throwable, ? extends T> mapper);
/** Chain error handling with RestAction */
RestAction<T> onErrorFlatMap(Function<? super Throwable, ? extends RestAction<T>> flatMapper);
RestAction<T> onErrorFlatMap(Predicate<? super Throwable> condition, Function<? super Throwable, ? extends RestAction<T>> flatMapper);
/** Set request timeout */
RestAction<T> timeout(long timeout, TimeUnit unit);
/** Set deadline for request */
RestAction<T> deadline(long timestamp);
/** Add check before execution */
RestAction<T> setCheck(BooleanSupplier checks);
/** Get the check supplier */
BooleanSupplier getCheck();
/** Check if this RestAction is completed */
boolean isPassContext();
/** Set context passing */
RestAction<T> setPassContext(boolean passContext);
/** Get JDA instance */
JDA getJDA();
}Usage Examples:
import net.dv8tion.jda.api.requests.RestAction;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
// Basic asynchronous execution
channel.sendMessage("Hello!").queue();
// With success callback
channel.sendMessage("Hello!").queue(
message -> System.out.println("Message sent: " + message.getId())
);
// With success and error callbacks
channel.sendMessage("Hello!").queue(
message -> System.out.println("Success: " + message.getId()),
error -> System.err.println("Failed to send: " + error.getMessage())
);
// Using CompletableFuture
CompletableFuture<Message> future = channel.sendMessage("Hello!").submit();
future.thenAccept(message -> System.out.println("Message sent: " + message.getId()))
.exceptionally(error -> {
System.err.println("Error: " + error.getMessage());
return null;
});
// Synchronous execution (blocking)
try {
Message message = channel.sendMessage("Hello!").complete();
System.out.println("Message sent synchronously: " + message.getId());
} catch (Exception e) {
System.err.println("Failed to send message: " + e.getMessage());
}
// Delayed execution
channel.sendMessage("This will be sent in 5 seconds")
.queueAfter(5, TimeUnit.SECONDS);
// With timeout
channel.sendMessage("Hello!")
.timeout(10, TimeUnit.SECONDS)
.queue(
success -> System.out.println("Sent within timeout"),
error -> System.err.println("Failed or timed out: " + error.getMessage())
);Powerful chaining capabilities for sequential and dependent operations.
/**
* Chain RestActions together for complex operations.
*/
// Example chaining methods (part of RestAction interface)
/** Transform the result */
<U> RestAction<U> map(Function<? super T, ? extends U> mapper);
/** Chain with another RestAction */
<U> RestAction<U> flatMap(Function<? super T, ? extends RestAction<U>> flatMapper);
/** Handle errors with transformation */
RestAction<T> onErrorMap(Function<? super Throwable, ? extends T> mapper);
/** Handle errors with RestAction chaining */
RestAction<T> onErrorFlatMap(Function<? super Throwable, ? extends RestAction<T>> flatMapper);Usage Examples:
// Transform message content
channel.retrieveMessageById("123456789")
.map(Message::getContentRaw)
.queue(content -> System.out.println("Message content: " + content));
// Chain operations: send message, then pin it
channel.sendMessage("Important announcement!")
.flatMap(Message::pin)
.queue(
success -> System.out.println("Message sent and pinned"),
error -> System.err.println("Failed: " + error.getMessage())
);
// Complex chaining: create role, assign to user, send confirmation
guild.createRole()
.setName("New Member")
.setColor(Color.GREEN)
.flatMap(role -> guild.addRoleToMember(member, role)
.map(success -> role))
.flatMap(role -> channel.sendMessage("Welcome! You've been given the " + role.getAsMention() + " role."))
.queue(
message -> System.out.println("Welcome process completed"),
error -> System.err.println("Welcome process failed: " + error.getMessage())
);
// Error handling with fallback
guild.retrieveMemberById(userId)
.onErrorFlatMap(error -> {
if (error instanceof ErrorResponseException) {
ErrorResponseException ere = (ErrorResponseException) error;
if (ere.getErrorCode() == 10007) { // Unknown Member
return jda.retrieveUserById(userId)
.map(user -> null); // Return null for non-members
}
}
return RestAction.forResult(null);
})
.queue(member -> {
if (member != null) {
System.out.println("Member found: " + member.getEffectiveName());
} else {
System.out.println("User exists but is not a member");
}
});
// Transform with error handling
channel.retrieveMessageById(messageId)
.map(message -> message.getContentRaw().length())
.onErrorMap(error -> -1) // Return -1 if message not found
.queue(length -> {
if (length == -1) {
System.out.println("Message not found");
} else {
System.out.println("Message length: " + length);
}
});RestActions that create entries in the guild's audit log with reason tracking.
/**
* Extension of RestAction for actions that appear in audit log.
* @param <T> The return type of this RestAction
*/
interface AuditableRestAction<T> extends RestAction<T> {
/** Set audit log reason */
AuditableRestAction<T> reason(String reason);
/** Get current audit log reason */
String getReason();
}Usage Examples:
// Ban with audit log reason
guild.ban(user, 7, TimeUnit.DAYS)
.reason("Spamming in chat")
.queue(
success -> System.out.println("User banned with reason logged"),
error -> System.err.println("Ban failed: " + error.getMessage())
);
// Kick with reason
guild.kick(member)
.reason("Violation of community guidelines")
.queue();
// Role modification with reason
guild.createRole()
.setName("Moderator")
.setPermissions(Permission.KICK_MEMBERS, Permission.MESSAGE_MANAGE)
.reason("Creating moderator role for new staff")
.queue();
// Channel creation with reason
guild.createTextChannel("announcements")
.setTopic("Important server announcements")
.reason("Creating dedicated announcement channel")
.queue();
// Member timeout with reason
member.timeoutFor(1, TimeUnit.HOURS)
.reason("Inappropriate behavior in voice chat")
.queue(
success -> channel.sendMessage("Member has been timed out for 1 hour").queue(),
error -> channel.sendMessage("Failed to timeout member: " + error.getMessage()).queue()
);Specific RestAction implementations for different Discord API operations.
/**
* RestAction for creating messages with additional message-specific options.
*/
interface MessageCreateAction extends RestAction<Message> {
MessageCreateAction setContent(String content);
MessageCreateAction setEmbeds(MessageEmbed... embeds);
MessageCreateAction setFiles(FileUpload... files);
MessageCreateAction setComponents(ActionRow... components);
MessageCreateAction setTTS(boolean tts);
MessageCreateAction setSuppressEmbeds(boolean suppress);
MessageCreateAction mentionUsers(String... userIds);
MessageCreateAction mentionRoles(String... roleIds);
}
/**
* RestAction for editing messages.
*/
interface MessageEditAction extends RestAction<Message> {
MessageEditAction setContent(String content);
MessageEditAction setEmbeds(MessageEmbed... embeds);
MessageEditAction setComponents(ActionRow... components);
MessageEditAction setAttachments(AttachedFile... attachments);
MessageEditAction setSuppressEmbeds(boolean suppress);
}
/**
* RestAction for creating channels.
*/
interface ChannelAction<T extends GuildChannel> extends AuditableRestAction<T> {
ChannelAction<T> setName(String name);
ChannelAction<T> setTopic(String topic);
ChannelAction<T> setNSFW(boolean nsfw);
ChannelAction<T> setPosition(Integer position);
ChannelAction<T> setParent(Category parent);
ChannelAction<T> setSlowmode(int slowmode);
ChannelAction<T> setBitrate(Integer bitrate);
ChannelAction<T> setUserlimit(Integer userlimit);
ChannelAction<T> addPermissionOverride(IPermissionHolder permHolder, long allow, long deny);
ChannelAction<T> addMemberPermissionOverride(long memberId, long allow, long deny);
ChannelAction<T> addRolePermissionOverride(long roleId, long allow, long deny);
}
/**
* RestAction for creating roles.
*/
interface RoleAction extends AuditableRestAction<Role> {
RoleAction setName(String name);
RoleAction setHoisted(Boolean hoisted);
RoleAction setMentionable(Boolean mentionable);
RoleAction setColor(Color color);
RoleAction setColor(int rgb);
RoleAction setPermissions(Permission... permissions);
RoleAction setPermissions(Collection<Permission> permissions);
RoleAction setPermissions(long permissions);
RoleAction setIcon(Icon icon);
RoleAction setEmoji(Emoji emoji);
}
/**
* RestAction for creating invites.
*/
interface InviteAction extends RestAction<Invite> {
InviteAction setMaxAge(Integer maxAge);
InviteAction setMaxUses(Integer maxUses);
InviteAction setTemporary(Boolean temporary);
InviteAction setUnique(Boolean unique);
InviteAction setTargetUser(User targetUser);
InviteAction setTargetApplication(long applicationId);
InviteAction setTargetType(Invite.TargetType targetType);
}Usage Examples:
// Complex channel creation
guild.createTextChannel("general-chat")
.setTopic("General discussion channel")
.setNSFW(false)
.setSlowmode(5) // 5 second slowmode
.addMemberPermissionOverride(member.getIdLong(),
Permission.VIEW_CHANNEL.getRawValue(), 0)
.addRolePermissionOverride(mutedRole.getIdLong(),
0, Permission.SEND_MESSAGES.getRawValue())
.reason("Creating general chat channel")
.queue(channel -> {
System.out.println("Created channel: " + channel.getName());
// Send welcome message to new channel
channel.sendMessage("Welcome to the general chat!")
.queue();
});
// Complex role creation with permissions
guild.createRole()
.setName("Helper")
.setColor(Color.CYAN)
.setHoisted(true)
.setMentionable(true)
.setPermissions(
Permission.MESSAGE_MANAGE,
Permission.KICK_MEMBERS,
Permission.MANAGE_ROLES
)
.reason("Creating helper role for trusted members")
.flatMap(role -> {
// Assign role to specific member
return guild.addRoleToMember(member, role)
.reason("Promoting member to helper")
.map(success -> role);
})
.queue(role -> {
channel.sendMessage("Created " + role.getAsMention() + " role and assigned to " + member.getAsMention())
.queue();
});
// Invite creation with specific settings
channel.createInvite()
.setMaxAge(3600) // 1 hour
.setMaxUses(10)
.setTemporary(false)
.setUnique(true)
.queue(invite -> {
System.out.println("Created invite: " + invite.getUrl());
// Send invite in DM
member.getUser().openPrivateChannel()
.flatMap(dm -> dm.sendMessage("Here's your server invite: " + invite.getUrl()))
.queue();
});Utility methods and classes for advanced RestAction manipulation.
/**
* Utility methods for RestAction operations.
*/
class RestAction {
/** Create RestAction that immediately completes with value */
static <E> RestAction<E> forResult(E result);
/** Combine multiple RestActions */
static RestAction<List<Object>> allOf(RestAction<?>... actions);
static RestAction<List<Object>> allOf(Collection<? extends RestAction<?>> actions);
/** Execute first successful RestAction */
static <T> RestAction<T> firstOf(RestAction<T>... actions);
static <T> RestAction<T> firstOf(Collection<? extends RestAction<T>> actions);
/** Set default failure handler */
static void setDefaultFailure(Consumer<? super Throwable> callback);
/** Set default success handler */
static void setDefaultSuccess(Consumer<Object> callback);
/** Get default timeout */
static long getDefaultTimeout(TimeUnit unit);
/** Set default timeout */
static void setDefaultTimeout(long timeout, TimeUnit unit);
}Usage Examples:
// Combine multiple operations
List<RestAction<Message>> messageActions = Arrays.asList(
channel1.sendMessage("Hello from channel 1"),
channel2.sendMessage("Hello from channel 2"),
channel3.sendMessage("Hello from channel 3")
);
RestAction.allOf(messageActions)
.queue(results -> System.out.println("All messages sent successfully"));
// First successful operation
List<RestAction<User>> userRetrieves = Arrays.asList(
jda.retrieveUserById("123456789"),
jda.retrieveUserById("987654321"),
jda.retrieveUserById("555666777")
);
RestAction.firstOf(userRetrieves)
.queue(user -> System.out.println("Found user: " + user.getName()));
// Immediate result
RestAction<String> immediateResult = RestAction.forResult("Cached value");
immediateResult.queue(value -> System.out.println("Got: " + value));
// Set global defaults
RestAction.setDefaultTimeout(30, TimeUnit.SECONDS);
RestAction.setDefaultFailure(error ->
System.err.println("Default error handler: " + error.getMessage()));
// Complex batch operation
List<Member> membersToTimeout = getViolatingMembers();
List<RestAction<Void>> timeoutActions = membersToTimeout.stream()
.map(member -> member.timeoutFor(10, TimeUnit.MINUTES)
.reason("Automated moderation action"))
.collect(Collectors.toList());
RestAction.allOf(timeoutActions)
.queue(
success -> channel.sendMessage("Timed out " + membersToTimeout.size() + " members").queue(),
error -> channel.sendMessage("Failed to timeout some members: " + error.getMessage()).queue()
);// Error response exception for API errors
class ErrorResponseException extends RuntimeException {
/** Get Discord error code */
int getErrorCode();
/** Get error response */
ErrorResponse getErrorResponse();
/** Get meaning of error */
String getMeaning();
/** Check if error is server error */
boolean isServerError();
}
// Common error responses
enum ErrorResponse {
GENERAL_ERROR(0, "General error"),
UNKNOWN_ACCOUNT(10001, "Unknown account"),
UNKNOWN_APPLICATION(10002, "Unknown application"),
UNKNOWN_CHANNEL(10003, "Unknown channel"),
UNKNOWN_GUILD(10004, "Unknown guild"),
UNKNOWN_INTEGRATION(10005, "Unknown integration"),
UNKNOWN_INVITE(10006, "Unknown invite"),
UNKNOWN_MEMBER(10007, "Unknown member"),
UNKNOWN_MESSAGE(10008, "Unknown message"),
UNKNOWN_OVERWRITE(10009, "Unknown permission overwrite"),
UNKNOWN_PROVIDER(10010, "Unknown provider"),
UNKNOWN_ROLE(10011, "Unknown role"),
UNKNOWN_TOKEN(10012, "Unknown token"),
UNKNOWN_USER(10013, "Unknown user"),
UNKNOWN_EMOJI(10014, "Unknown emoji"),
UNKNOWN_WEBHOOK(10015, "Unknown webhook"),
UNKNOWN_BAN(10026, "Unknown ban"),
UNKNOWN_INTERACTION(10062, "Unknown interaction"),
BOT_PROHIBITED_ENDPOINT(20001, "Bots cannot use this endpoint"),
BOT_ONLY_ENDPOINT(20002, "Only bots can use this endpoint"),
ANNOUNCEMENT_EDIT_LIMIT_EXCEEDED(20022, "Announcement rate limit exceeded"),
CHANNEL_HIT_WRITE_RATELIMIT(20028, "Channel rate limit exceeded"),
MAXIMUM_GUILDS(30001, "Maximum number of guilds reached"),
MAXIMUM_FRIENDS(30002, "Maximum number of friends reached"),
MAXIMUM_PINS(30003, "Maximum number of pins reached for the channel"),
MAXIMUM_ROLES(30005, "Maximum number of guild roles reached"),
MAXIMUM_WEBHOOKS(30007, "Maximum number of webhooks reached"),
MAXIMUM_EMOJIS(30008, "Maximum number of emojis reached"),
MAXIMUM_REACTIONS(30010, "Maximum number of reactions reached"),
MAXIMUM_CHANNELS(30013, "Maximum number of guild channels reached"),
MAXIMUM_ATTACHMENTS(30015, "Maximum number of attachments in a message reached"),
MAXIMUM_INVITES(30016, "Maximum number of invites reached"),
GUILD_ALREADY_HAS_TEMPLATE(30031, "Guild already has a template"),
UNAUTHORIZED(40001, "Unauthorized"),
ACCOUNT_VERIFICATION_REQUIRED(40002, "Account verification required"),
REQUEST_ENTITY_TOO_LARGE(40005, "Request entity too large"),
FEATURE_TEMPORARILY_DISABLED(40006, "Feature temporarily disabled server-side"),
USER_BANNED(40007, "User banned from this guild"),
MISSING_ACCESS(50001, "Missing access"),
INVALID_ACCOUNT_TYPE(50002, "Invalid account type"),
CANNOT_EXECUTE_ON_DM(50003, "Cannot execute action on a DM channel"),
GUILD_WIDGET_DISABLED(50004, "Guild widget disabled"),
CANNOT_EDIT_MESSAGE_BY_OTHER(50005, "Cannot edit a message authored by another user"),
CANNOT_SEND_EMPTY_MESSAGE(50006, "Cannot send an empty message"),
CANNOT_MESSAGE_USER(50007, "Cannot send messages to this user"),
CANNOT_SEND_IN_VOICE_CHANNEL(50008, "Cannot send messages in a voice channel"),
CHANNEL_VERIFICATION_TOO_HIGH(50009, "Channel verification level is too high for you to gain access"),
OAUTH2_APPLICATION_BOT_ABSENT(50010, "OAuth2 application does not have a bot"),
OAUTH2_APPLICATION_LIMIT_REACHED(50011, "OAuth2 application limit reached"),
INVALID_OAUTH_STATE(50012, "Invalid OAuth2 state"),
MISSING_PERMISSIONS(50013, "Missing permissions"),
INVALID_AUTHENTICATION_TOKEN(50014, "Invalid authentication token"),
NOTE_TOO_LONG(50015, "Note was too long"),
INVALID_BULK_DELETE_QUANTITY(50016, "Invalid bulk delete quantity"),
CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL(50019, "Cannot pin a message in a different channel"),
INVALID_OR_TAKEN_INVITE_CODE(50020, "Invalid or taken invite code"),
CANNOT_EXECUTE_ON_SYSTEM_MESSAGE(50021, "Cannot execute action on a system message"),
CANNOT_EXECUTE_ON_CHANNEL_TYPE(50024, "Cannot execute action on this channel type"),
INVALID_OAUTH_TOKEN(50025, "Invalid OAuth2 access token"),
MISSING_OAUTH_SCOPE(50026, "Missing required OAuth2 scope"),
INVALID_WEBHOOK_TOKEN(50027, "Invalid webhook token"),
INVALID_ROLE(50028, "Invalid role"),
INVALID_RECIPIENTS(50033, "Invalid Recipient(s)"),
BULK_DELETE_MESSAGE_TOO_OLD(50034, "Bulk delete messages too old"),
INVALID_FORM_BODY(50035, "Invalid form body"),
INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT(50036, "Invite accepted to guild not containing bot"),
INVALID_API_VERSION(50041, "Invalid API version"),
FILE_UPLOADED_EXCEEDS_MAXIMUM_SIZE(50045, "File uploaded exceeds maximum size"),
INVALID_FILE_UPLOADED(50046, "Invalid file uploaded"),
REACTION_BLOCKED(90001, "Reaction was blocked"),
API_RESOURCE_OVERLOADED(130000, "API resource is currently overloaded");
/** Get numeric error code */
int getCode();
/** Get error meaning */
String getMeaning();
}
// Rate limit exception
class RateLimitedException extends RuntimeException {
/** Get retry after time in milliseconds */
long getRetryAfter();
/** Get rate limit bucket */
String getScope();
/** Check if rate limit is global */
boolean isGlobal();
}Install with Tessl CLI
npx tessl i tessl/maven-net-dv8tion--jda