Finished poll commands (except for removing a button) added a timer to update total votes every 5 minutes

This commit is contained in:
Teriuihi 2023-06-04 23:30:09 +02:00
parent 4af7628203
commit 706ceb4b55
21 changed files with 750 additions and 143 deletions

View File

@ -17,6 +17,7 @@ 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;
@ -25,14 +26,22 @@ public class ButtonManager extends ListenerAdapter {
private final List<DiscordButton> buttons;
public ButtonManager() {
buttons = List.of(
new ButtonSuggestionReviewAccept(),
new ButtonSuggestionReviewDeny(),
new ButtonRemindMeCancel(),
new ButtonRemindMeConfirm(),
new ButtonAccepted(),
new ButtonInProgress(),
new ButtonRejected());
buttons = new ArrayList<>();
buttons.add(new ButtonSuggestionReviewAccept());
buttons.add(new ButtonSuggestionReviewDeny());
buttons.add(new ButtonRemindMeCancel());
buttons.add(new ButtonRemindMeConfirm());
buttons.add(new ButtonAccepted());
buttons.add(new ButtonInProgress());
buttons.add(new ButtonRejected());
}
public void addButton(DiscordButton button) {
buttons.add(button);
}
public void removeButton(DiscordButton button) {
}
@Override

View File

@ -0,0 +1,73 @@
package com.alttd.buttonManager.buttons.pollButton;
import com.alttd.buttonManager.DiscordButton;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.database.queries.Poll.PollButtonClicksQueries;
import com.alttd.util.Util;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.util.HashSet;
public class PollButton extends DiscordButton {
private final long internalId;
private final String buttonId;
private final String buttonTitle;
private final HashSet<Long> userClicks;
public PollButton(long internalId, String buttonId, String buttonTitle, HashSet<Long> userClicks) {
this.internalId = internalId;
this.buttonId = buttonId;
this.buttonTitle = buttonTitle;
this.userClicks = userClicks;
}
@Override
public String getButtonId() {
return buttonId;
}
@Override
public void execute(ButtonInteractionEvent event) {
long userId = event.getUser().getIdLong();
Poll poll = Poll.getPoll(event.getMessage().getIdLong());
if (!poll.isActive()) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to process your vote for " + buttonTitle + ", the poll might have ended!")).setEphemeral(true).queue();
return;
}
if (userClicks.contains(userId)) {
PollButtonClicksQueries.removeButtonClick(poll.getPollId(), userId);
userClicks.remove(userId);
poll.receivedVote();
event.replyEmbeds(Util.genericSuccessEmbed("Success", "Removed your vote from " + buttonTitle + ", thanks!")).setEphemeral(true).queue();
return;
}
poll.resetClicks(userId);
PollButtonClicksQueries.addButtonClick(poll.getPollId(), internalId, userId);
userClicks.add(userId);
poll.receivedVote();
event.replyEmbeds(Util.genericSuccessEmbed("Success", "You voted on " + buttonTitle + ", thanks!")).setEphemeral(true).queue();
}
@Override
public Button getButton() {
return Button.primary(getButtonId(), buttonTitle);
}
public long getInternalId() {
return internalId;
}
public void removeClick(long userId) {
userClicks.remove(userId);
}
public String getButtonTitle() {
return buttonTitle;
}
public int getVotes() {
return userClicks.size();
}
}

View File

