From 27f19200815e2b12ff92748939ada583e9de40a1 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Wed, 7 Aug 2024 00:18:40 +0200 Subject: [PATCH] Create event management system, added event notifier for community events Added functionalities to create and manage events, including the creation of necessary database tables, context menus, modals, and scheduling tasks. Introduced `UserToMessageTracker` utility for tracking user messages within modals. --- .../alttd/buttonManager/ButtonManager.java | 6 +- .../buttons/eventButton/EventButton.java | 101 ++++++++++++ .../ContextMenuManager.java | 2 + .../contextMenus/ContextMenuCreateEvent.java | 68 ++++++++ .../ContextMenuRespondSuggestion.java | 2 +- .../com/alttd/database/DatabaseTables.java | 18 +++ .../alttd/database/queries/events/Event.java | 79 +++++++++ .../database/queries/events/QueriesEvent.java | 59 +++++++ .../java/com/alttd/listeners/JDAListener.java | 4 + .../com/alttd/modalManager/ModalManager.java | 7 +- .../modalManager/modals/ModalCreateEvent.java | 150 ++++++++++++++++++ .../modals/ModalReplySuggestion.java | 15 +- .../com/alttd/schedulers/EventTimerTask.java | 45 ++++++ .../com/alttd/util/UserToMessageTracker.java | 19 +++ 14 files changed, 553 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/alttd/buttonManager/buttons/eventButton/EventButton.java create mode 100644 src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuCreateEvent.java create mode 100644 src/main/java/com/alttd/database/queries/events/Event.java create mode 100644 src/main/java/com/alttd/database/queries/events/QueriesEvent.java create mode 100644 src/main/java/com/alttd/modalManager/modals/ModalCreateEvent.java create mode 100644 src/main/java/com/alttd/schedulers/EventTimerTask.java create mode 100644 src/main/java/com/alttd/util/UserToMessageTracker.java diff --git a/src/main/java/com/alttd/buttonManager/ButtonManager.java b/src/main/java/com/alttd/buttonManager/ButtonManager.java index 3ee151f..caf555e 100644 --- a/src/main/java/com/alttd/buttonManager/ButtonManager.java +++ b/src/main/java/com/alttd/buttonManager/ButtonManager.java @@ -3,20 +3,17 @@ package com.alttd.buttonManager; import com.alttd.buttonManager.buttons.autoReminder.ButtonAccepted; import com.alttd.buttonManager.buttons.autoReminder.ButtonInProgress; import com.alttd.buttonManager.buttons.autoReminder.ButtonRejected; +import com.alttd.buttonManager.buttons.eventButton.EventButton; import com.alttd.buttonManager.buttons.remindMeConfirm.ButtonRemindMeCancel; import com.alttd.buttonManager.buttons.remindMeConfirm.ButtonRemindMeConfirm; import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewAccept; import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewDeny; -import com.alttd.util.Util; -import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.requests.RestAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -34,6 +31,7 @@ public class ButtonManager extends ListenerAdapter { buttons.add(new ButtonAccepted()); buttons.add(new ButtonInProgress()); buttons.add(new ButtonRejected()); + buttons.add(new EventButton()); } public void addButton(DiscordButton button) { diff --git a/src/main/java/com/alttd/buttonManager/buttons/eventButton/EventButton.java b/src/main/java/com/alttd/buttonManager/buttons/eventButton/EventButton.java new file mode 100644 index 0000000..981102f --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/eventButton/EventButton.java @@ -0,0 +1,101 @@ +package com.alttd.buttonManager.buttons.eventButton; + +import com.alttd.AltitudeBot; +import com.alttd.buttonManager.DiscordButton; +import com.alttd.database.queries.events.Event; +import com.alttd.util.Logger; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +public class EventButton extends DiscordButton { + + public EventButton() { + } + + @Override + public String getButtonId() { + return "event_button"; + } + + @Override + public void execute(ButtonInteractionEvent event) { + Event eventForButton = Event.getEvent(event.getMessageIdLong()); + if (eventForButton == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Event not found")).setEphemeral(true).queue(); + return; + } + + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This button can only be used within a guild")).setEphemeral(true).queue(); + return; + } + + if (Instant.now().isAfter(eventForButton.getStartTime())) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to process you joining" + eventForButton.getTitle() + ", the event might have ended!")).setEphemeral(true).queue(); + return; + } + + Optional any = member.getRoles().stream().filter(role -> role.getIdLong() == eventForButton.getRoleId()).findAny(); + if (any.isPresent()) { + updateJoinedEventUsersInEmbed(eventForButton); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Removed you from " + eventForButton.getTitle() + "!")).setEphemeral(true).queue(); + try { + eventForButton.getRole().getGuild().removeRoleFromMember(member, eventForButton.getRole()).queue(); + } catch (Exception e) { + Logger.altitudeLogs.error(e); + } + return; + } + + try { + eventForButton.getRole().getGuild().addRoleToMember(member, eventForButton.getRole()).queue(); + } catch (Exception e) { + Logger.altitudeLogs.error(e); + } + if (updateJoinedEventUsersInEmbed(eventForButton)) { + event.replyEmbeds(Util.genericSuccessEmbed("Success", "You joined " + eventForButton.getTitle() + "!")).setEphemeral(true).queue(); + } else { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Failed to add you to the event, contact an admin for help if needed")).setEphemeral(true).queue(); + } + } + + @Override + public Button getButton() { + return Button.primary(getButtonId(), "Join Event"); + } + + private boolean updateJoinedEventUsersInEmbed(Event event) { + Optional optionalMessage = event.getMessage(); + if (optionalMessage.isEmpty()) { + Logger.altitudeLogs.error("Unable to find event message"); + return false; + } + Message message = optionalMessage.get(); + List embeds = message.getEmbeds(); + if (embeds.isEmpty()) { + Logger.altitudeLogs.error("Unable to find event embed"); + return false; + } + + EmbedBuilder builder = new EmbedBuilder(embeds.get(0)); + builder.clearFields() + .addField("Event Start", "", true); + Guild guildById = AltitudeBot.getInstance().getJDA().getGuildById(event.getGuildId()); + if (guildById != null) { + List membersWithRole = guildById.getMembersWithRoles(event.getRole()); + builder.addField("Participants", String.valueOf(membersWithRole.size()), true); + } + MessageEmbed updatedEmbed = builder.build(); + message.editMessageEmbeds(updatedEmbed).queue(); + return true; + } + +} diff --git a/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java index 2d153ca..e6bf451 100644 --- a/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java +++ b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java @@ -1,5 +1,6 @@ package com.alttd.contextMenuManager; +import com.alttd.contextMenuManager.contextMenus.ContextMenuCreateEvent; import com.alttd.contextMenuManager.contextMenus.ContextMenuForwardToKanboard; import com.alttd.contextMenuManager.contextMenus.ContextMenuRespondSuggestion; import com.alttd.modalManager.ModalManager; @@ -23,6 +24,7 @@ public class ContextMenuManager extends ListenerAdapter { public ContextMenuManager(ModalManager modalManager) { contextMenus = List.of( new ContextMenuRespondSuggestion(modalManager), + new ContextMenuCreateEvent(modalManager), new ContextMenuForwardToKanboard() ); } diff --git a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuCreateEvent.java b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuCreateEvent.java new file mode 100644 index 0000000..90fc379 --- /dev/null +++ b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuCreateEvent.java @@ -0,0 +1,68 @@ +package com.alttd.contextMenuManager.contextMenus; + +import com.alttd.contextMenuManager.DiscordContextMenu; +import com.alttd.modalManager.ModalManager; +import com.alttd.modalManager.modals.ModalCreateEvent; +import com.alttd.util.Util; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.requests.RestAction; + +public class ContextMenuCreateEvent extends DiscordContextMenu { + + private final ModalManager modalManager; + + public ContextMenuCreateEvent(ModalManager modalManager) { + this.modalManager = modalManager; + } + + @Override + public String getContextMenuId() { + return "Create Event"; + } + + @Override + public void execute(UserContextInteractionEvent event) { + event.getInteraction().replyEmbeds(Util.genericErrorEmbed("Error", "This interaction should have been a message interaction")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + @Override + public void execute(MessageContextInteractionEvent event) { + if (!event.isFromGuild()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This has to be done in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + Message message = event.getInteraction().getTarget(); + + if (message.getChannel().getIdLong() != 1172922338023591956L || message.getAuthor().getIdLong() != event.getUser().getIdLong()) { + event.getInteraction().replyEmbeds(Util.genericErrorEmbed("Error", "You can only use this on your own community post")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Modal createEvent = modalManager.getModalFor("create_event"); + if (createEvent == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find create event modal")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + ModalCreateEvent.userToMessageTracker.putMessage(event.getUser().getIdLong(), message); //TODO find a better way to do this + event.replyModal(createEvent).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + @Override + public CommandData getUserContextInteraction() { + return Commands.message(getContextMenuId()) + .setGuildOnly(true) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MESSAGE_SEND)); + } +} diff --git a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java index 24cfe7d..0a3ac92 100644 --- a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java +++ b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java @@ -56,7 +56,7 @@ public class ContextMenuRespondSuggestion extends DiscordContextMenu { return; } - ModalReplySuggestion.putMessage(event.getUser().getIdLong(), message); //TODO find a better way to do this + ModalReplySuggestion.userToMessageTracker.putMessage(event.getUser().getIdLong(), message); //TODO find a better way to do this event.replyModal(replySuggestion).queue(RestAction.getDefaultSuccess(), Util::handleFailure); } diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index bdfa4da..ff19fc6 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -191,6 +191,24 @@ public class DatabaseTables { } } + private void createEventsTable() { + String sql = "CREATE TABLE IF NOT EXISTS events(" + + "message_id BIGINT NOT NULL, " + + "channel_id BIGINT NOT NULL, " + + "guild_id BIGINT NOT NULL, " + + "epoch_second BIGINT NOT NULL, " + + "role_id BIGINT NOT NULL," + + "title VARCHAR(128) NOT NULL, " + + "PRIMARY KEY (message_id)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.altitudeLogs.error(e); + Logger.altitudeLogs.error("Unable to create auction settings table, shutting down..."); + } + } + public static void createTables(Connection connection) { if (instance == null) instance = new DatabaseTables(connection); diff --git a/src/main/java/com/alttd/database/queries/events/Event.java b/src/main/java/com/alttd/database/queries/events/Event.java new file mode 100644 index 0000000..a0582ad --- /dev/null +++ b/src/main/java/com/alttd/database/queries/events/Event.java @@ -0,0 +1,79 @@ +package com.alttd.database.queries.events; + +import com.alttd.AltitudeBot; +import com.alttd.util.Logger; +import lombok.Getter; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +import java.time.Instant; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Optional; + + +public final class Event { + + private static final HashMap eventMap = new HashMap<>(); + @Getter + private final long messageId, guildId, channelId, roleId; + @Getter + private final Instant startTime; + @Getter + private Role role = null; + @Getter + private final String title; + + + public static Event getEvent(long messageId) { + return eventMap.get(messageId); + } + + private Message message = null; + + public Event(long messageId, long guildId, long channelId, Instant startTime, long roleId, String title) { + this.messageId = messageId; + this.guildId = guildId; + this.channelId = channelId; + this.startTime = startTime; + this.roleId = roleId; + this.title = title; + + eventMap.put(messageId, this); + loadMessageAndRole(); + Logger.altitudeLogs.info(String.format("Loaded event with title [%s] and message id [%s]", title, messageId)); + } + + public static Optional getNextEvent() { + return eventMap.values().stream().min(Comparator.comparing(Event::getStartTime)); + } + + public static void removeEvent(Event event) { + eventMap.remove(event.getMessageId()); + } + + private void loadMessageAndRole() { + Guild guild = AltitudeBot.getInstance().getJDA().getGuildById(guildId); + if (guild == null) { + Logger.altitudeLogs.error(String.format("Unable to find guild %s when creating event", guildId)); + return; + } + TextChannel textChannel = guild.getTextChannelById(channelId); + if (textChannel == null) { + Logger.altitudeLogs.error(String.format("Unable to find text channel %s when creating event", channelId)); + return; + } + textChannel.retrieveMessageById(messageId).queue(message -> { + this.message = message; + Logger.altitudeLogs.debug(String.format("Loaded message for event with title [%s]", title)); + }); + role = guild.getRoleById(roleId); + } + + public Optional getMessage() { + return message == null ? Optional.empty() : Optional.of(message); + } + +}; diff --git a/src/main/java/com/alttd/database/queries/events/QueriesEvent.java b/src/main/java/com/alttd/database/queries/events/QueriesEvent.java new file mode 100644 index 0000000..38fe0d7 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/events/QueriesEvent.java @@ -0,0 +1,59 @@ +package com.alttd.database.queries.events; + +import com.alttd.database.Database; +import com.alttd.util.Logger; +import net.dv8tion.jda.api.entities.Message; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; + +public class QueriesEvent { + + public void addEvent(Message message, Instant start, long roleId, String title) { + String sql = "INSERT INTO events VALUES(?, ?, ?, ?, ?, ?)"; + + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, message.getIdLong()); + preparedStatement.setLong(2, message.getChannelIdLong()); + preparedStatement.setLong(3, message.getGuildIdLong()); + preparedStatement.setLong(4, start.getEpochSecond()); + preparedStatement.setLong(5, roleId); + preparedStatement.setString(6, title); + + preparedStatement.executeUpdate(); + } catch (SQLException exception) { + Logger.altitudeLogs.error(exception); + } + } + + public void loadActiveEvents() { + String sql = "SELECT * FROM events WHERE epoch_second > ?"; + int counter = 0; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, Instant.now().getEpochSecond()); + + ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + new Event( + resultSet.getLong("message_id"), + resultSet.getLong("guild_id"), + resultSet.getLong("channel_id"), + Instant.ofEpochSecond(resultSet.getLong("epoch_second")), + resultSet.getLong("role_id"), + resultSet.getString("title") + ); + counter++; + } + } catch (SQLException exception) { + Logger.altitudeLogs.error(exception); + } + Logger.altitudeLogs.info(String.format("Loaded %d events", counter)); + } + +} diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index b67e340..7f90577 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -5,8 +5,10 @@ import com.alttd.buttonManager.ButtonManager; import com.alttd.commandManager.CommandManager; import com.alttd.contextMenuManager.ContextMenuManager; import com.alttd.database.queries.Poll.PollQueries; +import com.alttd.database.queries.events.QueriesEvent; import com.alttd.modalManager.ModalManager; import com.alttd.schedulers.AuctionScheduler; +import com.alttd.schedulers.EventTimerTask; import com.alttd.schedulers.PollTimerTask; import com.alttd.schedulers.ReminderScheduler; import com.alttd.request.RequestManager; @@ -49,6 +51,8 @@ public class JDAListener extends ListenerAdapter { jda.addEventListener(buttonManager, tagAdded, modalManager, commandManager, contextMenuManager, lockedChannel, appealRepost, selectMenuManager); PollQueries.loadPolls(buttonManager); new Timer().scheduleAtFixedRate(new PollTimerTask(jda, Logger.altitudeLogs), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(5)); + new QueriesEvent().loadActiveEvents(); + new Timer().scheduleAtFixedRate(new EventTimerTask(), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1)); startSchedulers(); // RequestManager.init(); // Logger.altitudeLogs.info("Starting spring application"); diff --git a/src/main/java/com/alttd/modalManager/ModalManager.java b/src/main/java/com/alttd/modalManager/ModalManager.java index 38ca225..619d4be 100644 --- a/src/main/java/com/alttd/modalManager/ModalManager.java +++ b/src/main/java/com/alttd/modalManager/ModalManager.java @@ -2,16 +2,12 @@ package com.alttd.modalManager; import com.alttd.buttonManager.ButtonManager; import com.alttd.modalManager.modals.*; -import com.alttd.util.Util; -import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.requests.RestAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; import java.util.List; import java.util.Optional; @@ -25,7 +21,8 @@ public class ModalManager extends ListenerAdapter { new ModalEvidence(), new ModalReplySuggestion(), new ModalRemindMe(buttonManager), - new ModalCrateItem()); + new ModalCrateItem(), + new ModalCreateEvent(buttonManager)); } @Override diff --git a/src/main/java/com/alttd/modalManager/modals/ModalCreateEvent.java b/src/main/java/com/alttd/modalManager/modals/ModalCreateEvent.java new file mode 100644 index 0000000..96c1766 --- /dev/null +++ b/src/main/java/com/alttd/modalManager/modals/ModalCreateEvent.java @@ -0,0 +1,150 @@ +package com.alttd.modalManager.modals; + +import com.alttd.buttonManager.ButtonManager; +import com.alttd.database.queries.events.Event; +import com.alttd.database.queries.events.QueriesEvent; +import com.alttd.modalManager.DiscordModal; +import com.alttd.util.UserToMessageTracker; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.interactions.modals.ModalMapping; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.RoleAction; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.awt.*; +import java.time.Instant; +import java.util.Random; +import java.util.stream.Collectors; + +public class ModalCreateEvent extends DiscordModal { + + public static final UserToMessageTracker userToMessageTracker = new UserToMessageTracker(); + private final ButtonManager buttonManager; + + public ModalCreateEvent(ButtonManager buttonManager) { + this.buttonManager = buttonManager; + } + + @Override + public String getModalId() { + return "create_event"; + } + + @Override + public void execute(ModalInteractionEvent event) { + ModalMapping titleModalMapping = event.getInteraction().getValue("title"); + ModalMapping timeModalMapping = event.getInteraction().getValue("time"); + if (titleModalMapping == null || timeModalMapping == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find time or title in modal")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + String title = titleModalMapping.getAsString(); + String time = timeModalMapping.getAsString(); + if (title.isEmpty() || time.isEmpty()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Response in modal is empty")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Instant eventStart; + try { + eventStart = Instant.ofEpochSecond(Long.parseLong(time)); + } catch (NumberFormatException e) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid time format, try again")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This modal only works from within a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Message message = userToMessageTracker.pullMessage(member.getIdLong()); + if (message == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "No message found to create an event from")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle(title) + .addField("Event Start", "", true) + .addField("Participants", "0", true) + .setFooter("Click the join button to be notified when the event starts") + .setColor(Color.GREEN) + .build(); + + Button eventButton = buttonManager.getButtonFor("event_button"); + + try (MessageCreateData build = new MessageCreateBuilder() + .setEmbeds(messageEmbed) + .setActionRow(eventButton) + .build()) { + + Guild guild = message.getGuild(); + ReplyCallbackAction replyCallbackAction = event.deferReply(true); + createRole(guild).queue(role -> { + message.reply(build).queue(newMessage -> { + new QueriesEvent().addEvent(newMessage, eventStart, role.getIdLong(), title); + new Event(newMessage.getIdLong(), newMessage.getGuildIdLong(), newMessage.getChannelIdLong(), eventStart, role.getIdLong(), title); + }); + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Your event has been created")).queue(); + }, failed -> { + replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to create event")).queue(); + }); + } + } + + private RoleAction createRole(Guild guild) { + String possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + Random rand = new Random(); + String randomStr = rand.ints(5, 0, possibleCharacters.length()) + .mapToObj(i -> possibleCharacters.charAt(i) + "") + .collect(Collectors.joining()); + + return guild.createRole().setName("event role " + randomStr) + .setMentionable(false) + .setHoisted(false); + } + + @Override + public Modal getModal() { + String currentTimestamp = String.valueOf(Instant.now().getEpochSecond()); + TextInput time = TextInput.create("time", "Epoch time, see https://epochconverter.com/", TextInputStyle.SHORT) + .setPlaceholder(currentTimestamp) + .setMinLength(currentTimestamp.length()) + .setMaxLength(currentTimestamp.length() + 1) + .setRequired(true) + .build(); + + TextInput title = TextInput.create("title", "Event title", TextInputStyle.SHORT) + .setPlaceholder("The title for your event") + .setMinLength(5) + .setMaxLength(128) + .setRequired(true) + .build(); + + return Modal.create(getModalId(), "Create an event") + .addComponents(ActionRow.of(title), ActionRow.of(time)) + .build(); + } +} diff --git a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java index 1409ba0..3b30042 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java @@ -1,6 +1,7 @@ package com.alttd.modalManager.modals; import com.alttd.modalManager.DiscordModal; +import com.alttd.util.UserToMessageTracker; import com.alttd.util.Util; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; @@ -11,19 +12,9 @@ import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; import net.dv8tion.jda.api.interactions.modals.ModalMapping; import net.dv8tion.jda.api.requests.RestAction; -import java.util.HashMap; - public class ModalReplySuggestion extends DiscordModal { - private static final HashMap userToMessageMap = new HashMap<>(); - - public static synchronized void putMessage(long userId, Message message) { - userToMessageMap.put(userId, message); - } - - private static synchronized Message pullMessage(long userId) { - return userToMessageMap.remove(userId); - } + public final static UserToMessageTracker userToMessageTracker = new UserToMessageTracker(); @Override public String getModalId() { @@ -53,7 +44,7 @@ public class ModalReplySuggestion extends DiscordModal { return; } - Message message = pullMessage(member.getIdLong()); + Message message = userToMessageTracker.pullMessage(member.getIdLong()); if (message == null) { event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a message for this modal")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); diff --git a/src/main/java/com/alttd/schedulers/EventTimerTask.java b/src/main/java/com/alttd/schedulers/EventTimerTask.java new file mode 100644 index 0000000..920426e --- /dev/null +++ b/src/main/java/com/alttd/schedulers/EventTimerTask.java @@ -0,0 +1,45 @@ +package com.alttd.schedulers; + +import com.alttd.database.queries.events.Event; +import com.alttd.util.Logger; +import net.dv8tion.jda.api.entities.Message; + +import java.time.Instant; +import java.util.Optional; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +public class EventTimerTask extends TimerTask { + @Override + public void run() { + checkShouldStartEvent(); + } + + private void checkShouldStartEvent(){ + Optional nextEvent = Event.getNextEvent(); + if (nextEvent.isEmpty()) { + return; + } + Event event = nextEvent.get(); + if (event.getStartTime().isAfter(Instant.now())) { + return; + } + + Event.removeEvent(event); + performEventAction(event); + event.getRole().delete().queueAfter(1, TimeUnit.HOURS); + checkShouldStartEvent(); + } + + private void performEventAction(Event event) { + Optional optionalMessage = event.getMessage(); + if (optionalMessage.isEmpty()) { + Logger.altitudeLogs.error("Unable to find message for event"); + return; + } + + Message message = optionalMessage.get(); + + message.reply(String.format("%s [%s] is starting!", event.getRole().getAsMention(), event.getTitle())).queue(); + } +} diff --git a/src/main/java/com/alttd/util/UserToMessageTracker.java b/src/main/java/com/alttd/util/UserToMessageTracker.java new file mode 100644 index 0000000..bd1438e --- /dev/null +++ b/src/main/java/com/alttd/util/UserToMessageTracker.java @@ -0,0 +1,19 @@ +package com.alttd.util; + +import net.dv8tion.jda.api.entities.Message; + +import java.util.HashMap; + +public class UserToMessageTracker { + + private final HashMap userToMessageMap = new HashMap<>(); + + public synchronized void putMessage(long userId, Message message) { + userToMessageMap.put(userId, message); + } + + public synchronized Message pullMessage(long userId) { + return userToMessageMap.remove(userId); + } + +}