Java Discord API - A comprehensive Java library for building Discord bots and applications
—
Multi-shard bot support for scaling beyond Discord's gateway limits with centralized management and cross-shard operations for large-scale Discord applications.
Centralized management interface for multiple JDA shards with unified operations and monitoring.
/**
* The ShardManager is a higher level abstraction for managing multiple shards.
* It provides a unified interface for operating across multiple JDA instances.
*/
interface ShardManager {
/** Get all JDA shards */
List<JDA> getShards();
/** Get JDA shards with specific status */
List<JDA> getShards(JDA.Status status);
/** Get specific shard by ID */
JDA getShardById(int shardId);
/** Get shard cache view */
ShardCacheView getShardCache();
/** Get shard that contains specific guild */
JDA getShardFor(long guildId);
JDA getShardFor(String guildId);
/** Start specific shard */
void start(int shardId);
/** Restart specific shard */
void restart(int shardId);
/** Shutdown specific shard */
void shutdown(int shardId);
/** Shutdown all shards */
void shutdown();
/** Get average gateway ping across all shards */
double getAverageGatewayPing();
/** Get average shard status */
JDA.Status getStatus();
/** Set status across all shards */
void setStatus(OnlineStatus status);
/** Set activity across all shards */
void setActivity(Activity activity);
/** Set idle status across all shards */
void setIdle(boolean idle);
/** Set presence across all shards */
void setPresence(OnlineStatus status, Activity activity, boolean idle);
/** Get unified guild cache across all shards */
SnowflakeCacheView<Guild> getGuildCache();
/** Get guild by ID from any shard */
Guild getGuildById(long guildId);
Guild getGuildById(String guildId);
/** Get guilds by name from all shards */
List<Guild> getGuildsByName(String name, boolean ignoreCase);
/** Get mutual guilds for user across all shards */
List<Guild> getMutualGuilds(User user);
/** Get unified user cache across all shards */
SnowflakeCacheView<User> getUserCache();
/** Get user by ID from any shard */
User getUserById(long userId);
User getUserById(String userId);
/** Get users by name from all shards */
List<User> getUsersByName(String name, boolean ignoreCase);
/** Get total guild count across all shards */
long getGuildTotal();
/** Get total user count across all shards */
long getUserTotal();
/** Add event listeners to all shards */
void addEventListener(Object... listeners);
/** Remove event listeners from all shards */
void removeEventListener(Object... listeners);
/** Get registered event listeners */
List<Object> getRegisteredListeners();
/** Retrieve user from any shard */
RestAction<User> retrieveUserById(long userId);
RestAction<User> retrieveUserById(String userId);
/** Get statistics for all shards */
ShardStatistics getShardStatistics();
}Usage Examples:
import net.dv8tion.jda.api.sharding.ShardManager;
import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder;
// Create shard manager
ShardManager shardManager = DefaultShardManagerBuilder.createDefault("BOT_TOKEN")
.setShardsTotal(4)
.addEventListeners(new MyEventListener())
.setStatus(OnlineStatus.ONLINE)
.setActivity(Activity.playing("on multiple shards"))
.build();
// Wait for all shards to be ready
List<JDA> shards = shardManager.getShards();
for (JDA shard : shards) {
try {
shard.awaitReady();
System.out.println("Shard " + shard.getShardInfo().getShardId() + " is ready");
} catch (InterruptedException e) {
System.err.println("Interrupted while waiting for shard to be ready");
}
}
// Get statistics
long totalGuilds = shardManager.getGuildTotal();
long totalUsers = shardManager.getUserTotal();
double avgPing = shardManager.getAverageGatewayPing();
System.out.println("Total guilds: " + totalGuilds);
System.out.println("Total users: " + totalUsers);
System.out.println("Average ping: " + avgPing + "ms");
// Find guild across all shards
Guild targetGuild = shardManager.getGuildById("123456789");
if (targetGuild != null) {
JDA shard = shardManager.getShardFor(targetGuild.getIdLong());
System.out.println("Guild found on shard " + shard.getShardInfo().getShardId());
}
// Set presence across all shards
shardManager.setPresence(OnlineStatus.DO_NOT_DISTURB,
Activity.watching(totalGuilds + " servers"), false);
// Restart specific shard
shardManager.restart(2);
// Shutdown all shards
shardManager.shutdown();Builder pattern for creating and configuring shard managers with comprehensive options.
/**
* Builder for creating ShardManager instances with various configuration options.
*/
class DefaultShardManagerBuilder {
/** Create with default settings */
static DefaultShardManagerBuilder createDefault(String token);
/** Create with minimal cache settings */
static DefaultShardManagerBuilder createLight(String token);
/** Create with custom intent settings */
static DefaultShardManagerBuilder create(String token, GatewayIntent intent, GatewayIntent... intents);
static DefaultShardManagerBuilder create(String token, Collection<GatewayIntent> intents);
/** Set bot token */
DefaultShardManagerBuilder setToken(String token);
/** Set total number of shards */
DefaultShardManagerBuilder setShardsTotal(int shardsTotal);
/** Set specific shard range to manage */
DefaultShardManagerBuilder setShards(int minShardId, int maxShardId);
DefaultShardManagerBuilder setShards(int... shardIds);
/** Enable specific gateway intents */
DefaultShardManagerBuilder enableIntents(GatewayIntent... intents);
DefaultShardManagerBuilder enableIntents(Collection<GatewayIntent> intents);
/** Disable specific gateway intents */
DefaultShardManagerBuilder disableIntents(GatewayIntent... intents);
DefaultShardManagerBuilder disableIntents(Collection<GatewayIntent> intents);
/** Set enabled intents */
DefaultShardManagerBuilder setEnabledIntents(Collection<GatewayIntent> intents);
/** Enable cache flags */
DefaultShardManagerBuilder enableCache(CacheFlag... flags);
DefaultShardManagerBuilder enableCache(Collection<CacheFlag> flags);
/** Disable cache flags */
DefaultShardManagerBuilder disableCache(CacheFlag... flags);
DefaultShardManagerBuilder disableCache(Collection<CacheFlag> flags);
/** Set member cache policy */
DefaultShardManagerBuilder setMemberCachePolicy(MemberCachePolicy policy);
/** Set chunking filter */
DefaultShardManagerBuilder setChunkingFilter(ChunkingFilter filter);
/** Set event manager */
DefaultShardManagerBuilder setEventManagerProvider(IntFunction<IEventManager> eventManagerProvider);
/** Add event listeners to all shards */
DefaultShardManagerBuilder addEventListeners(Object... listeners);
DefaultShardManagerBuilder addEventListenerProviders(IntFunction<Object>... providers);
/** Remove event listeners */
DefaultShardManagerBuilder removeEventListeners(Object... listeners);
DefaultShardManagerBuilder removeEventListenerProviders(IntFunction<Object>... providers);
/** Set initial status */
DefaultShardManagerBuilder setStatus(OnlineStatus status);
/** Set initial activity */
DefaultShardManagerBuilder setActivity(Activity activity);
/** Set idle status */
DefaultShardManagerBuilder setIdle(boolean idle);
/** Set HTTP client builder */
DefaultShardManagerBuilder setHttpClientBuilder(OkHttpClient.Builder builder);
/** Set HTTP client */
DefaultShardManagerBuilder setHttpClient(OkHttpClient client);
/** Set WebSocket factory */
DefaultShardManagerBuilder setWebsocketFactory(WebSocketFactory factory);
/** Set rate limit scheduler */
DefaultShardManagerBuilder setRateLimitScheduler(ScheduledExecutorService scheduler, boolean shutdown);
/** Set callback pool */
DefaultShardManagerBuilder setCallbackPool(ExecutorService executor, boolean shutdown);
/** Set event pool */
DefaultShardManagerBuilder setEventPool(ExecutorService executor, boolean shutdown);
/** Set audio pool */
DefaultShardManagerBuilder setAudioPool(ScheduledExecutorService executor, boolean shutdown);
/** Set session controller */
DefaultShardManagerBuilder setSessionController(SessionController controller);
/** Set voice dispatch interceptor */
DefaultShardManagerBuilder setVoiceDispatchInterceptor(VoiceDispatchInterceptor interceptor);
/** Set shard listeners */
DefaultShardManagerBuilder setShardListenerProvider(IntFunction<ShardListener> provider);
/** Set thread factory */
DefaultShardManagerBuilder setThreadFactory(ThreadFactory threadFactory);
/** Set compression type */
DefaultShardManagerBuilder setCompression(Compression compression);
/** Set gateway encoding */
DefaultShardManagerBuilder setEncoding(GatewayEncoding encoding);
/** Set large threshold */
DefaultShardManagerBuilder setLargeThreshold(int threshold);
/** Set maximum reconnect delay */
DefaultShardManagerBuilder setMaxReconnectDelay(int maxReconnectDelay);
/** Set maximum buffer size */
DefaultShardManagerBuilder setMaxBufferSize(int bufferSize);
/** Enable/disable config flags */
DefaultShardManagerBuilder setFlag(ConfigFlag flag, boolean enable);
/** Set context map */
DefaultShardManagerBuilder setContextMap(ConcurrentMap<String, String> map);
/** Build the shard manager */
ShardManager build() throws LoginException;
}Usage Examples:
// Basic shard manager setup
ShardManager basicShardManager = DefaultShardManagerBuilder.createDefault("BOT_TOKEN")
.setShardsTotal(2)
.build();
// Advanced shard manager configuration
ShardManager advancedShardManager = DefaultShardManagerBuilder.createDefault("BOT_TOKEN")
.setShardsTotal(8)
.setShards(0, 3) // Only manage shards 0-3
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES)
.setMemberCachePolicy(MemberCachePolicy.VOICE.or(MemberCachePolicy.OWNER))
.setChunkingFilter(ChunkingFilter.NONE)
.disableCache(CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS)
.addEventListeners(new MyShardedEventListener())
.setStatus(OnlineStatus.ONLINE)
.setActivity(Activity.playing("across " + 8 + " shards"))
.setLargeThreshold(250)
.build();
// Custom shard configuration per shard
ShardManager customShardManager = DefaultShardManagerBuilder.createDefault("BOT_TOKEN")
.setShardsTotal(4)
.addEventListenerProviders(shardId -> {
// Different listeners per shard
if (shardId == 0) {
return new PrimaryShardListener();
} else {
return new SecondaryShardListener();
}
})
.setEventManagerProvider(shardId -> {
// Custom event manager per shard
return new CustomEventManager("Shard-" + shardId);
})
.build();
// Light shard manager for memory efficiency
ShardManager lightShardManager = DefaultShardManagerBuilder.createLight("BOT_TOKEN",
EnumSet.of(GatewayIntent.GUILD_MESSAGES, GatewayIntent.MESSAGE_CONTENT))
.setShardsTotal(4)
.setMemberCachePolicy(MemberCachePolicy.NONE)
.disableCache(CacheFlag.VOICE_STATE, CacheFlag.EMOJI, CacheFlag.STICKER)
.build();Comprehensive monitoring and statistics for shard performance and health.
/**
* Interface for monitoring individual shard events and lifecycle.
*/
interface ShardListener {
/** Called when shard comes online */
void onShardReady(int shardId);
/** Called when shard disconnects */
void onShardDisconnect(int shardId, CloseCode closeCode, OffsetDateTime disconnectTime, boolean resumable);
/** Called when shard resumes connection */
void onShardResume(int shardId);
/** Called when shard reconnects */
void onShardReconnect(int shardId);
/** Called when shard shuts down */
void onShardShutdown(int shardId, OffsetDateTime shutdownTime);
}
/**
* Cache view for accessing shards.
*/
interface ShardCacheView extends CacheView<JDA> {
/** Get shard by ID */
JDA getElementById(int shardId);
/** Get all shards with specific status */
List<JDA> getElementsByStatus(JDA.Status status);
/** Get shard statistics */
default ShardStatistics getStatistics() {
return new ShardStatistics(asList());
}
}
/**
* Statistics and metrics for shard performance.
*/
class ShardStatistics {
/** Get total number of shards */
int getTotalShards();
/** Get number of shards with specific status */
int getShardCount(JDA.Status status);
/** Get number of connected shards */
int getConnectedShards();
/** Get average gateway ping across all shards */
double getAverageGatewayPing();
/** Get minimum gateway ping */
long getMinGatewayPing();
/** Get maximum gateway ping */
long getMaxGatewayPing();
/** Get total guild count across all shards */
long getTotalGuilds();
/** Get total user count across all shards */
long getTotalUsers();
/** Get total cached entities count */
long getTotalCachedEntities();
/** Get memory usage per shard */
Map<Integer, Long> getMemoryUsagePerShard();
/** Get uptime per shard */
Map<Integer, Duration> getUptimePerShard();
/** Get guild distribution per shard */
Map<Integer, Integer> getGuildDistribution();
/** Get shard load balancing metrics */
ShardLoadMetrics getLoadMetrics();
}Usage Examples:
// Custom shard listener for monitoring
public class ShardMonitor implements ShardListener {
private final TextChannel logChannel;
private final Map<Integer, OffsetDateTime> shardStartTimes = new HashMap<>();
public ShardMonitor(TextChannel logChannel) {
this.logChannel = logChannel;
}
@Override
public void onShardReady(int shardId) {
shardStartTimes.put(shardId, OffsetDateTime.now());
logChannel.sendMessage("✅ Shard " + shardId + " is ready").queue();
// Log shard info
JDA shard = shardManager.getShardById(shardId);
if (shard != null) {
String info = String.format("Shard %d: %d guilds, %d users, %dms ping",
shardId,
shard.getGuildCache().size(),
shard.getUserCache().size(),
shard.getGatewayPing());
logChannel.sendMessage(info).queue();
}
}
@Override
public void onShardDisconnect(int shardId, CloseCode closeCode, OffsetDateTime disconnectTime, boolean resumable) {
String status = resumable ? "⚠️ Disconnected (resumable)" : "❌ Disconnected (non-resumable)";
String message = String.format("%s Shard %d: %s (%d)",
status, shardId, closeCode.getMeaning(), closeCode.getCode());
logChannel.sendMessage(message).queue();
}
@Override
public void onShardResume(int shardId) {
logChannel.sendMessage("🔄 Shard " + shardId + " resumed").queue();
}
@Override
public void onShardReconnect(int shardId) {
logChannel.sendMessage("🔄 Shard " + shardId + " reconnected").queue();
}
@Override
public void onShardShutdown(int shardId, OffsetDateTime shutdownTime) {
Duration uptime = Duration.between(shardStartTimes.get(shardId), shutdownTime);
String message = String.format("🛑 Shard %d shutdown after %d minutes uptime",
shardId, uptime.toMinutes());
logChannel.sendMessage(message).queue();
shardStartTimes.remove(shardId);
}
}
// Set up monitoring
ShardMonitor monitor = new ShardMonitor(logChannel);
ShardManager shardManager = DefaultShardManagerBuilder.createDefault("BOT_TOKEN")
.setShardsTotal(4)
.setShardListenerProvider(shardId -> monitor)
.build();
// Regular statistics reporting
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
ShardStatistics stats = shardManager.getShardStatistics();
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Shard Statistics")
.addField("Total Shards", String.valueOf(stats.getTotalShards()), true)
.addField("Connected", String.valueOf(stats.getConnectedShards()), true)
.addField("Average Ping", String.format("%.1fms", stats.getAverageGatewayPing()), true)
.addField("Total Guilds", String.valueOf(stats.getTotalGuilds()), true)
.addField("Total Users", String.valueOf(stats.getTotalUsers()), true)
.addField("Memory Usage", formatMemoryUsage(stats.getMemoryUsagePerShard()), false)
.setTimestamp(Instant.now())
.setColor(Color.BLUE);
logChannel.sendMessageEmbeds(embed.build()).queue();
}, 0, 1, TimeUnit.HOURS);
// Helper method for memory formatting
private String formatMemoryUsage(Map<Integer, Long> memoryUsage) {
return memoryUsage.entrySet().stream()
.map(entry -> String.format("Shard %d: %dMB", entry.getKey(), entry.getValue() / 1024 / 1024))
.collect(Collectors.joining("\n"));
}Utility methods and patterns for operations that span multiple shards.
/**
* Utility class for cross-shard operations.
*/
class ShardUtils {
/** Execute operation on all shards */
static <T> List<CompletableFuture<T>> executeOnAllShards(
ShardManager shardManager,
Function<JDA, CompletableFuture<T>> operation);
/** Execute operation on specific shards */
static <T> List<CompletableFuture<T>> executeOnShards(
ShardManager shardManager,
Collection<Integer> shardIds,
Function<JDA, CompletableFuture<T>> operation);
/** Find optimal shard for operation */
static JDA findOptimalShard(ShardManager shardManager, Predicate<JDA> criteria);
/** Get total count across all shards */
static long getTotalCount(ShardManager shardManager, Function<JDA, Long> counter);
/** Distribute load across shards */
static <T> Map<Integer, List<T>> distributeLoad(
ShardManager shardManager,
List<T> items,
Function<T, Long> hashFunction);
}Usage Examples:
// Cross-shard guild search
public CompletableFuture<List<Guild>> findGuildsByName(String name) {
List<CompletableFuture<List<Guild>>> futures = ShardUtils.executeOnAllShards(
shardManager,
shard -> CompletableFuture.supplyAsync(() ->
shard.getGuildsByName(name, true))
);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.flatMap(future -> future.join().stream())
.collect(Collectors.toList()));
}
// Cross-shard message broadcasting
public void broadcastMessage(String message) {
shardManager.getShards().parallelStream().forEach(shard -> {
shard.getGuilds().stream()
.map(Guild::getDefaultChannel)
.filter(Objects::nonNull)
.forEach(channel -> {
channel.sendMessage(message).queue(
success -> {},
error -> System.err.println("Failed to send to " + channel.getGuild().getName())
);
});
});
}
// Load balancing for processing
public void processGuildsBalanced(List<String> guildIds) {
Map<Integer, List<String>> distribution = ShardUtils.distributeLoad(
shardManager,
guildIds,
guildId -> Long.parseLong(guildId)
);
distribution.forEach((shardId, guildList) -> {
JDA shard = shardManager.getShardById(shardId);
CompletableFuture.runAsync(() -> {
guildList.forEach(guildId -> {
Guild guild = shard.getGuildById(guildId);
if (guild != null) {
processGuild(guild);
}
});
});
});
}
// Cross-shard statistics collection
public ShardHealthReport generateHealthReport() {
Map<Integer, CompletableFuture<ShardHealth>> healthFutures =
shardManager.getShards().stream()
.collect(Collectors.toMap(
shard -> shard.getShardInfo().getShardId(),
this::collectShardHealth
));
// Wait for all health checks to complete
CompletableFuture.allOf(healthFutures.values().toArray(new CompletableFuture[0])).join();
Map<Integer, ShardHealth> healthMap = healthFutures.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().join()
));
return new ShardHealthReport(healthMap);
}
private CompletableFuture<ShardHealth> collectShardHealth(JDA shard) {
return CompletableFuture.supplyAsync(() -> {
return ShardHealth.builder()
.shardId(shard.getShardInfo().getShardId())
.status(shard.getStatus())
.ping(shard.getGatewayPing())
.guildCount(shard.getGuildCache().size())
.userCount(shard.getUserCache().size())
.uptime(Duration.between(shard.getTimeCreated(), Instant.now()))
.build();
});
}// Shard information
class ShardInfo {
/** Get shard ID (0-based) */
int getShardId();
/** Get total shard count */
int getShardTotal();
/** Get string representation (id/total) */
String getShardString();
}
// Session controller for coordinating shard logins
interface SessionController {
/** Get gateway information */
CompletableFuture<SessionController.Gateway> getGateway();
/** Request session identifier */
SessionController.SessionConnectNode appendSession(SessionController.SessionConnectNode node);
/** Remove session from queue */
void removeSession(SessionController.SessionConnectNode node);
/** Get remaining session starts */
int getRemainingStarts();
/** Get session start limit reset time */
long getSessionStartLimitResetTime();
}
// Chunking filter for member loading
enum ChunkingFilter {
ALL, NONE;
/** Create filter that includes specific guilds */
static ChunkingFilter include(long... guildIds);
/** Create filter that excludes specific guilds */
static ChunkingFilter exclude(long... guildIds);
/** Filter guilds */
boolean filter(long guildId);
}
// Member cache policy
enum MemberCachePolicy {
ALL, OWNER, VOICE, ONLINE, PENDING, NONE;
/** Combine policies with OR logic */
MemberCachePolicy or(MemberCachePolicy policy);
/** Combine policies with AND logic */
MemberCachePolicy and(MemberCachePolicy policy);
/** Create policy for specific users */
static MemberCachePolicy of(Collection<User> users);
/** Check if member should be cached */
boolean cacheMember(Member member);
}
// Compression types
enum Compression {
NONE, ZLIB
}
// Gateway encoding
enum GatewayEncoding {
JSON, ETF
}
// Config flags for JDA behavior
enum ConfigFlag {
SHUTDOWN_HOOK, BULK_DELETE_SPLITTING, AUTO_RECONNECT,
BUFFER_POOL, LAZY_LOADING, MEMBER_CACHE_CHUNKING,
USE_SHUTDOWN_NOW;
/** Get default config flags */
static EnumSet<ConfigFlag> getDefault();
}Install with Tessl CLI
npx tessl i tessl/maven-net-dv8tion--jda