@ -1,5 +1,6 @@
package com.alttd.commandManager;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.commandManager.commands.AddCommand.CommandManage;
import com.alttd.commandManager.commands.*;
import com.alttd.commandManager.commands.PollCommand.CommandPoll;
@ -30,7 +31,7 @@ public class CommandManager extends ListenerAdapter {
private final List<DiscordCommand> commands;
private final HashMap<String, List<ScopeInfo>> commandList = new HashMap<>();
public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager, LockedChannel lockedChannel, SelectMenuManager selectMenuManager) {
public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager, LockedChannel lockedChannel, SelectMenuManager selectMenuManager, ButtonManager buttonManager) {
commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0))));
loadCommands();
Logger.altitudeLogs.info("Loading commands...");
@ -38,7 +39,7 @@ public class CommandManager extends ListenerAdapter {
commands = List.of(
new CommandManage(jda, this, contextMenuManager),
new CommandHelp(jda, this),
new CommandPoll(jda, this),
new CommandPoll(jda, this, buttonManager),
new CommandSuggestion(jda, modalManager, this),
new CommandSuggestCrateItem(jda, modalManager, this),
new CommandSetOutputChannel(jda, this),

View File

@ -1,5 +1,6 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.commandManager.CommandManager;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubOption;
@ -22,7 +23,7 @@ public class CommandPoll extends DiscordCommand {
private final HashMap<String, SubOption> subOptionsMap = new HashMap<>();
private final CommandData commandData;
public CommandPoll(JDA jda, CommandManager commandManager) {
public CommandPoll(JDA jda, CommandManager commandManager, ButtonManager buttonManager) {
commandData = Commands.slash(getName(), "Create, edit, and manage polls")
.addSubcommands(
new SubcommandData("add", "Add a new poll to a channel")
@ -58,11 +59,11 @@ public class CommandPoll extends DiscordCommand {
.setGuildOnly(true);
Util.registerSubOptions(subOptionsMap,
new SubCommandAdd(null,this),
new SubCommandAddButton(null, this),
new SubCommandClose(null,this),
new SubCommandAddButton(null, this, buttonManager),
new SubCommandClose(null,this, buttonManager),
new SubCommandEditDescription(null, this),
new SubCommandEditTitle(null, this),
new SubCommandOpen(null, this),
new SubCommandOpen(null, this, buttonManager),
new SubCommandRemoveButton(null, this),
new SubCommandResults(null,this));
Util.registerCommand(commandManager, jda, commandData, getName());

View File

@ -0,0 +1,8 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.database.queries.Poll.Poll;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
public record PollChannel(Poll poll, TextChannel textChannel) {
}

View File

@ -0,0 +1,81 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.util.OptionMappingParsing;
import com.alttd.util.Util;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class PollUtil {
protected static PollChannel getPollHandleErrors(SlashCommandInteractionEvent event, String name) {
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, name));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
return null;
}
Poll poll = Poll.getPoll(messageId);
if (poll == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "There is no poll with this message id")).setEphemeral(true).queue();
return null;
}
Guild guild = event.getGuild();
if (guild == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "There is no guild for this event")).setEphemeral(true).queue();
return null;
}
TextChannel textChannel = guild.getTextChannelById(poll.getChannelId());
if (textChannel == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Cannot find the poll channel")).setEphemeral(true).queue();
return null;
}
if (!textChannel.canTalk()) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "I cannot talk in this channel")).setEphemeral(true).queue();
return null;
}
Member member = event.getMember();
if (member == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Cannot find you as a member")).setEphemeral(true).queue();
return null;
}
if (!textChannel.canTalk(member)) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "You cannot talk in this channel")).setEphemeral(true).queue();
return null;
}
return new PollChannel(poll, textChannel);
}
protected static void handleSuggestMessageId(CommandAutoCompleteInteractionEvent event) {
Guild guild = event.getGuild();
if (guild == null) {
event.replyChoices(new ArrayList<>()).queue();
return;
}
AutoCompleteQuery focusedOption = event.getFocusedOption();
if (focusedOption.getName().equals("message_id")) {
List<Poll> guildPolls = Poll.getGuildPolls(event.getGuild().getIdLong());
try {
String value = focusedOption.getValue();
event.replyChoiceLongs(guildPolls.stream().map(Poll::getPollId).filter(pollId -> String.valueOf(pollId).startsWith(value)).collect(Collectors.toList())).queue();
} catch (NumberFormatException ignored) {
event.replyChoices(new ArrayList<>()).queue();
}
}
}
}

