diff --git a/menu/src/main/java/pw/chew/jdachewtils/menu/EmbedPaginator.java b/menu/src/main/java/pw/chew/jdachewtils/menu/EmbedPaginator.java new file mode 100644 index 00000000..4bd5d5f5 --- /dev/null +++ b/menu/src/main/java/pw/chew/jdachewtils/menu/EmbedPaginator.java @@ -0,0 +1,661 @@ +package pw.chew.jdachewtils.menu; + +import com.jagrosh.jdautilities.commons.waiter.EventWaiter; +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.internal.utils.Checks; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class EmbedPaginator implements PaginationHandler.Builder +{ + private EventWaiter waiter = null; + + private int bulkSkipNumber = 1; + + private boolean singlePage = false; + private boolean pageWrap = false; + + private long time = 1; + private TimeUnit timeUnit = TimeUnit.MINUTES; + + private Consumer finalAction = m -> m.delete().queue(); + + private final Set allowedUserIds = new HashSet<>(); + private final Set allowedRoleIds = new HashSet<>(); + + private PaginationHandler.PaginationIcon bulkSkipLeftIcon = PaginationHandler.PaginationIcon.fromUnicode(PaginationHandler.EMOJI_BULK_SKIP_LEFT); + private PaginationHandler.PaginationIcon leftIcon = PaginationHandler.PaginationIcon.fromUnicode(PaginationHandler.EMOJI_LEFT); + private PaginationHandler.PaginationIcon stopIcon = PaginationHandler.PaginationIcon.fromUnicode(PaginationHandler.EMOJI_STOP); + private PaginationHandler.PaginationIcon rightIcon = PaginationHandler.PaginationIcon.fromUnicode(PaginationHandler.EMOJI_RIGHT); + private PaginationHandler.PaginationIcon bulkSkipRightIcon = PaginationHandler.PaginationIcon.fromUnicode(PaginationHandler.EMOJI_BULK_SKIP_RIGHT); + + private final List embeds = new ArrayList<>(); + + @Override + public EventWaiter getEventWaiter(){ + return waiter; + } + + @Override + public int getBulkSkipNumber(){ + return bulkSkipNumber; + } + + @Override + public boolean allowSinglePage(){ + return singlePage; + } + + @Override + public boolean allowPageWrap(){ + return pageWrap; + } + + @Override + public long getTime(){ + return time; + } + + @Override + public TimeUnit getTimeUnit(){ + return timeUnit; + } + + @Override + public Consumer getFinalAction(){ + return finalAction; + } + + @Override + public Set getAllowedUserIds(){ + return allowedUserIds; + } + + @Override + public Set getAllowedRoleIds(){ + return allowedRoleIds; + } + + @Override + public PaginationHandler.PaginationIcon getBulkSkipLeftIcon(){ + return bulkSkipLeftIcon; + } + + @Override + public PaginationHandler.PaginationIcon getLeftIcon(){ + return leftIcon; + } + + @Override + public PaginationHandler.PaginationIcon getStopIcon(){ + return stopIcon; + } + + @Override + public PaginationHandler.PaginationIcon getRightIcon(){ + return rightIcon; + } + + @Override + public PaginationHandler.PaginationIcon getBulkSkipRightIcon(){ + return bulkSkipRightIcon; + } + + @Override + public List getEmbeds(){ + return embeds; + } + + // =========================================== + + /** + * {@inheritDoc} + * + * @return New instance of the {@link PaginationHandler PaginationHandler} to use. + */ + @Override + public PaginationHandler build(){ + return PaginationHandler.fromBuilder(this); + } + + /** + * {@inheritDoc} + * + * @param waiter + * The EventWaiter instance to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setEventWaiter(EventWaiter waiter){ + Checks.notNull(waiter, "EventWaiter"); + + this.waiter = waiter; + return this; + } + + /** + * {@inheritDoc} + * + * @param bulkSkipNumber + * The number to set for bulk skip. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipNumber(int bulkSkipNumber){ + Checks.positive(bulkSkipNumber, "BulkSkipNumber"); + + this.bulkSkipNumber = bulkSkipNumber; + return this; + } + + /** + * {@inheritDoc} + * + * @param singlePage + * Whether the Paginator should wait on a single page. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowSinglePage(boolean singlePage){ + this.singlePage = singlePage; + return this; + } + + /** + * + * @param pageWrap + * Whether the Paginator should wrap around when reaching an end. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowPageWrap(boolean pageWrap){ + this.pageWrap = pageWrap; + return this; + } + + /** + * + * @param time + * The amount of TimeUnits to wait for interactions before cancelling. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setTime(long time){ + Checks.positive(time, "Time"); + + this.time = time; + return this; + } + + /** + * + * @param timeUnit + * The TimeUnit to use for the specified time. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setTimeUnit(TimeUnit timeUnit){ + Checks.notNull(timeUnit, "TimeUnit"); + + this.timeUnit = timeUnit; + return this; + } + + /** + * + * @param finalAction + * A Consumer of type Message to set the final action. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setFinalAction(Consumer finalAction){ + Checks.notNull(finalAction, "FinalAction"); + + this.finalAction = finalAction; + return this; + } + + /** + * + * @param userIds + * Array of Longs that should be added as allowed User ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addAllowedUserIds(Long... userIds){ + Checks.noneNull(userIds, "UserIds"); + + this.allowedUserIds.addAll(Arrays.asList(userIds)); + return this; + } + + /** + * + * @param userIds + * Collection of Longs that should be added as allowed User ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addAllowedUserIds(Collection userIds){ + Checks.noneNull(userIds, "UserIds"); + + this.allowedUserIds.addAll(userIds); + return this; + } + + /** + * + * @param userIds + * Array of Longs that should be set as allowed User ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowedUserIds(Long... userIds){ + Checks.noneNull(userIds, "UserIds"); + + this.allowedUserIds.clear(); + this.allowedUserIds.addAll(Arrays.asList(userIds)); + return this; + } + + /** + * + * @param userIds + * Collection of Longs that should be set as allowed User ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowedUserIds(Collection userIds){ + Checks.noneNull(userIds, "UserIds"); + + this.allowedUserIds.clear(); + this.allowedUserIds.addAll(userIds); + return this; + } + + /** + * + * @param roleIds + * Array of Longs that should be added as allowed Role ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addAllowedRoleIds(Long... roleIds){ + Checks.noneNull(roleIds, "RoleIds"); + + this.allowedUserIds.addAll(Arrays.asList(roleIds)); + return this; + } + + /** + * + * @param roleIds + * Collection of Longs that should be added as allowed Role ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addAllowedRoleIds(Collection roleIds){ + Checks.noneNull(roleIds, "RoleIds"); + + this.allowedUserIds.addAll(roleIds); + return this; + } + + /** + * + * @param roleIds + * Array of Longs that should be set as allowed Role ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowedRoleIds(Long... roleIds){ + Checks.noneNull(roleIds, "RoleIds"); + + this.allowedUserIds.clear(); + this.allowedUserIds.addAll(Arrays.asList(roleIds)); + return this; + } + + /** + * + * @param roleIds + * Collection of Longs that should be set as allowed Role ids. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setAllowedRoleIds(Collection roleIds){ + Checks.noneNull(roleIds, "RoleIds"); + + this.allowedUserIds.clear(); + this.allowedUserIds.addAll(roleIds); + return this; + } + + /** + * {@inheritDoc} + * + * @param unicode + * The unicode Emoji to use. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipLeft(String unicode){ + Checks.notNull(unicode, "Unicode"); + + this.bulkSkipLeftIcon = PaginationHandler.PaginationIcon.fromUnicode(unicode); + return this; + } + + /** + * {@inheritDoc} + * + * @param name + * The name of the emote + * @param id + * The id of the emote + * @param animated + * If the emote is actually animated or not + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipLeft(String name, long id, boolean animated){ + Checks.notNull(name, "Name"); + + this.bulkSkipLeftIcon = PaginationHandler.PaginationIcon.fromEmote(name, id, animated); + return this; + } + + /** + * {@inheritDoc} + * + * @param emote + * The {@link net.dv8tion.jda.api.entities.Emote Emote} to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipLeft(Emote emote){ + Checks.notNull(emote, "Emote"); + + this.bulkSkipLeftIcon = PaginationHandler.PaginationIcon.fromEmote(emote); + return this; + } + + /** + * {@inheritDoc} + * + * @param unicode + * The unicode Emoji to use. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setLeft(String unicode){ + Checks.notNull(unicode, "Unicode"); + + this.leftIcon = PaginationHandler.PaginationIcon.fromUnicode(unicode); + return this; + } + + /** + * {@inheritDoc} + * + * @param name + * The name of the emote + * @param id + * The id of the emote + * @param animated + * If the emote is actually animated or not + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setLeft(String name, long id, boolean animated){ + Checks.notNull(name, "Name"); + + this.leftIcon = PaginationHandler.PaginationIcon.fromEmote(name, id, animated); + return this; + } + + /** + * {@inheritDoc} + * + * @param emote + * The {@link net.dv8tion.jda.api.entities.Emote Emote} to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setLeft(Emote emote){ + Checks.notNull(emote, "Emote"); + + this.leftIcon = PaginationHandler.PaginationIcon.fromEmote(emote); + return this; + } + + /** + * {@inheritDoc} + * + * @param unicode + * The unicode Emoji to use. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setStop(String unicode){ + Checks.notNull(unicode, "Unicode"); + + this.stopIcon = PaginationHandler.PaginationIcon.fromUnicode(unicode); + return this; + } + + /** + * {@inheritDoc} + * + * @param name + * The name of the emote + * @param id + * The id of the emote + * @param animated + * If the emote is actually animated or not + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setStop(String name, long id, boolean animated){ + Checks.notNull(name, "Name"); + + this.stopIcon = PaginationHandler.PaginationIcon.fromEmote(name, id, animated); + return this; + } + + /** + * {@inheritDoc} + * + * @param emote + * The {@link net.dv8tion.jda.api.entities.Emote Emote} to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setStop(Emote emote){ + Checks.notNull(emote, "Emote"); + + this.stopIcon = PaginationHandler.PaginationIcon.fromEmote(emote); + return this; + } + + /** + * {@inheritDoc} + * + * @param unicode + * The unicode Emoji to use. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setRight(String unicode){ + Checks.notNull(unicode, "Unicode"); + + this.rightIcon = PaginationHandler.PaginationIcon.fromUnicode(unicode); + return this; + } + + /** + * {@inheritDoc} + * + * @param name + * The name of the emote + * @param id + * The id of the emote + * @param animated + * If the emote is actually animated or not + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setRight(String name, long id, boolean animated){ + Checks.notNull(name, "Name"); + + this.rightIcon = PaginationHandler.PaginationIcon.fromEmote(name, id, animated); + return this; + } + + /** + * {@inheritDoc} + * + * @param emote + * The {@link net.dv8tion.jda.api.entities.Emote Emote} to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setRight(Emote emote){ + Checks.notNull(emote, "Emote"); + + this.rightIcon = PaginationHandler.PaginationIcon.fromEmote(emote); + return this; + } + + /** + * {@inheritDoc} + * + * @param unicode + * The unicode Emoji to use. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipRight(String unicode){ + Checks.notNull(unicode, "Unicode"); + + this.bulkSkipRightIcon = PaginationHandler.PaginationIcon.fromUnicode(unicode); + return this; + } + + /** + * {@inheritDoc} + * + * @param name + * The name of the emote + * @param id + * The id of the emote + * @param animated + * If the emote is actually animated or not + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipRight(String name, long id, boolean animated){ + Checks.notNull(name, "Name"); + + this.bulkSkipRightIcon = PaginationHandler.PaginationIcon.fromEmote(name, id, animated); + return this; + } + + /** + * {@inheritDoc} + * + * @param emote + * The {@link net.dv8tion.jda.api.entities.Emote Emote} to use + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setBulkSkipRight(Emote emote){ + Checks.notNull(emote, "Emote"); + + this.bulkSkipRightIcon = PaginationHandler.PaginationIcon.fromEmote(emote); + return this; + } + + /** + * {@inheritDoc} + * + * @param embeds + * The MessageEmbeds to add to the list. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addEmbeds(MessageEmbed... embeds){ + Checks.noneNull(embeds, "Embeds"); + + this.embeds.addAll(Arrays.asList(embeds)); + return this; + } + + /** + * {@inheritDoc} + * + * @param embeds + * The MessageEmbeds to add to the list. + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator addEmbeds(Collection embeds){ + Checks.noneNull(embeds, "Embeds"); + + this.embeds.addAll(embeds); + return this; + } + + /** + * {@inheritDoc} + * + * @param embeds + * List of {@link MessageEmbed embeds} to use for the Paginator + * + * @return EmbedPaginator class for chaining convenience. + */ + @Override + public EmbedPaginator setEmbeds(Collection embeds){ + Checks.noneNull(embeds, "Embeds"); + + this.embeds.clear(); + this.embeds.addAll(embeds); + return this; + } +} diff --git a/menu/src/main/java/pw/chew/jdachewtils/menu/PaginationHandler.java b/menu/src/main/java/pw/chew/jdachewtils/menu/PaginationHandler.java new file mode 100644 index 00000000..ab7806d6 --- /dev/null +++ b/menu/src/main/java/pw/chew/jdachewtils/menu/PaginationHandler.java @@ -0,0 +1,1161 @@ +package pw.chew.jdachewtils.menu; + +import com.jagrosh.jdautilities.commons.waiter.EventWaiter; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Central class used for handling Pagination of the various Menus. + *
Own menus can be added to this handler by using the {@link PaginationHandler.Builder Builder class} + * for creating a new instance and then calling one of the various "displayX" methods available: + *
    + *
  • {@link #displayWithButtons(MessageChannel, int)} will send a message with Buttons attached to it + * in the provided {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} and start the Menu.
  • + * + *
  • {@link #displayWithReactions(MessageChannel, int)} will send a message in the provided + * {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel}, add Reactions to it and start the Menu.
  • + * + *
  • {@link #displayWithReactions(Message, int)} will edit the provided Message, add Reactions to it + * and start the Menu.
  • + *
+ */ +public class PaginationHandler +{ + /** + * The ⏪ emoji used for moving X pages to the left. + */ + public static final String EMOJI_BULK_SKIP_LEFT = "\u23EA"; + /** + * The ◀ emoji used for moving one page to the left. + */ + public static final String EMOJI_LEFT = "\u25C0"; + /** + * The ⏹ emoji used for stopping/closing the menu. + */ + public static final String EMOJI_STOP = "\u23F9"; + /** + * The ▶ emoji used for moving one page to the right. + */ + public static final String EMOJI_RIGHT = "\u25B6"; + /** + * The ⏩ emoji used for moving X pages to the right. + */ + public static final String EMOJI_BULK_SKIP_RIGHT = "\u23E9"; + + private final String BUTTON_ID_BULK_SKIP_LEFT = "bulk_skip_left"; + private final String BUTTON_ID_LEFT = "left"; + private final String BUTTON_ID_STOP = "stop"; + private final String BUTTON_ID_RIGHT = "right"; + private final String BUTTON_ID_BULK_SKIP_RIGHT = "bulk_skip_right"; + + private final EventWaiter waiter; + + private final int totalPages; + private final int bulkSkip; + + private final boolean allowSinglePage; + private final boolean allowPageWrap; + + private final long time; + private final TimeUnit unit; + + private final Consumer finalAction; + + private final Set allowedUserIds; + private final Set allowedRoleIds; + + private final PaginationIcon bulkSkipLeftButton; + private final PaginationIcon leftButton; + private final PaginationIcon stopButton; + private final PaginationIcon rightButton; + private final PaginationIcon bulkSkipRightButton; + + private final String bulkSkipLeftName; + private final String leftName; + private final String stopName; + private final String rightName; + private final String bulkSkipRightName; + + private final List embeds; + + public PaginationHandler() throws IllegalAccessException{ + throw new IllegalAccessException("Cannot initialize PaginationHandler directly! Use the Builder instead!"); + } + + private PaginationHandler(EventWaiter waiter, int totalPages, int bulkSkip, boolean allowSinglePage, + boolean allowPageWrap, long time, TimeUnit unit, Consumer finalAction, + Set allowedUserIds, Set allowedRoleIds, PaginationIcon bulkSkipLeftButton, + PaginationIcon leftButton, PaginationIcon stopButton, PaginationIcon rightButton, + PaginationIcon bulkSkipRightButton, List embeds) + { + this.waiter = waiter; + + this.totalPages = totalPages; + this.bulkSkip = bulkSkip; + + this.allowSinglePage = allowSinglePage; + this.allowPageWrap = allowPageWrap; + + this.time = time; + this.unit = unit; + + this.finalAction = finalAction; + + this.allowedUserIds = allowedUserIds; + this.allowedRoleIds = allowedRoleIds; + + this.bulkSkipLeftButton = bulkSkipLeftButton; + this.leftButton = leftButton; + this.stopButton = stopButton; + this.rightButton = rightButton; + this.bulkSkipRightButton = bulkSkipRightButton; + + this.bulkSkipLeftName = bulkSkipLeftButton.getName(); + this.leftName = leftButton.getName(); + this.stopName = stopButton.getName(); + this.rightName = rightButton.getName(); + this.bulkSkipRightName = bulkSkipRightButton.getName(); + + this.embeds = embeds; + } + + /** + * Creates a new instance of the PaginationHandler class with values taken from the {@link Builder Builder interface}. + * + * @param builder + * Class implementing the {@link Builder Builder interface} to use values from. + * + * @return New instance of the PaginationHandler class to use. + */ + public static PaginationHandler fromBuilder(Builder builder) + { + return new PaginationHandler(builder.getEventWaiter(), builder.getEmbeds().size(), builder.getBulkSkipNumber(), + builder.allowSinglePage(), builder.allowPageWrap(), builder.getTime(), builder.getTimeUnit(), + builder.getFinalAction(), builder.getAllowedUserIds(), builder.getAllowedRoleIds(), + builder.getBulkSkipLeftIcon(), builder.getLeftIcon(), builder.getStopIcon(), builder.getRightIcon(), + builder.getBulkSkipRightIcon(), builder.getEmbeds()); + } + + /** + * Will send a Message in the provided {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} and + * add Reactions to it, before handling the pagination. + *
The provided page number will be set to 1 if it is lower than 1, or set to the total page count if going + * above it. + * + * @param channel + * The {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} to display the initial message in. + * @param pageNumber + * The page at which the menu should start. + */ + public void displayWithReactions(MessageChannel channel, int pageNumber) + { + Checks.notEmpty(embeds, "List of embeds cannot be null or empty."); + + pageNumber = setPageNumberLimit(pageNumber); + + channel.sendMessage(renderMessage(pageNumber)).queue(this::initReactions); + } + + /** + * Will edit the provided {@link net.dv8tion.jda.api.entities.Message Message} and add Reactions to it, + * before handling the pagination. + *
The provided page number will be set to 1 if it is lower than 1, or set to the total page count if going + * above it. + * + * @param message + * The {@link net.dv8tion.jda.api.entities.Message Message} to edit and display the page on. + * @param pageNumber + * The page at which the menu should start. + */ + public void displayWithReactions(Message message, int pageNumber) + { + Checks.notEmpty(embeds, "List of embeds cannot be null or empty."); + + pageNumber = setPageNumberLimit(pageNumber); + + message.editMessage(renderMessage(pageNumber)).queue(this::initReactions); + } + + /** + * Will send a Message in the provided {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} with + * {@link net.dv8tion.jda.api.interactions.components.buttons.Button Buttons} attached to it, + *
The provided page number will be set to 1 if it is lower than 1, or set to the total page count if going + * above it. + * + * @param channel + * The {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} to display the initial message in. + * @param pageNumber + * The page at which the menu should start. + */ + public void displayWithButtons(MessageChannel channel, int pageNumber) + { + Checks.notEmpty(embeds, "List of embeds cannot be null or empty."); + + pageNumber = setPageNumberLimit(pageNumber); + + initButtons(channel, pageNumber); + } + + private int setPageNumberLimit(int pageNumber) + { + if(pageNumber < 1) + return 1; + + return Math.min(pageNumber, totalPages); + + } + + private void initReactions(Message message) + { + if(totalPages > 1) + { + Set> reactions = new HashSet<>(); + if(bulkSkip > 1 && bulkSkipLeftButton != null) + reactions.add(message.addReaction(bulkSkipLeftButton.toString())); + + reactions.add(message.addReaction(leftButton.toString())); + reactions.add(message.addReaction(stopButton.toString())); + reactions.add(message.addReaction(rightButton.toString())); + + if(bulkSkip > 1 && bulkSkipRightButton != null) + reactions.add(message.addReaction(bulkSkipRightButton.toString())); + + RestAction.allOf(reactions).queue(); + } + else if(allowSinglePage) + { + message.addReaction(stopButton.toString()).queue(); + } + else + { + finalAction.accept(message); + } + } + + private void initButtons(MessageChannel channel, int pageNumber) + { + MessageBuilder builder = new MessageBuilder(renderMessage(pageNumber)); + + if(totalPages > 1) + { + Set