Rich Embeds
Embeds let you send formatted messages with titles, descriptions, colors, fields, images, and more. The EmbedBuilder provides a fluent API with built-in validation.
EmbedBuilder
Build embeds using a chainable fluent API:
import fluxer4j.embed.EmbedBuilder;
import fluxer4j.data.models.Embed;
Embed embed = new EmbedBuilder()
.withTitle("Server Status")
.withDescription("All systems are **operational**.")
.withColor(0x3fb950) // Green
.withUrl("https://status.example.com")
.withCurrentTimestamp()
.build();
Available Methods
| Method | Description |
|---|---|
withTitle(String) | Set the embed title (max 256 chars) |
withDescription(String) | Set the description (max 4096 chars) |
withUrl(String) | Make the title a hyperlink |
withColor(int) | Set the color bar (hex RGB, e.g. 0x5865F2) |
withColor(int r, int g, int b) | Set color from RGB components |
withTimestamp(Instant) | Set a specific timestamp |
withCurrentTimestamp() | Use the current time |
withThumbnailUrl(String) | Set a small image (top-right) |
withImageUrl(String) | Set a large image |
withAuthor(name, iconUrl, url) | Set the author section |
withFooter(text, iconUrl) | Set the footer section |
addField(name, value, inline) | Add a field (max 25 fields) |
build() | Build and validate the embed |
Fields
Fields can be inline (displayed side by side) or full-width:
Embed embed = new EmbedBuilder()
.withTitle("User Info")
.addField("Username", "Alice", true) // Inline
.addField("Status", "Online", true) // Inline (same row)
.addField("Bio", "Hello, I'm Alice!", false) // Full width
.build();
Images
Embed embed = new EmbedBuilder()
.withTitle("Gallery")
.withImageUrl("https://example.com/photo.png") // Large image
.withThumbnailUrl("https://example.com/thumb.png") // Small image (top-right)
.build();
http://, https://, or attachment://.Embed Limits
The builder validates these limits and throws IllegalArgumentException / IllegalStateException if exceeded:
| Property | Max Length |
|---|---|
| Title | 256 characters |
| Description | 4,096 characters |
| Fields | 25 fields |
| Total embed length | 6,000 characters (sum of title + description + author name + footer text + all field names/values) |
Sending Embeds
// Via ApiClient
Message msg = new Message();
msg.setContent("Check this out:");
msg.setEmbeds(List.of(embed));
api.sendMessage(channelId, msg).join();
// Inside a command (ModuleBase)
@CommandAttribute(name = "embed")
public CompletableFuture<Message> embedCommand() {
Embed embed = new EmbedBuilder()
.withTitle("Hello!")
.withColor(0x5865F2)
.build();
Message msg = new Message();
msg.setEmbeds(List.of(embed));
return getContext().getClient().sendMessage(getContext().getChannelId(), msg);
}
Voice Support
Fluxer4J supports joining voice channels and streaming audio via UDP or LiveKit WebRTC. The VoiceManager handles connections per guild.
webrtc-java dependency (included by default). LiveKit connections use WebRTC; UDP connections use direct Opus streaming.VoiceManager
The VoiceManager is thread-safe and manages one voice connection per guild.
| Method | Description |
|---|---|
joinAsync(VoiceChannel) | Join a voice channel (returns CompletableFuture) |
leave(long guildId) | Leave the voice channel in a guild |
leaveAsync(long guildId) | Leave asynchronously |
isConnected(long guildId) | Check if connected in a guild |
getVoiceConnection(long guildId) | Get the UDP voice connection |
getLiveKitConnection(long guildId) | Get the LiveKit voice connection |
close() | Disconnect all voice connections |
Joining & Leaving
// Get the voice manager (available after READY event)
VoiceManager voice = bot.getVoiceManager();
// Create a voice channel reference
VoiceChannel channel = new VoiceChannel(channelId, guildId);
// Join
voice.joinAsync(channel).thenAccept(conn -> {
System.out.println("Connected to voice!");
});
// Leave
voice.leave(guildId);
Audio Playback (UDP)
For UDP voice connections, stream length-prefixed Opus packets:
VoiceConnection vc = voice.getVoiceConnection(guildId);
if (vc != null) {
// Load audio from a URL (Opus format)
URL audioUrl = URI.create("https://example.com/audio.opus").toURL();
InputStream stream = audioUrl.openStream();
// Create an AudioSource and play
AudioSource source = AudioSource.fromLengthPrefixedOpusStream(stream);
vc.play(source);
// Stop playback
vc.stop();
// Check status
boolean speaking = vc.isSpeaking();
}
LiveKit (WebRTC)
For LiveKit voice connections, stream raw PCM audio:
LiveKitVoiceConnection lk = voice.getLiveKitConnection(guildId);
if (lk != null) {
// Get PCM audio stream (e.g. from ffmpeg/yt-dlp)
InputStream pcmStream = PcmFromUrl.openPcmStream(url);
lk.playPcm(pcmStream);
// Stop
lk.stop();
}
Voice Events
Listen for voice connection events with VoiceEventListener:
vc.addEventListener(new VoiceEventListener() {
@Override
public void onDisconnect(String reason) {
System.out.println("Disconnected: " + reason);
}
@Override
public void onPlaybackFinished() {
System.out.println("Playback finished!");
}
});
Full Voice Command Example
public class MusicCommands extends ModuleBase {
private VoiceManager getVoice() {
return getService(Fluxer4JBot.class).getVoiceManager();
}
@CommandAttribute(name = "join")
@SummaryAttribute("Join a voice channel by ID")
public CompletableFuture<Message> joinCommand(long channelId) {
Long guildId = getContext().getGuildId();
if (guildId == null) return replyAsync("Guild only!");
VoiceChannel channel = new VoiceChannel(channelId, guildId);
getVoice().joinAsync(channel);
return replyAsync("Joining voice channel...");
}
@CommandAttribute(name = "leave")
public CompletableFuture<Message> leaveCommand() {
Long guildId = getContext().getGuildId();
if (guildId == null) return replyAsync("Guild only!");
getVoice().leave(guildId);
return replyAsync("Left voice channel.");
}
@CommandAttribute(name = "stop")
public CompletableFuture<Message> stopCommand() {
Long guildId = getContext().getGuildId();
VoiceConnection vc = getVoice().getVoiceConnection(guildId);
if (vc != null) vc.stop();
return replyAsync("Playback stopped.");
}
}