View File

@ -1,32 +0,0 @@
package com.alttd.commandManager.commands.PollCommand;
import java.util.HashMap;
public class Polls {
private static Polls instance = null;
private HashMap<Long, PollData> pollDataMap;
private Polls() {
pollDataMap = new HashMap<>();
//TODO load poll data
}
public static Polls getInstance() {
if (instance == null)
instance = new Polls();
return instance;
}
public boolean addPoll(long pollId, PollData pollData) {
if (pollDataMap.containsKey(pollId))
return (false);
pollDataMap.put(pollId, pollData);
return true;
}
public PollData getPoll(long pollId) {
return pollDataMap.getOrDefault(pollId, null);
}
}

View File

@ -3,6 +3,7 @@ package com.alttd.commandManager.commands.PollCommand;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.database.queries.Poll.PollQueries;
import com.alttd.templates.Parser;
import com.alttd.templates.Template;
import com.alttd.util.Logger;
@ -71,10 +72,11 @@ public class SubCommandAdd extends SubCommand {
.setTitle(title)
.setColor(Color.RED)
.build()
).queue(message -> createdPoll(message, hook), throwable -> failedCreatingPoll(throwable, hook));
).queue(message -> createdPoll(message, title, hook), throwable -> failedCreatingPoll(throwable, hook));
}
private void createdPoll(Message message, InteractionHook hook) {
private void createdPoll(Message message, String title, InteractionHook hook) {
PollQueries.addPoll(message.getIdLong(), message.getChannel().getIdLong(), message.getGuild().getIdLong(), title);
hook.editOriginalEmbeds(Util.genericSuccessEmbed("Created Poll!",
Parser.parse("Created a poll with the message id: `<message_id>`. " +
"When you're ready don't forget to open the poll!",

View File

@ -1,24 +1,33 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.buttonManager.buttons.pollButton.PollButton;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.database.queries.Poll.PollButtonQueries;
import com.alttd.templates.Parser;
import com.alttd.templates.Template;
import com.alttd.util.Logger;
import com.alttd.util.OptionMappingParsing;
import com.alttd.util.Util;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class SubCommandAddButton extends SubCommand {
protected SubCommandAddButton(SubCommandGroup parentGroup, DiscordCommand parent) {
private final ButtonManager buttonManager;
protected SubCommandAddButton(SubCommandGroup parentGroup, DiscordCommand parent, ButtonManager buttonManager) {
super(parentGroup, parent);
this.buttonManager = buttonManager;
}
@Override
@ -28,23 +37,9 @@ public class SubCommandAddButton extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
Member member = event.getMember();
if (member == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find valid guild member."))
.setEphemeral(true)
.queue();
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
}
if (!Util.validateGuildMessageChannel(event.getInteraction(), channel, ChannelType.TEXT, member))
return;
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, getName()));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
return;
}
//TODO verify that message id is in database
Long rowLong = Util.parseLong(OptionMappingParsing.getString("button_row", event, getName()));
if (rowLong == null) {
@ -63,6 +58,7 @@ public class SubCommandAddButton extends SubCommand {
.queue();
return;
}
String buttonName = OptionMappingParsing.getString("button_name", event, getName());
if (buttonName == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve button name."))
@ -70,15 +66,16 @@ public class SubCommandAddButton extends SubCommand {
.queue();
return;
}
event.deferReply(true).queue(hook ->
channel.retrieveMessageById(messageId).queue(
message -> updatePoll(channel, rowId, buttonName, message, hook),
pollChannel.textChannel().retrieveMessageById(pollChannel.poll().getPollId()).queue(
message -> updatePoll(rowId, buttonName, message, hook),
throwable -> failedToGetMessage(throwable, hook)));
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
PollUtil.handleSuggestMessageId(event);
}
private void failedToGetMessage(Throwable throwable, InteractionHook hook) {
@ -88,11 +85,40 @@ public class SubCommandAddButton extends SubCommand {
.queue();
}
private void updatePoll(GuildMessageChannel channel, int rowId, String buttonName, Message message,
InteractionHook hook) {
//TODO add button, generate id, add a way to remove button, store id in database
//Maybe temporarily disable the poll and listen for someone to click the button, then delete that button
// and switch back to normal poll mode?
private void updatePoll(int rowId, String buttonName, Message message, InteractionHook hook) {
String buttonId = message.getId() + buttonName;
Poll poll = Poll.getPoll(message.getIdLong());
PollButtonQueries.addButton(message.getIdLong(), buttonId, buttonName);
List<PollButton> pollButtons = PollButtonQueries.loadButtons(poll, buttonManager);
if (pollButtons == null) {
hook.editOriginalEmbeds(Util.genericErrorEmbed("Error","Unable to retrieve buttons for this poll")).queue();
return;
}
Optional<PollButton> any = pollButtons.stream().filter(button -> button.getButtonId().equals(buttonId)).findAny();
if (any.isEmpty()) {
hook.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to find newly created button")).queue();
return;
}
PollButton pollButton = any.get();
List<ActionRow> actionRows = message.getActionRows();
if (rowId > 1) {//todo fix if needed in the future
hook.editOriginalEmbeds(Util.genericErrorEmbed("Error",
"Polls have only been set up to handle 1 row if you need more than one row update the code."))
.queue();
return;
}
List<ItemComponent> components;
if (!actionRows.isEmpty()) {
components = actionRows.get(0).getComponents();
} else
components = new ArrayList<>();
components.add(pollButton.getButton());
message.editMessageComponents().setActionRow(components).queue();
hook.editOriginalEmbeds(Util.genericSuccessEmbed("Success", "Added a button")).queue();
}
@Override

View File

@ -1,17 +1,26 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.util.OptionMappingParsing;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.database.queries.Poll.PollQueries;
import com.alttd.util.Util;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import java.util.List;
public class SubCommandClose extends SubCommand {
protected SubCommandClose(SubCommandGroup parentGroup, DiscordCommand parent) {
private final ButtonManager buttonManager;
protected SubCommandClose(SubCommandGroup parentGroup, DiscordCommand parent, ButtonManager buttonManager) {
super(parentGroup, parent);
this.buttonManager = buttonManager;
}
@Override
@ -21,22 +30,28 @@ public class SubCommandClose extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid channel")).setEphemeral(true).queue();
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
Poll poll = pollChannel.poll();
ReplyCallbackAction replyCallbackAction = event.deferReply(true);
pollChannel.textChannel().retrieveMessageById(poll.getPollId()).queue(message -> closePoll(message, poll, replyCallbackAction));
}
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, getName()));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
private void closePoll(Message message, Poll poll, ReplyCallbackAction replyCallbackAction) {
List<MessageEmbed> embeds = message.getEmbeds();
if (embeds.size() > 1) {
replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "This poll has already been closed!")).queue();
return;
}
message.editMessageEmbeds(embeds.get(0), poll.getVotesEmbed()).queue();
PollQueries.setPollStatus(poll.getPollId(), false, buttonManager);
replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "This poll has been closed!")).queue();
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -3,16 +3,21 @@ package com.alttd.commandManager.commands.PollCommand;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.util.OptionMappingParsing;
import com.alttd.util.Util;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
import net.dv8tion.jda.api.interactions.InteractionHook;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SubCommandEditDescription extends SubCommand {
protected SubCommandEditDescription(SubCommandGroup parentGroup, DiscordCommand parent) {
@ -26,17 +31,9 @@ public class SubCommandEditDescription extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid channel")).setEphemeral(true).queue();
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
}
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, getName()));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
return;
}
String description = OptionMappingParsing.getString("description", event, getName());
if (description == null || description.length() > 2048) {
@ -48,8 +45,8 @@ public class SubCommandEditDescription extends SubCommand {
}
event.replyEmbeds(Util.genericWaitingEmbed("Waiting...", "Editing poll...")).setEphemeral(true).queue(hook -> {
channel.retrieveMessageById(messageId).queue(message -> updatePoll(message, description, hook),
error -> hook.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to find message with id [" + messageId + "].")).queue());
pollChannel.textChannel().retrieveMessageById(pollChannel.poll().getPollId()).queue(message -> updatePoll(message, description, hook),
error -> hook.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to find message with id [" + pollChannel.poll().getPollId() + "].")).queue());
});
}
@ -67,7 +64,7 @@ public class SubCommandEditDescription extends SubCommand {
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
event.replyChoices(new ArrayList<>()).queue();
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -3,16 +3,21 @@ package com.alttd.commandManager.commands.PollCommand;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.util.OptionMappingParsing;
import com.alttd.util.Util;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
import net.dv8tion.jda.api.interactions.InteractionHook;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SubCommandEditTitle extends SubCommand {
protected SubCommandEditTitle(SubCommandGroup parentGroup, DiscordCommand parent) {
@ -26,17 +31,9 @@ public class SubCommandEditTitle extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid channel")).setEphemeral(true).queue();
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
}
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, getName()));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
return;
}
String title = OptionMappingParsing.getString("title", event, getName());
if (title == null || title.length() > 256) {
@ -48,8 +45,8 @@ public class SubCommandEditTitle extends SubCommand {
}
event.replyEmbeds(Util.genericWaitingEmbed("Waiting...", "Editing poll...")).setEphemeral(true).queue(hook -> {
channel.retrieveMessageById(messageId).queue(message -> updatePoll(message, title, hook),
error -> hook.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to find message with id [" + messageId + "].")).queue());
pollChannel.textChannel().retrieveMessageById(pollChannel.poll().getPollId()).queue(message -> updatePoll(message, title, hook),
error -> hook.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to find message with id [" + pollChannel.poll().getPollId() + "].")).queue());
});
}
@ -67,7 +64,7 @@ public class SubCommandEditTitle extends SubCommand {
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
event.replyChoices(new ArrayList<>()).queue();
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -1,17 +1,33 @@
package com.alttd.commandManager.commands.PollCommand;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.database.queries.Poll.Poll;
import com.alttd.database.queries.Poll.PollQueries;
import com.alttd.util.OptionMappingParsing;
import com.alttd.util.Util;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SubCommandOpen extends SubCommand {
protected SubCommandOpen(SubCommandGroup parentGroup, DiscordCommand parent) {
private final ButtonManager buttonManager;
protected SubCommandOpen(SubCommandGroup parentGroup, DiscordCommand parent, ButtonManager buttonManager) {
super(parentGroup, parent);
this.buttonManager = buttonManager;
}
@Override
@ -21,22 +37,34 @@ public class SubCommandOpen extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid channel")).setEphemeral(true).queue();
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
ReplyCallbackAction replyCallbackAction = event.deferReply(true);
pollChannel.textChannel().retrieveMessageById(pollChannel.poll().getPollId()).queue(a -> openPoll(a, replyCallbackAction));
}
Long messageId = Util.parseLong(OptionMappingParsing.getString("message_id", event, getName()));
if (messageId == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid message id")).setEphemeral(true).queue();
private void openPoll(Message message, ReplyCallbackAction replyCallbackAction) {
boolean res = PollQueries.setPollStatus(message.getIdLong(), true, buttonManager);
if (!res) {
replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Could not open poll in database")).queue();
return;
}
List<MessageEmbed> embeds = message.getEmbeds();
if (embeds.isEmpty()) {
replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Could not find poll embed")).queue();
return;
}
MessageEmbed messageEmbed = embeds.get(0);
EmbedBuilder embedBuilder = new EmbedBuilder(messageEmbed);
embedBuilder.setColor(Color.GREEN);
message.editMessageEmbeds(embedBuilder.build()).queue(success -> replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Poll opened")).queue());
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -3,8 +3,7 @@ package com.alttd.commandManager.commands.PollCommand;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.util.OptionMappingParsing;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import com.alttd.util.Util;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
@ -20,20 +19,18 @@ public class SubCommandRemoveButton extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null)
return;
Long messageId = OptionMappingParsing.getLong("message_id", event, getName());
if (messageId == null)
return;
String buttonName = OptionMappingParsing.getString("button_name", event, getName());
if (buttonName == null)
return;
event.replyEmbeds(Util.genericErrorEmbed("Error", "The code to remove buttons was never implemented")).setEphemeral(true).queue();
// PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
// if (pollChannel == null)
// return;
// String buttonName = OptionMappingParsing.getString("button_name", event, getName());
// if (buttonName == null)
// return;
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -3,8 +3,6 @@ package com.alttd.commandManager.commands.PollCommand;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubCommandGroup;
import com.alttd.util.OptionMappingParsing;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
@ -20,17 +18,15 @@ public class SubCommandResults extends SubCommand {
@Override
public void execute(SlashCommandInteractionEvent event) {
GuildMessageChannel channel = OptionMappingParsing.getGuildChannel("channel", event, getName());
if (channel == null)
return;
Long messageId = OptionMappingParsing.getLong("message_id", event, getName());
if (messageId == null)
PollChannel pollChannel = PollUtil.getPollHandleErrors(event, getName());
if (pollChannel == null)
return;
event.replyEmbeds(pollChannel.poll().getVotesEmbed()).setEphemeral(true).queue();
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
PollUtil.handleSuggestMessageId(event);
}
@Override

View File

@ -0,0 +1,137 @@
package com.alttd.database.queries.Poll;
import com.alttd.buttonManager.buttons.pollButton.PollButton;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
public class Poll {
private static final HashMap<Long, Poll> polls = new HashMap<>();
public static Poll getPoll(long pollId) {
return polls.get(pollId);
}
public static List<Poll> getGuildPolls(long guildId) {
return polls.values().stream().filter(poll -> poll.getGuildId() == guildId).collect(Collectors.toList());
}
public static Collection<Poll> getAllPolls() {
return polls.values();
}
private static void addPoll(Poll poll) {
polls.put(poll.getPollId(), poll);
}
private final long pollId;
private final long channelId;
private final long guildId;
private boolean active;
private String title;
private final List<PollButton> buttons = new ArrayList<>();
private boolean needsUpdate = false;
public Poll(long pollId, long channelId, long guildId, boolean active, String title) {
this.pollId = pollId;
this.channelId = channelId;
this.guildId = guildId;
this.active = active;
this.title = title;
Poll.addPoll(this);
}
public void setTitle(String title) {
this.title = title;
}
public void resetClicks(long userId) {
for (PollButton button : buttons) {
button.removeClick(userId);
}
}
public void addButton(PollButton button) {
if (buttons.stream()
.filter(listButton -> listButton.getButtonId().equals(button.getButtonId()))
.findAny()
.isEmpty())
buttons.add(button);
}
public void addButtons(List<PollButton> buttons) {
buttons.forEach(this::addButton);
}
public void removeButton(PollButton button) {
buttons.remove(button);
}
public long getPollId() {
return pollId;
}
public long getChannelId() {
return channelId;
}
public long getGuildId() {
return guildId;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getTitle() {
return title;
}
public PollButton getButton(long internalId) {
return buttons.stream().filter(button -> button.getInternalId() == internalId).findAny().orElse(null);
}
public MessageEmbed getVotesEmbed() {
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder
.setColor(Color.GREEN)
.setTitle(getEmbedTitle());
buttons.sort(Comparator.comparingLong(PollButton::getInternalId));
for (PollButton button : buttons) {
embedBuilder.addField(button.getButtonTitle(), String.valueOf(button.getVotes()), true);
}
return embedBuilder.build();
}
private String getEmbedTitle() {
String embedTitle = "Results for: " + title;
if (embedTitle.length() > MessageEmbed.TITLE_MAX_LENGTH)
embedTitle = embedTitle.substring(0, MessageEmbed.TITLE_MAX_LENGTH - 3) + "...";
return embedTitle;
}
public boolean needsUpdate() {
return needsUpdate;
}
public void receivedVote() {
needsUpdate = true;
}
public void setUpdated() {
needsUpdate = false;
}
public long getTotalVotes() {
return buttons.stream().map(PollButton::getVotes).count();
}
}

View File

@ -0,0 +1,63 @@
package com.alttd.database.queries.Poll;
import com.alttd.database.Database;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
public class PollButtonClicksQueries {
public static boolean addButtonClick(long pollId, long buttonId, long userId) {
String sql = "INSERT INTO poll_entries (poll_id, button_id, user_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE button_id = ?";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, pollId);
statement.setLong(2, buttonId);
statement.setLong(3, userId);
statement.setLong(4, buttonId);
return statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public static boolean removeButtonClick(long pollId, long userId) {
String sql = "DELETE FROM poll_entries WHERE poll_id = ? AND user_id = ?";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, pollId);
statement.setLong(2, userId);
return statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public static HashMap<Long, HashSet<Long>> loadClicks(long pollId) {
String sql = "SELECT button_id,user_id FROM poll_entries WHERE poll_id = ?";
HashMap<Long, HashSet<Long>> buttonMap = new HashMap<>();
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, pollId);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
Long buttonId = resultSet.getLong("button_id");
HashSet<Long> userSet = buttonMap.getOrDefault(buttonId, new HashSet<>());
userSet.add(resultSet.getLong("user_id"));
buttonMap.put(buttonId, userSet);
}
return buttonMap;
} catch (SQLException exception) {
exception.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,60 @@
package com.alttd.database.queries.Poll;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.buttonManager.buttons.pollButton.PollButton;
import com.alttd.database.Database;
import com.alttd.util.Logger;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class PollButtonQueries {
public static boolean addButton(long pollId, String buttonId, String buttonName/*TODO: , String buttonType*/) {
String sql = "INSERT INTO poll_buttons (poll_id, button_id, button_name, button_type) VALUES (?, ?, ?, ?)";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, pollId);
statement.setString(2, buttonId);
statement.setString(3, buttonName);
statement.setString(4, "POLL_BUTTON"); //TODO make this change to the other thing too?
return statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public static List<PollButton> loadButtons(Poll poll, ButtonManager buttonManager) {
HashMap<Long, HashSet<Long>> userClicks = PollButtonClicksQueries.loadClicks(poll.getPollId());
if (userClicks == null) {
Logger.altitudeLogs.warning("Unable to load userClicks for poll with id: " + poll);
return null;
}
String sql = "SELECT * FROM poll_buttons WHERE poll_id = ?";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, poll.getPollId());
List<PollButton> buttons = new ArrayList<>();
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
long internalButtonId = resultSet.getLong("id");
PollButton pollButton = new PollButton(internalButtonId, resultSet.getString("button_id"), resultSet.getString("button_name"), userClicks.getOrDefault(internalButtonId, new HashSet<>()));
buttonManager.addButton(pollButton);
buttons.add(pollButton);
}
poll.addButtons(buttons);
return buttons;
} catch (SQLException exception) {
exception.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,72 @@
package com.alttd.database.queries.Poll;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.database.Database;
import com.alttd.util.Logger;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class PollQueries {
public static boolean addPoll(long pollId, long channelId, long guildId, String title) {
new Poll(pollId, channelId, guildId, false, title);
String sql = "INSERT INTO polls (poll_id, channel_id, guild_id, active, poll_title, embed_type) VALUES (?, ?, ?, ?, ?, ?)";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setLong(1, pollId);
statement.setLong(2, channelId);
statement.setLong(3, guildId);
statement.setInt(4, 0);
statement.setString(5, title);
statement.setString(6, "POLL_EMBED");
return statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public static boolean setPollStatus(long pollId, boolean active, ButtonManager buttonManager) {
String sql = "UPDATE polls SET active = ? WHERE poll_id = ?";
try {
Poll poll = Poll.getPoll(pollId);
if (poll == null) {
Logger.altitudeLogs.warning("Received null poll in setPollStatus (query)");
return false;
}
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setInt(1, active ? 1 : 0);
statement.setLong(2, pollId);
poll.setActive(true);
PollButtonQueries.loadButtons(poll, buttonManager);
return statement.executeUpdate() == 1;
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public static void loadPolls(ButtonManager buttonManager) {
String sql = "SELECT * FROM polls";
try {
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
Poll poll = new Poll(
resultSet.getLong("poll_id"),
resultSet.getLong("channel_id"),
resultSet.getLong("guild_id"),
resultSet.getInt("active") == 1,
resultSet.getString("poll_title"));
PollButtonQueries.loadButtons(poll, buttonManager);
Logger.altitudeLogs.debug("Loaded poll: " + poll.getTitle());
}
} catch (SQLException exception) {
exception.printStackTrace();
}
}
}

View File

@ -3,8 +3,10 @@ package com.alttd.listeners;
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.modalManager.ModalManager;
import com.alttd.schedulers.AuctionScheduler;
import com.alttd.schedulers.PollTimerTask;
import com.alttd.schedulers.ReminderScheduler;
import com.alttd.request.RequestManager;
import com.alttd.selectMenuManager.SelectMenuManager;
@ -17,6 +19,9 @@ import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEve
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
public class JDAListener extends ListenerAdapter {
private final JDA jda;
@ -34,8 +39,10 @@ public class JDAListener extends ListenerAdapter {
ModalManager modalManager = new ModalManager(buttonManager);
ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager);
SelectMenuManager selectMenuManager = new SelectMenuManager();
CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, lockedChannel, selectMenuManager);
CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, lockedChannel, selectMenuManager, buttonManager);
jda.addEventListener(buttonManager, 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));
startSchedulers();
// RequestManager.init();
}

View File

@ -0,0 +1,69 @@
package com.alttd.schedulers;
import com.alttd.AltitudeLogs;
import com.alttd.database.queries.Poll.Poll;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TimerTask;
public class PollTimerTask extends TimerTask {
private final JDA jda;
private final AltitudeLogs logger;
public PollTimerTask(JDA jda, AltitudeLogs logger) {
this.jda = jda;
this.logger = logger;
}
@Override
public void run() {
Collection<Poll> polls = Poll.getAllPolls();
for (Poll poll : polls) {
if (poll.needsUpdate()) {
updatePoll(poll);
}
}
}
private void updatePoll(Poll poll) {
Guild guild = jda.getGuildById(poll.getGuildId());
if (guild == null) {
logger.warning("Unable to retrieve guild for poll: " + poll.getPollId());
return;
}
TextChannel textChannel = guild.getTextChannelById(poll.getChannelId());
if (textChannel == null) {
logger.warning("Unable to retrieve text channel for poll: " + poll.getPollId());
return;
}
textChannel.retrieveMessageById(poll.getPollId()).queue(message -> updatePoll(message, poll));
}
private void updatePoll(Message message, Poll poll) {
List<MessageEmbed> embeds = new ArrayList<>(message.getEmbeds());
if (embeds.isEmpty()) {
logger.warning("Unable to retrieve embeds for poll " + poll.getPollId());
return;
}
MessageEmbed messageEmbed = embeds.get(0);
EmbedBuilder embedBuilder = new EmbedBuilder(messageEmbed);
embedBuilder.setFooter("Counted " + poll.getTotalVotes() + " total votes!");
embeds.set(0, embedBuilder.build());
message.editMessageEmbeds(embeds).queue();
poll.setUpdated();
}
}