Skip to content

Traqueur-dev/JDAFluent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

JDA Fluent

A fluent API for JDA interactions - buttons, modals, select menus, panels, and more.

Installation

Gradle (Kotlin DSL)

repositories {
    mavenCentral()
    maven("https://repo.groupez.dev/releases")
}

dependencies {
    implementation("fr.traqueur:jdafluent:1.0.0")
}

Gradle (Groovy)

repositories {
    mavenCentral()
    maven { url 'https://repo.groupez.dev/releases' }
}

dependencies {
    implementation 'fr.traqueur:jdafluent:1.0.0'
}

Maven

<repository>
    <id>groupez</id>
    <url>https://repo.groupez.dev/releases</url>
</repository>

<dependency>
    <groupId>fr.traqueur</groupId>
    <artifactId>jdafluent</artifactId>
    <version>1.0.0</version>
</dependency>

Quick Start

JDA jda = JDABuilder.createDefault("TOKEN").build();
JdaFluent fluent = new JdaFluent(jda);

Features

Buttons

// Register a permanent button handler
ButtonFactory buyButton = fluent.buttons().register("buy", (event, data) -> {
    long productId = data.getLong(0);
    int quantity = data.getInt(1);
    // Process purchase...
    event.reply("Purchased!").setEphemeral(true).queue();
});

// Create buttons with data
channel.sendMessage("Buy this item?")
    .setComponents(ActionRow.of(
        buyButton.primary("Buy x1", productId, 1),
        buyButton.primary("Buy x10", productId, 10)
    ))
    .queue();

One-Shot Buttons (auto-cleanup)

// One-shot: removed after first click
Button button = fluent.buttons().oneShot(
    ButtonStyle.PRIMARY, 
    "Click me", 
    Duration.ofMinutes(5),
    event -> event.reply("Clicked!").queue()
);

// Temporary: stays active until timeout
Button temp = fluent.buttons().temporary(
    ButtonStyle.PRIMARY,
    "โ—€",
    Duration.ofMinutes(5),
    event -> previousPage(event)
);

Button Groups (linked buttons)

// When one button is clicked, all are invalidated
OneShotGroup group = fluent.buttons().group(Duration.ofMinutes(2));

channel.sendMessage("Delete this server?")
    .setComponents(ActionRow.of(
        group.danger("Yes, delete", event -> {
            deleteServer();
            event.editMessage("Deleted.").setComponents().queue();
        }),
        group.secondary("Cancel", event -> {
            event.editMessage("Cancelled.").setComponents().queue();
        })
    ))
    .queue();

Dialogs (confirmations)

// Simple confirmation
channel.sendMessage("Are you sure?")
    .setComponents(fluent.dialogs().confirm("Yes", Duration.ofMinutes(2), event -> {
        // Handle confirmation
        event.editMessage("Confirmed!").setComponents().queue();
    }))
    .queue();

// Dangerous action with double confirmation
channel.sendMessage("Delete all data?")
    .setComponents(fluent.dialogs().dangerConfirm("Delete", Duration.ofMinutes(1), event -> {
        deleteAllData();
        event.editMessage("All data deleted.").setComponents().queue();
    }))
    .queue();

// Typed choices
channel.sendMessage("Select difficulty:")
    .setComponents(fluent.dialogs().choices(
        Duration.ofMinutes(5),
        difficulty -> startGame(difficulty),
        Choice.of("Easy", Difficulty.EASY),
        Choice.of("Normal", Difficulty.NORMAL),
        Choice.of("Hard", Difficulty.HARD)
    ))
    .queue();

Panels (permanent menus)

// Create a ticket panel
Panel<TicketCategory> ticketPanel = fluent.panels()
    .<TicketCategory>choices("ticket", ctx -> {
        createTicket(ctx.event(), ctx.value());
    })
    .codec(Enum::name, TicketCategory::valueOf)
    .choice("๐ŸŽซ", "Support", TicketCategory.SUPPORT)
    .choice("๐Ÿ›", "Bug Report", TicketCategory.BUG)
    .choice("๐Ÿ’ฐ", "Billing", TicketCategory.BILLING)
    .build();

// Send the panel (survives bot restarts)
channel.sendMessageEmbeds(embed)
    .setComponents(ticketPanel.toActionRows())
    .queue();

Select Menus

// Create a role selector
SelectMenuBuilder.BuiltSelectMenu<String> roleMenu = fluent.selects()
    .<String>create("roles", ctx -> {
        String roleId = ctx.value();
        toggleRole(ctx.member(), roleId);
        ctx.reply("Role updated!");
    })
    .codec(s -> s, s -> s)
    .placeholder("Choose your roles...")
    .multiple()
    .option("๐ŸŽฎ", "Gamer", "gamer-role-id")
    .option("๐ŸŽจ", "Artist", "artist-role-id")
    .option("๐ŸŽต", "Music", "music-role-id")
    .build();

channel.sendMessage("Select your roles:")
    .setComponents(ActionRow.of(roleMenu.toMenu()))
    .queue();

Modals

// Create a modal
ModalBuilder.BuiltModal reportModal = fluent.modals()
    .create("report", ctx -> {
        String reason = ctx.get("reason");
        String details = ctx.getOptional("details").orElse("No details");
        
        submitReport(ctx.user(), reason, details);
        ctx.reply("Report submitted!");
    })
    .title("Report User")
    .shortText("reason", "Reason", b -> b
        .required()
        .placeholder("Why are you reporting?")
        .maxLength(100))
    .paragraph("details", "Details", b -> b
        .optional()
        .placeholder("Additional information...")
        .minLength(20))
    .build();

