From d6c272cdaf4b1b6e747f2339214cda68546c0f77 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Tue, 6 Sep 2022 16:34:28 +0200 Subject: [PATCH] Added the Poll module V1 --- SheepstarModules/PollV1/.gitignore | 3 + SheepstarModules/PollV1/README.md | 2 + SheepstarModules/PollV1/pom.xml | 24 +++ .../poll/action/AutomaticCloseAction.java | 27 +++ .../poll/api/controller/PollController.java | 163 ++++++++++++++++++ .../xyz/sheepstar/poll/api/entities/Poll.java | 93 ++++++++++ .../poll/api/models/PollAnswerManager.java | 59 +++++++ .../poll/api/models/PollManager.java | 115 ++++++++++++ .../poll/commands/ClosePollCommand.java | 35 ++++ .../poll/commands/CreatePollCommand.java | 78 +++++++++ .../xyz/sheepstar/poll/core/PollCore.java | 70 ++++++++ .../listener/MultipleReactionListener.java | 65 +++++++ .../PollV1/src/main/resources/module.yml | 3 + 13 files changed, 737 insertions(+) create mode 100644 SheepstarModules/PollV1/.gitignore create mode 100644 SheepstarModules/PollV1/README.md create mode 100644 SheepstarModules/PollV1/pom.xml create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/action/AutomaticCloseAction.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/controller/PollController.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/entities/Poll.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollAnswerManager.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollManager.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/ClosePollCommand.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/CreatePollCommand.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/core/PollCore.java create mode 100644 SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/listener/MultipleReactionListener.java create mode 100644 SheepstarModules/PollV1/src/main/resources/module.yml diff --git a/SheepstarModules/PollV1/.gitignore b/SheepstarModules/PollV1/.gitignore new file mode 100644 index 0000000..7b98e63 --- /dev/null +++ b/SheepstarModules/PollV1/.gitignore @@ -0,0 +1,3 @@ +# Project exclude paths +/target/ +.idea \ No newline at end of file diff --git a/SheepstarModules/PollV1/README.md b/SheepstarModules/PollV1/README.md new file mode 100644 index 0000000..71dea27 --- /dev/null +++ b/SheepstarModules/PollV1/README.md @@ -0,0 +1,2 @@ +# SheepstarModule-Poll +The official sheepstar poll module diff --git a/SheepstarModules/PollV1/pom.xml b/SheepstarModules/PollV1/pom.xml new file mode 100644 index 0000000..6664fec --- /dev/null +++ b/SheepstarModules/PollV1/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + xyz.sheepstar + SheepstarModule-Poll + pre1.0.0 + + + 8 + 8 + + + + + xyz.sheepstar + SheepstarCore + beta1.0.2 + + + + \ No newline at end of file diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/action/AutomaticCloseAction.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/action/AutomaticCloseAction.java new file mode 100644 index 0000000..996d6f2 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/action/AutomaticCloseAction.java @@ -0,0 +1,27 @@ +package xyz.sheepstar.poll.action; + +import xyz.sheepstar.poll.api.controller.PollController; +import xyz.sheepstar.poll.api.models.PollManager; +import xyz.sheepstar.poll.core.PollCore; +import xyz.sheepstar.util.action.RepeatedAction; + +import java.time.LocalDateTime; + +public class AutomaticCloseAction extends RepeatedAction { + + private final PollManager pollManager = (PollManager) api.getDatabase().getTableFactory().getTable(PollManager.class); + private final PollController pollController = PollCore.getPollController(); + + @Override + public long time() { + return 5; + } + + @Override + public void execute() { + pollManager.getExpirablePolls().forEach((pollID, dateTime) -> { + if (dateTime.compareTo(LocalDateTime.now()) <= 0) + pollController.endPoll(pollID, pollManager.getPollById(pollID).getGuild()); + }); + } +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/controller/PollController.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/controller/PollController.java new file mode 100644 index 0000000..e96c93f --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/controller/PollController.java @@ -0,0 +1,163 @@ +package xyz.sheepstar.poll.api.controller; + +import net.dv8tion.jda.api.entities.Emoji; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.components.ButtonStyle; +import org.apache.commons.lang3.RandomStringUtils; +import xyz.sheepstar.poll.api.entities.Poll; +import xyz.sheepstar.poll.api.models.PollAnswerManager; +import xyz.sheepstar.poll.api.models.PollManager; +import xyz.sheepstar.poll.commands.ClosePollCommand; +import xyz.sheepstar.util.bot.builder.message.DefaultEmbedBuilder; +import xyz.sheepstar.util.bot.builder.message.DefaultResponseBuilder; +import xyz.sheepstar.util.bot.builder.message.MessageType; +import xyz.sheepstar.util.bot.listener.ListenerBasics; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +public class PollController extends ListenerBasics { + + private PollManager pollManager = (PollManager) table(PollManager.class); + private PollAnswerManager pollAnswerManager = (PollAnswerManager) table(PollAnswerManager.class); + + /** + * Gets the number emoji by number + * + * @param number The number the emoji should display + * @return the number as emoji + */ + private Emoji getEmojiByNumber(int number) { + return Emoji.fromUnicode(number + "️⃣"); + } + + /** + * Gets the 'started'-message of the poll + * + * @param pollID The id of the poll (used for the end button) + * @param date The date when the poll should expire + * @param title The title of the poll + * @param description The description of the poll + * @param answers All answers available in this poll + * @param guild The guild which created the poll + * @param closeButton Should the close button be displayed? + * @param multipleChoices Are multiple choices allowed? + * @return the final message + */ + public Message getStartedPollMessage(String pollID, Date date, String title, String description, ArrayList answers, Guild guild, + boolean closeButton, boolean multipleChoices) { + DefaultResponseBuilder response = DefaultResponseBuilder.createSimple(guild, ClosePollCommand.class).withColor(MessageType.PRIMARY) + .withTitle(translate("poll.poll_started", guild)); + + response.addField(":label: " + translate("poll.object.title", guild), title); + + if (description != null) + response.addField(":clipboard: " + translate("poll.object.description", guild), description); + + ArrayList subFields = new ArrayList<>(); + + for (int i = 0; i < answers.size(); i++) + subFields.add(getEmojiByNumber(i + 1).getAsMention() + " - " + answers.get(i)); + + response.addField(":ballot_box: " + translate("poll.object." + (multipleChoices ? "multiple_" : "") + "choices", guild) + "", subFields); + + if (date != null) + response.addField(String.format("\n:alarm_clock: %s ", translate("poll.ends", guild), date.getTime() / 1000)); + + if (closeButton) + response.addButton(ButtonStyle.DANGER, "end#" + pollID, date == null ? translate("poll.end_poll", guild) : translate("poll.end_now", guild)); + + return response.build(); + } + + /** + * Gets the 'ended'-message of the poll + * + * @param poll The poll you want to get the message from + * @return The final 'ended'-message + */ + public Message getEndedPollMessage(Poll poll) { + DefaultEmbedBuilder embedBuilder = new DefaultEmbedBuilder(MessageType.PRIMARY).setTitle(translate("poll.poll_ended", poll.getGuild())); + + StringBuilder builder = new StringBuilder(); + + boolean isTimeMessage = false; + for (String line : poll.getMessage().getEmbeds().get(0).getDescription().split("\n")) { + if (line.startsWith(":alarm_clock:")) isTimeMessage = true; + if (!line.startsWith(":alarm_clock:")) builder.append(line).append("\n"); + } + + embedBuilder.setDescription((!isTimeMessage ? builder + "\n" : builder.substring(0, builder.length() - 3))); + + HashMap reaction_counts = new HashMap<>(); + + poll.getMessage().getReactions().forEach(reaction -> { + if (reaction.getReactionEmote().isEmoji()) + reaction_counts.put(reaction.getReactionEmote().getEmoji(), reaction.getCount() - 1); + }); + + ArrayList subFields = new ArrayList<>(); + + for (int i = 0; i < poll.getAnswers().size(); i++) + subFields.add(getEmojiByNumber(i + 1).getAsMention() + " - " + reaction_counts.getOrDefault(getEmojiByNumber(i + 1).getAsMention(), 0)); + + embedBuilder.addBField(":abacus: " + translate("poll.object.results", poll.getGuild()), subFields.toArray(new String[0])); + + return embedBuilder.toMessage(); + } + + /** + * Creates a new poll + * + * @param channel The channel the poll should be created in + * @param date The date when the poll should expire + * @param title The title of the poll + * @param description The description of the poll + * @param answers All answers available in this poll + * @param closeButton Should the close button be displayed? + * @param multipleChoices Are multiple choices allowed? + * @return the id of the created poll + */ + public String createPoll(TextChannel channel, Date date, String title, String description, ArrayList answers, boolean closeButton, boolean multipleChoices) { + String pollID = RandomStringUtils.randomAlphanumeric(10); + + pollAnswerManager.addAnswers(pollID, answers); + + channel.sendMessage(getStartedPollMessage(pollID, date, title, description, answers, channel.getGuild(), closeButton, multipleChoices)).queue(message -> { + pollManager.createPoll(pollID, message, date); + for (int i = 0; i < answers.size(); i++) + message.addReaction(getEmojiByNumber(i + 1).getAsMention()).queue(); + }); + return pollID; + } + + /** + * Ends the provided poll + * + * @param pollID The poll you want to end + * @param guild The guild which created the poll + * @return true if the poll has been ended successfully, otherwise false + */ + public boolean endPoll(String pollID, Guild guild) { + if (!pollManager.isCreated(pollID, guild)) return false; + + try { + Poll poll = pollManager.getPollById(pollID); + + poll.getMessage().editMessage(getEndedPollMessage(poll)).queue(); + + poll.getMessage().clearReactions().queue(); + } catch (Exception e) { + return false; + } + + pollManager.deletePoll(pollID, guild); + pollAnswerManager.deleteAnswers(pollID); + + return true; + } + +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/entities/Poll.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/entities/Poll.java new file mode 100644 index 0000000..0ed7957 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/entities/Poll.java @@ -0,0 +1,93 @@ +package xyz.sheepstar.poll.api.entities; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.TextChannel; +import xyz.sheepstar.core.SheepstarCore; +import xyz.sheepstar.poll.api.models.PollAnswerManager; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +public class Poll { + + private PollAnswerManager pollAnswerManager = (PollAnswerManager) SheepstarCore + .getSheepstar().getDatabase().getTableFactory().getTable(PollAnswerManager.class); + + private String pollID; + private Guild guild; + private TextChannel channel; + private Message message; + private LocalDateTime expiryDate; + private ArrayList answers; + + /** + * Basic constructor of the {@link Poll} entity + * + * @param pollID The id of the poll + * @param message The poll message created by the bot + * @param expiryDate The date when the poll should expire + */ + public Poll(String pollID, Message message, LocalDateTime expiryDate) { + this.pollID = pollID; + this.message = message; + this.guild = message.getGuild(); + this.channel = message.getTextChannel(); + this.expiryDate = expiryDate; + this.answers = pollAnswerManager.getAnswers(pollID); + } + + /** + * Gets the poll id + * + * @return the poll id + */ + public String getPollID() { + return pollID; + } + + /** + * Gets the guild + * + * @return the guild + */ + public Guild getGuild() { + return guild; + } + + /** + * Gets the channel + * + * @return the channel + */ + public TextChannel getChannel() { + return channel; + } + + /** + * Gets the message + * + * @return the message + */ + public Message getMessage() { + return message; + } + + /** + * Gets the date when the poll should expire + * + * @return the expiry date + */ + public LocalDateTime getExpiryDate() { + return expiryDate; + } + + /** + * Gets all answers + * + * @return all answers + */ + public ArrayList getAnswers() { + return answers; + } +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollAnswerManager.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollAnswerManager.java new file mode 100644 index 0000000..7f44531 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollAnswerManager.java @@ -0,0 +1,59 @@ +package xyz.sheepstar.poll.api.models; + +import xyz.sheepstar.util.sql.SheepManager; + +import java.util.ArrayList; + +public class PollAnswerManager extends SheepManager { + + @Override + protected String tableName() { + return "poll_poll_answers"; + } + + @Override + protected void tableFields() { + custom("poll_id").add(); + custom("answer").add(); + } + + /** + * Adds a single answer to the poll answers + * + * @param pollID The id of the poll you want to add the answer to + * @param answer The answer you want to add + */ + public void addAnswer(String pollID, String answer) { + insert().value("poll_id", pollID).value("answer", answer).execute(); + } + + /** + * Adds multiple answers to the poll answers + * + * @param pollID The id of the poll you want to add the answer to + * @param answers The answers you want to add + */ + public void addAnswers(String pollID, ArrayList answers) { + answers.forEach(answer -> addAnswer(pollID, answer)); + } + + /** + * Deletes all answers from a poll + * + * @param pollID The id of the poll + */ + public void deleteAnswers(String pollID) { + delete().where("poll_id", pollID).execute(); + } + + /** + * Gets all answers from the poll + * + * @param pollID The id of the poll you want to get the answers from + * @return all answers from the poll + */ + public ArrayList getAnswers(String pollID) { + return select().where("poll_id", pollID).getResult().getList("answer"); + } + +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollManager.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollManager.java new file mode 100644 index 0000000..db4cb46 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/api/models/PollManager.java @@ -0,0 +1,115 @@ +package xyz.sheepstar.poll.api.models; + +import de.gnmyt.sqltoolkit.types.SQLType; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import xyz.sheepstar.poll.api.entities.Poll; +import xyz.sheepstar.util.sql.SheepManager; + +import java.time.LocalDateTime; +import java.util.Date; +import java.util.HashMap; + +public class PollManager extends SheepManager { + + @Override + protected String tableName() { + return "poll_polls"; + } + + @Override + protected void tableFields() { + custom("poll_id").add(); + custom("guild_id").add(); + custom("channel_id").add(); + custom("message_id").add(); + custom("expiry").type(SQLType.DATETIME).allowNull(true).add(); + } + + /** + * Checks if the provided poll has been already created + * + * @param pollID The id of the poll you want to check + * @param guild The guild in which the poll has been created + * @return true if the provided poll has been already created, otherwise false + */ + public boolean isCreated(String pollID, Guild guild) { + return select().where("guild_id", guild.getId()).where("poll_id", pollID).getResult().exists(); + } + + /** + * Checks if the provided message is a poll + * + * @param message The message you want to check + * @return true if the message is a poll, otherwise false + */ + public boolean isPollMessage(Message message) { + return select().where("message_id", message.getId()).getResult().exists(); + } + + /** + * Gets the id from the poll by a message + * + * @param message The message you want to get the poll id from + * @return the id of the poll + */ + public String getIDByMessage(Message message) { + return select().where("message_id", message.getId()).getResult().getString("poll_id"); + } + + /** + * Creates a new poll for you + * + * @param pollID The id the poll should have + * @param message The already sent message of the poll (to edit if finished) + * @param expiry The date when the poll should expire, NULL if it should not close automatically + */ + public void createPoll(String pollID, Message message, Date expiry) { + if (isCreated(pollID, message.getGuild())) return; + insert() + .value("poll_id", pollID) + .value("guild_id", message.getGuild().getId()) + .value("channel_id", message.getChannel().getId()) + .value("message_id", message.getId()) + .value("expiry", expiry) + .execute(); + } + + /** + * Deletes a poll from the table + * + * @param pollID The id of the poll you want to delete + * @param guild The id of the guild the poll was created on + */ + public void deletePoll(String pollID, Guild guild) { + if (!isCreated(pollID, guild)) return; + delete().where("poll_id", pollID).where("guild_id", guild.getId()).execute(); + } + + /** + * Gets all polls that can expire + * + * @return all polls that can expire + */ + public HashMap getExpirablePolls() { + HashMap expirable_polls = new HashMap<>(); + select().getResult().getList().forEach(entry -> { + if (entry.get("expiry") != null) + expirable_polls.put((String) entry.get("poll_id"), (LocalDateTime) entry.get("expiry")); + }); + return expirable_polls; + } + + /** + * Gets a specific poll by id + * + * @param pollID The id of the poll you want to get + * @return the poll + */ + public Poll getPollById(String pollID) { + return new Poll(pollID, api.getJDA().getTextChannelById(select().where("poll_id", pollID) + .getResult().getString("channel_id")).retrieveMessageById(select().where("poll_id", pollID).getResult().getString("message_id")).complete(), + (LocalDateTime) select().where("poll_id", pollID).getResult().getObject("expiry")); + } + +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/ClosePollCommand.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/ClosePollCommand.java new file mode 100644 index 0000000..1d6be8d --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/ClosePollCommand.java @@ -0,0 +1,35 @@ +package xyz.sheepstar.poll.commands; + +import xyz.sheepstar.poll.api.controller.PollController; +import xyz.sheepstar.poll.core.PollCore; +import xyz.sheepstar.util.bot.command.Arguments; +import xyz.sheepstar.util.bot.command.GuildCommand; +import xyz.sheepstar.util.bot.command.GuildEventController; +import xyz.sheepstar.util.bot.command.PublicCommandException; +import xyz.sheepstar.util.bot.command.annotations.CommandMeta; +import xyz.sheepstar.util.bot.permission.PermissionNode; + +@CommandMeta(aliases = "poll", subAliases = "close", description = "Closes a specific poll", permission = PermissionNode.ADMINISTRATOR) +public class ClosePollCommand extends GuildCommand { + + @Override + public void usage() { + usage("poll_id", "The id of the poll you want to close").required(true).add(); + } + + private PollController pollController = PollCore.getPollController(); + + @Override + public void execute(GuildEventController event, Arguments args) throws Exception { + if (pollController.endPoll(args.getString("poll_id"), event.getGuild())) { + event.success("poll.ended_successfully"); + } else throw new PublicCommandException("poll.not_found"); + } + + @Override + public void buttonClick(GuildEventController event, String id) throws Exception { + if (!id.startsWith("end#")) return; + + pollController.endPoll(id.split("#")[1], event.getGuild()); + } +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/CreatePollCommand.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/CreatePollCommand.java new file mode 100644 index 0000000..92badf8 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/commands/CreatePollCommand.java @@ -0,0 +1,78 @@ +package xyz.sheepstar.poll.commands; + +import net.dv8tion.jda.api.entities.ChannelType; +import net.dv8tion.jda.api.entities.GuildChannel; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import xyz.sheepstar.poll.api.controller.PollController; +import xyz.sheepstar.poll.core.PollCore; +import xyz.sheepstar.util.bot.command.Arguments; +import xyz.sheepstar.util.bot.command.GuildCommand; +import xyz.sheepstar.util.bot.command.GuildEventController; +import xyz.sheepstar.util.bot.command.PublicCommandException; +import xyz.sheepstar.util.bot.command.annotations.CommandMeta; +import xyz.sheepstar.util.bot.permission.PermissionNode; + +import java.util.ArrayList; +import java.util.Date; + +@CommandMeta(aliases = "poll", subAliases = "create", permission = PermissionNode.ADMINISTRATOR, description = "Creates a poll") +public class CreatePollCommand extends GuildCommand { + + private PollController pollController = PollCore.getPollController(); + + @Override + public void usage() { + usage("title", "The title of the poll").required(true).add(); + + usage("choice_1", "The first choice").required(true).add(); + usage("choice_2", "The second choice").required(true).add(); + + + usage("description", "The description of the poll").add(); + + usage("time", "When should the poll automatically be dissolved").add(); + + usage(OptionType.BOOLEAN, "close_button", "Should the poll show a close button? Otherwise you get the id to close the poll").add(); + + usage(OptionType.BOOLEAN, "multiple_choices", "Allows the user to click on multiple options").add(); + + usage(OptionType.CHANNEL, "channel", "The channel in which the message should be sent (for example if you have an poll channel)").add(); + + // All optional choices + + usage("choice_3", "The third choice").add(); + usage("choice_4", "The fourth choice").add(); + usage("choice_5", "The fifth choice").add(); + usage("choice_6", "The 6th choice").add(); + usage("choice_7", "The 7th choice").add(); + usage("choice_8", "The 8th choice").add(); + usage("choice_9", "The 9th choice").add(); + } + + @Override + public void execute(GuildEventController event, Arguments args) throws Exception { + GuildChannel channel = args.exists("channel") ? args.getChannel("channel") : event.getChannel(); + + Date date = args.exists("time") ? getTimeFromString(args.getString("time")) : null; + + boolean closeButton = !args.exists("close_button") || args.getBoolean("close_button"); + + boolean multipleChoices = args.exists("multiple_choices") && args.getBoolean("multiple_choices"); + + if (channel.getType() != ChannelType.TEXT) + throw new PublicCommandException("poll.error.channel.must_be_text"); + + ArrayList answers = new ArrayList<>(); + + for (int i = 1; i < 10; i++) + if (args.exists("choice_" + i)) answers.add(args.getString("choice_" + i)); + + + String pollID = pollController.createPoll((TextChannel) channel, date, args.getString("title"), args.getString("description"), answers, closeButton, multipleChoices); + + event.success("poll.poll_created", channel.getAsMention(), pollID); + } + + +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/core/PollCore.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/core/PollCore.java new file mode 100644 index 0000000..5497884 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/core/PollCore.java @@ -0,0 +1,70 @@ +package xyz.sheepstar.poll.core; + +import xyz.sheepstar.poll.action.AutomaticCloseAction; +import xyz.sheepstar.poll.api.controller.PollController; +import xyz.sheepstar.poll.api.models.PollAnswerManager; +import xyz.sheepstar.poll.api.models.PollManager; +import xyz.sheepstar.poll.commands.ClosePollCommand; +import xyz.sheepstar.poll.commands.CreatePollCommand; +import xyz.sheepstar.poll.listener.MultipleReactionListener; +import xyz.sheepstar.util.bot.manager.ImportManager; +import xyz.sheepstar.util.module.SheepstarModule; + +public class PollCore extends SheepstarModule { + + private static PollController pollController; + + private ImportManager importManager; + + @Override + public void onEnable() { + importManager = new ImportManager(getAPI(), "poll"); + + registerManagers(); + + pollController = new PollController(); + + registerCommands(); + registerActions(); + registerListeners(); + } + + /** + * Registers all managers of the module + */ + public void registerManagers() { + registerTable(new PollAnswerManager()); + registerTable(new PollManager()); + } + + /** + * Registers all commands of the module + */ + public void registerCommands() { + importManager.registerCommand(new CreatePollCommand()); + importManager.registerCommand(new ClosePollCommand()); + } + + /** + * Registers all listeners of the module + */ + public void registerListeners() { + importManager.registerListener(new MultipleReactionListener()); + } + + /** + * Registers all actions of the module + */ + public void registerActions() { + new AutomaticCloseAction().start(); + } + + /** + * Gets the poll controller + * + * @return the poll controller + */ + public static PollController getPollController() { + return pollController; + } +} diff --git a/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/listener/MultipleReactionListener.java b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/listener/MultipleReactionListener.java new file mode 100644 index 0000000..f4b104d --- /dev/null +++ b/SheepstarModules/PollV1/src/main/java/xyz/sheepstar/poll/listener/MultipleReactionListener.java @@ -0,0 +1,65 @@ +package xyz.sheepstar.poll.listener; + +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; +import xyz.sheepstar.poll.api.models.PollAnswerManager; +import xyz.sheepstar.poll.api.models.PollManager; +import xyz.sheepstar.util.bot.listener.GuildListener; + +import java.util.concurrent.atomic.AtomicInteger; + +public class MultipleReactionListener extends GuildListener { + + private PollManager pollManager = (PollManager) table(PollManager.class); + private PollAnswerManager pollAnswerManager = (PollAnswerManager) table(PollAnswerManager.class); + + /** + * Checks if the provided emoji is valid + * + * @param emoji The emoji you want to check + * @param message The message to get the size of the answers + * @return true if the emoji is valid, otherwise false + */ + public boolean isValidEmote(String emoji, Message message) { + for (int i = 0; i < pollAnswerManager.getAnswers(pollManager.getIDByMessage(message)).size(); i++) { + if (emoji.contains(String.valueOf(i + 1))) return true; + } + return false; + } + + @Override + public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) { + event.retrieveMessage().queue(message -> { + if (event.getUser().getId().equals(jda.getSelfUser().getId())) return; + if (!message.getAuthor().getId().equals(jda.getSelfUser().getId())) return; + if (!pollManager.isPollMessage(message)) return; + + // TODO: Migrate to id instead of message + + if (!event.getReactionEmote().isEmoji()) { + event.getReaction().removeReaction(event.getUser()).queue(); + return; + } + + if (!isValidEmote(event.getReactionEmote().getEmoji(), message)) { + event.getReaction().removeReaction(event.getUser()).queue(); + return; + } + + String[] embedLines = message.getEmbeds().get(0).getDescription().split("\n"); + + for (String line : embedLines) + if (line.startsWith("**:ballot_box:") && line.contains("(")) return; + + AtomicInteger reactAmount = new AtomicInteger(); + message.getReactions().forEach(reaction -> reaction.retrieveUsers().queue(users -> users.forEach(user -> { + if (user.getId().equals(event.getUser().getId())) reactAmount.getAndIncrement(); + + if (reactAmount.get() > 1) { + event.getReaction().removeReaction(event.getUser()).queue(); + return; + } + }))); + }); + } +} diff --git a/SheepstarModules/PollV1/src/main/resources/module.yml b/SheepstarModules/PollV1/src/main/resources/module.yml new file mode 100644 index 0000000..4dce574 --- /dev/null +++ b/SheepstarModules/PollV1/src/main/resources/module.yml @@ -0,0 +1,3 @@ +main: xyz.sheepstar.poll.core.PollCore +name: poll +author: Mathias Wagner \ No newline at end of file