// Show modal on button click
fluent.buttons().register("report-btn", event -> {
    reportModal.show(event);
});

Awaiters (wait for responses)

// Wait for a message
channel.sendMessage("Type the server name to confirm deletion:").queue();

fluent.awaiters()
    .message()
    .from(user)
    .inChannel(channel)
    .timeout(Duration.ofMinutes(1))
    .filter(msg -> !msg.getContentRaw().isBlank())
    .onResponse(msg -> {
        if (msg.getContentRaw().equals("MyServer")) {
            deleteServer();
            channel.sendMessage("Server deleted.").queue();
        } else {
            channel.sendMessage("Incorrect name, cancelled.").queue();
        }
    })
    .onTimeout(() -> channel.sendMessage("Timeout, cancelled.").queue())
    .start();

// Wait for a button click
fluent.awaiters()
    .button()
    .from(user)
    .onMessage(message)
    .timeout(Duration.ofSeconds(30))
    .disableAllOnClick()
    .onResponse(event -> {
        event.reply("You clicked: " + event.getComponentId()).queue();
    })
    .onTimeout(() -> message.editMessage("Expired").setComponents().queue())
    .start();

// Wait for a select menu
fluent.awaiters()
    .select()
    .from(user)
    .onMessage(message)
    .timeout(Duration.ofMinutes(2))
    .onResponse(event -> {
        String selected = event.getValues().get(0);
        event.reply("You selected: " + selected).queue();
    })
    .start();

Fluent Embeds

// Simple embed
FluentEmbed.create()
    .title("Welcome!")
    .description("Thanks for joining our server.")
    .colorSuccess()
    .thumbnail(user.getAvatarUrl())
    .field("Member #", memberCount)
    .fieldInline("Joined", "Today")
    .fieldInline("Role", "Member")
    .footer("Enjoy your stay!")
    .timestampNow()
    .send(channel)
    .queue();

// Using templates
EmbedTemplate errorTemplate = EmbedTemplate.builder()
    .color(0xED4245)
    .footer("Error occurred")
    .timestampNow()
    .build();

errorTemplate.withTitle("Oops!")
    .description("Something went wrong: " + errorMessage)
    .send(channel)
    .queue();

// Preset templates
EmbedTemplate.success().withDescription("Operation completed!").send(channel).queue();
EmbedTemplate.error().withDescription("Something failed!").send(channel).queue();
EmbedTemplate.warning().withDescription("Be careful!").send(channel).queue();
EmbedTemplate.info().withDescription("FYI...").send(channel).queue();

Full Example: Ticket System

public class TicketSystem {
    
    private final JdaFluent fluent;
    private final Panel<TicketCategory> ticketPanel;
    
    public TicketSystem(JDA jda) {
        this.fluent = new JdaFluent(jda);
        
        this.ticketPanel = fluent.panels()
            .<TicketCategory>choices("ticket", this::handleTicketCreate)
            .codec(Enum::name, TicketCategory::valueOf)
            .choice("๐ŸŽซ", "General Support", TicketCategory.SUPPORT)
            .choice("๐Ÿ›", "Bug Report", TicketCategory.BUG)
            .choice("๐Ÿ’ฐ", "Billing Issue", TicketCategory.BILLING)
            .build();
    }
    
    public void sendTicketPanel(TextChannel channel) {
        FluentEmbed.create()
            .title("๐ŸŽซ Create a Ticket")
            .description("Click the button that matches your issue.")
            .colorInfo()
            .send(channel)
            .setComponents(ticketPanel.toActionRows())
            .queue();
    }
    
    private void handleTicketCreate(PanelContext<TicketCategory> ctx) {
        TicketCategory category = ctx.value();
        Member member = ctx.member();
        
        // Create ticket channel...
        createTicketChannel(ctx.guild(), member, category).queue(channel -> {
            sendWelcomeMessage(channel, member, category);
            ctx.reply("Ticket created: " + channel.getAsMention());
        });
    }
    
    private void sendWelcomeMessage(TextChannel channel, Member member, TicketCategory category) {
        OneShotGroup actions = fluent.buttons().group(Duration.ofDays(7));
        
        FluentEmbed.create()
            .title("Ticket - " + category.getLabel())
            .description("Welcome " + member.getAsMention() + "!\n\nDescribe your issue and staff will respond shortly.")
            .colorSuccess()
            .fieldInline("Category", category.getEmoji() + " " + category.getLabel())
            .fieldInline("Status", "๐ŸŸข Open")
            .thumbnail(member.getEffectiveAvatarUrl())
            .timestampNow()
            .send(channel)
            .setContent(member.getAsMention())
            .setComponents(ActionRow.of(
                actions.primary("๐Ÿ“", "Add Info", e -> showAddInfoModal(e)),
                actions.danger("๐Ÿ”’", "Close Ticket", e -> confirmClose(e, channel))
            ))
            .queue();
    }
    
    private void showAddInfoModal(ButtonInteractionEvent event) {
        fluent.modals()
            .create("add-info", ctx -> {
                String info = ctx.get("info");
                ctx.channel().sendMessage("**Additional info from " + ctx.user().getAsMention() + ":**\n" + info).queue();
                ctx.reply("Info added!");
            })
            .title("Add Information")
            .paragraph("info", "Additional Information", b -> b.required().minLength(10))
            .build()
            .show(event);
    }
    
    private void confirmClose(ButtonInteractionEvent event, TextChannel channel) {
        event.reply("Are you sure you want to close this ticket?")
            .setEphemeral(true)
            .setComponents(fluent.dialogs().confirm("Close", Duration.ofMinutes(2), e -> {
                channel.delete().queue();
            }))
            .queue();
    }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages