diff --git a/src/main/java/com/alttd/buttonManager/ButtonManager.java b/src/main/java/com/alttd/buttonManager/ButtonManager.java new file mode 100644 index 0000000..b9a1955 --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/ButtonManager.java @@ -0,0 +1,38 @@ +package com.alttd.buttonManager; + +import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewAccept; +import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewDeny; +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class ButtonManager extends ListenerAdapter { + + private final List buttons; + + public ButtonManager() { + buttons = List.of( + new ButtonSuggestionReviewAccept(), + new ButtonSuggestionReviewDeny()); + } + + @Override + public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { + + } + + public @Nullable Button getButtonFor(String buttonId) { + Optional first = buttons.stream() + .filter(discordButton -> discordButton.getButtonId().equalsIgnoreCase(buttonId)) + .findFirst(); + if (first.isEmpty()) + return null; + return first.get().getButton(); + } + +} diff --git a/src/main/java/com/alttd/buttonManager/DiscordButton.java b/src/main/java/com/alttd/buttonManager/DiscordButton.java new file mode 100644 index 0000000..3c4a8eb --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/DiscordButton.java @@ -0,0 +1,13 @@ +package com.alttd.buttonManager; + +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +public abstract class DiscordButton { + + public abstract String getButtonId(); + + public abstract void execute(ButtonInteractionEvent event); + + public abstract Button getButton(); +} diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java new file mode 100644 index 0000000..e4deccd --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java @@ -0,0 +1,87 @@ +package com.alttd.buttonManager.buttons.suggestionReview; + +import com.alttd.buttonManager.DiscordButton; +import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; +import com.alttd.database.queries.commandOutputChannels.OutputType; +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.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.requests.RestAction; + +import java.awt.*; +import java.util.List; + +public class ButtonSuggestionReviewAccept extends DiscordButton { + + @Override + public String getButtonId() { + return "suggestion_review_accept"; + } + + @Override + public void execute(ButtonInteractionEvent event) { + Message message = event.getMessage(); + long suggestionChannelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.SUGGESTION); + long modLogChannelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.MOD_LOG); + + List embeds = message.getEmbeds(); + if (embeds.size() != 1) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + GuildMessageChannel suggestionChannel = guild.getChannelById(GuildMessageChannel.class, suggestionChannelId); + if (suggestionChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + GuildMessageChannel modLogChannel = guild.getChannelById(GuildMessageChannel.class, modLogChannelId); + if (modLogChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + MessageEmbed reviewMessage = embeds.get(0); + List fields = reviewMessage.getFields(); + if (fields.size() != 1) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message's embed does not contain a field, can't be a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + MessageEmbed suggestionMessage = new EmbedBuilder(reviewMessage) + .clearFields() + .setColor(Color.GRAY) + .setTitle(fields.get(0).getName()) + .setDescription(fields.get(0).getValue()) + .build(); + suggestionChannel.sendMessageEmbeds(suggestionMessage).queue(success -> { + message.delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was accepted and posted in the suggestion channel")).setEphemeral(true).queue(); + modLogChannel.sendMessageEmbeds(new EmbedBuilder(suggestionMessage).addField("Accepted", event.getUser().getAsMention(), false).setColor(Color.GREEN).build()) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure); + }, failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + + @Override + public Button getButton() { + return Button.primary(getButtonId(), "Accept Suggestion"); + } +} diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java new file mode 100644 index 0000000..56b19a1 --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java @@ -0,0 +1,73 @@ +package com.alttd.buttonManager.buttons.suggestionReview; + +import com.alttd.buttonManager.DiscordButton; +import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; +import com.alttd.database.queries.commandOutputChannels.OutputType; +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.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +import java.awt.*; +import java.util.List; + +public class ButtonSuggestionReviewDeny extends DiscordButton { + + @Override + public String getButtonId() { + return "suggestion_review_deny"; + } + + @Override + public void execute(ButtonInteractionEvent event) { + Message message = event.getMessage(); + long channelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.MOD_LOG); + + List embeds = message.getEmbeds(); + if (embeds.size() != 1) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")).setEphemeral(true).queue(); + return; + } + + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve guild")).setEphemeral(true).queue(); + return; + } + + GuildMessageChannel channel = guild.getChannelById(GuildMessageChannel.class, channelId); + if (channel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid mod log channel")).setEphemeral(true).queue(); + return; + } + + MessageEmbed reviewMessage = embeds.get(0); + List fields = reviewMessage.getFields(); + if (fields.size() != 1) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message's embed does not contain a field, can't be a suggestion")).setEphemeral(true).queue(); + return; + } + + MessageEmbed suggestionMessage = new EmbedBuilder(reviewMessage) + .clearFields() + .setColor(Color.RED) + .setTitle(fields.get(0).getName()) + .setDescription(fields.get(0).getValue()) + .build(); + channel.sendMessageEmbeds(suggestionMessage).queue(success -> { + message.delete().queue(); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was denied and logged")).setEphemeral(true).queue(); + }, failure -> { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")).setEphemeral(true).queue(); + }); + } + + @Override + public Button getButton() { + return Button.primary(getButtonId(), "Deny Suggestion"); + } +} diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index 7eea1dc..2755eba 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -2,7 +2,9 @@ package com.alttd.commandManager; import com.alttd.commandManager.commands.AddCommand.CommandManage; import com.alttd.commandManager.commands.CommandHelp; +import com.alttd.commandManager.commands.CommandSetOutputChannel; import com.alttd.commandManager.commands.CommandSuggestion; +import com.alttd.commandManager.commands.CommandUpdateCommands; import com.alttd.commandManager.commands.PollCommand.CommandPoll; import com.alttd.database.Database; import com.alttd.modalManager.ModalManager; @@ -19,8 +21,10 @@ import java.awt.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public class CommandManager extends ListenerAdapter { @@ -36,7 +40,9 @@ public class CommandManager extends ListenerAdapter { new CommandManage(jda, this), new CommandHelp(jda, this), new CommandPoll(jda, this), - new CommandSuggestion(jda, modalManager, this)); + new CommandSuggestion(jda, modalManager, this), + new CommandSetOutputChannel(), + new CommandUpdateCommands(this)); } @Override @@ -83,6 +89,8 @@ public class CommandManager extends ListenerAdapter { public List getCommands(Guild guild) { return commands.stream().filter(command -> { List scopeInfoList = commandList.get(command.getName()); + if (scopeInfoList == null) + return false; for (ScopeInfo scopeInfo : scopeInfoList) { switch (scopeInfo.getScope()) { case GLOBAL -> { diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java index 8f762a6..546c85c 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java @@ -7,8 +7,10 @@ import com.alttd.commandManager.SubOption; import com.alttd.util.Logger; import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; @@ -29,7 +31,7 @@ public class CommandManage extends DiscordCommand { new SubcommandData("disable", "Disable a command") .addOption(OptionType.STRING, "command", "Name of the command to disable", true, true) ); - commandData.setDefaultEnabled(true); + commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); Util.registerSubOptions(subOptionsMap, new SubCommandEnable(commandManager, null, this), new SubCommandEnable(commandManager, null, this) diff --git a/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java b/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java new file mode 100644 index 0000000..987f876 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java @@ -0,0 +1,4 @@ +package com.alttd.commandManager.commands; + +public class CommandEvidence { +} diff --git a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java index c2f17d3..88b0d27 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java @@ -26,7 +26,7 @@ public class CommandHelp extends DiscordCommand { private final CommandManager commandManager; private final CommandData commandData; - public CommandHelp(JDA jda, CommandManager commandManager) { + public CommandHelp(JDA jda, CommandManager commandManager) { //TODO make this work without specifying a command... this.commandManager = commandManager; commandData = Commands.slash(getName(), "Show info about all commands or a specific command.") diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java new file mode 100644 index 0000000..92be107 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java @@ -0,0 +1,112 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.DiscordCommand; +import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; +import com.alttd.database.queries.commandOutputChannels.OutputType; +import com.alttd.util.Util; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +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.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.requests.RestAction; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandSetOutputChannel extends DiscordCommand { + + private final CommandData commandData; + + public CommandSetOutputChannel() { + commandData = Commands.slash(getName(), "Set up output channels") + .addOption(OptionType.STRING, "type", "The type of output channel", true, true) + .addOption(OptionType.CHANNEL, "channel", "The channel the specified output should go into", true) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + } + + @Override + public String getName() { + return "setoutputchannel"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command can only be used within guilds")).setEphemeral(true).queue(); + return; + } + + OptionMapping option = event.getOption("type"); + if (option == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find type parameter.")).setEphemeral(true).queue(); + return; + } + + String type = option.getAsString().toUpperCase(); + OutputType outputType; + try { + outputType = OutputType.valueOf(type); + } catch (IllegalArgumentException exception) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Not a valid output type.")).setEphemeral(true).queue(); + return; + } + + option = event.getOption("channel"); + if (option == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find channel parameter.")).setEphemeral(true).queue(); + return; + } + + GuildChannelUnion channel = option.getAsChannel(); + switch (channel.getType()) { + case TEXT, NEWS, GUILD_NEWS_THREAD, GUILD_PUBLIC_THREAD, GUILD_PRIVATE_THREAD -> { + boolean success = CommandOutputChannels.setOutputChannel(guild.getIdLong(), outputType, channel.getIdLong()); + if (success) + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Set channel " + channel.getAsMention() + " as the output channel for " + outputType.name() + ".")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + else + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to store the new channel output in the database")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + default -> event.replyEmbeds(Util.genericErrorEmbed("Error", "The channel type " + channel.getType().name() + " is not a valid output channel type")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + AutoCompleteQuery focusedOption = event.getFocusedOption(); + if (!focusedOption.getType().equals(OptionType.STRING) || !focusedOption.getName().equalsIgnoreCase("type")) + event.replyChoices(Collections.emptyList()).queue(); + String value = focusedOption.getValue().toLowerCase(); + List collect = Arrays.stream(OutputType.values()) + .map(Enum::name) + .filter(type -> type.toLowerCase().startsWith(value.toLowerCase())) + .collect(Collectors.toList()); + + for (int i = collect.size(); i > 25; i--) //Can only have 25 options + collect.remove(i - 1); + event.replyChoiceStrings(collect).queue(); + } + + @Override + public String getHelpMessage() { + return null; + } + + @Override + public CommandData getCommandData() { + return commandData; + } +} diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java b/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java index 3dcbd26..07003be 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEve import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.Modal; +import net.dv8tion.jda.api.requests.RestAction; import java.util.Collections; @@ -41,7 +42,7 @@ public class CommandSuggestion extends DiscordCommand { "Unable to find suggestion modal, please report this issue to Teri")).queue(); return; } - event.replyModal(modal).queue(); + event.replyModal(modal).queue(RestAction.getDefaultSuccess(), Util::handleFailure); } @Override diff --git a/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java b/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java new file mode 100644 index 0000000..09797f7 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java @@ -0,0 +1,65 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.util.Util; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +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.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandUpdateCommands extends DiscordCommand { + + private final CommandData commandData; + private final CommandManager commandManager; + + public CommandUpdateCommands(CommandManager commandManager) { + this.commandManager = commandManager; + this.commandData = Commands.slash(getName(), "Updates all commands for this bot in this guild") + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + } + + @Override + public String getName() { + return "updatecommands"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command can only be ran from a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + ReplyCallbackAction replyCallbackAction = event.deferReply(true); + List commandDataList = commandManager.getCommands(guild).stream().map(DiscordCommand::getCommandData).collect(Collectors.toList()); + guild.updateCommands().addCommands(commandDataList).queue(success -> replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Updated the commands in this guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), Util::handleFailure); + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + event.replyChoices(Collections.emptyList()).queue(); + } + + @Override + public String getHelpMessage() { + return null; + } + + @Override + public CommandData getCommandData() { + return commandData; + } +} diff --git a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java index 4b87f8e..9aa4cba 100644 --- a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java +++ b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java @@ -6,6 +6,7 @@ import com.alttd.commandManager.SubOption; import com.alttd.util.Logger; import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; @@ -53,7 +54,7 @@ public class CommandPoll extends DiscordCommand { new SubcommandData("results", "Get the results for a poll") .addOption(OptionType.CHANNEL, "channel", "Channel this poll is in", true) .addOption(OptionType.STRING, "message_id", "Id of the poll you want the results for", true)); - commandData.setDefaultPermissions(DefaultMemberPermissions.ENABLED); + commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); Util.registerSubOptions(subOptionsMap, new SubCommandAdd(null,this), new SubCommandAddButton(null, this), diff --git a/src/main/java/com/alttd/commandManager/commands/PollCommand/SubCommandAddButton.java b/src/main/java/com/alttd/commandManager/commands/PollCommand/SubCommandAddButton.java index 3cd337e..4b22acc 100644 --- a/src/main/java/com/alttd/commandManager/commands/PollCommand/SubCommandAddButton.java +++ b/src/main/java/com/alttd/commandManager/commands/PollCommand/SubCommandAddButton.java @@ -70,7 +70,7 @@ public class SubCommandAddButton extends SubCommand { .queue(); return; } - event.deferReply().queue(hook -> + event.deferReply(true).queue(hook -> channel.retrieveMessageById(messageId).queue( message -> updatePoll(channel, rowId, buttonName, message, hook), throwable -> failedToGetMessage(throwable, hook))); diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 05a89da..683b837 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -37,16 +37,16 @@ public class DatabaseTables { } private void createPollsTable() { + String sql = "CREATE TABLE IF NOT EXISTS polls(" + + "poll_id BIGINT NOT NULL, " + + "channel_id BIGINT NOT NULL, " + + "guild_id BIGINT NOT NULL, " + + "active BIT DEFAULT b'0', " + + "poll_title VARCHAR(256) NOT NULL, " + + "embed_type VARCHAR(32) DEFAULT 'ABSTRACT_EMBED', " + + "PRIMARY KEY (poll_id)" + + ")"; try { - String sql = "CREATE TABLE IF NOT EXISTS polls(" + - "poll_id BIGINT NOT NULL, " + - "channel_id BIGINT NOT NULL, " + - "guild_id BIGINT NOT NULL, " + - "active BIT DEFAULT b'0', " + - "poll_title VARCHAR(256) NOT NULL, " + - "embed_type VARCHAR(32) DEFAULT 'ABSTRACT_EMBED', " + - "PRIMARY KEY (poll_id)" + - ")"; connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); @@ -55,13 +55,28 @@ public class DatabaseTables { } private void createCommandsTable() { + String sql = "CREATE TABLE IF NOT EXISTS commands(" + + "command_name VARCHAR(64) NOT NULL, " + + "scope VARCHAR(16) NOT NULL, " + + "location_id BIGINT NOT NULL, " + + "PRIMARY KEY (command_name, scope, location_id)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create polls table, shutting down..."); + } + } + + private void createOutputChannelsTable() { + String sql = "CREATE TABLE IF NOT EXISTS output_channels(" + + "guild BIGINT NOT NULL, " + + "output_type VARCHAR(64) NOT NULL, " + + "channel BIGINT NOT NULL, " + + "PRIMARY KEY (guild, output_type, channel)" + + ")"; try { - String sql = "CREATE TABLE IF NOT EXISTS commands(" + - "command_name VARCHAR(64) NOT NULL, " + - "scope VARCHAR(16) NOT NULL, " + - "location_id BIGINT NOT NULL, " + - "PRIMARY KEY (command_name, scope, location_id)" + - ")"; connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); diff --git a/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java b/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java index d54e05e..67d7049 100644 --- a/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java +++ b/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java @@ -10,7 +10,7 @@ import java.sql.SQLException; public class CommandOutputChannels { public static boolean setOutputChannel(long guildId, OutputType outputType, long channelId) { - String sql = "INSERT INTO output_channel (guild, output_type, channel) VALUES (?, ?, ?)"; + String sql = "INSERT INTO output_channels (guild, output_type, channel) VALUES (?, ?, ?)"; try { PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); preparedStatement.setLong(1, guildId); @@ -31,7 +31,7 @@ public class CommandOutputChannels { * @return long channel id or 0 if it errors or can't be found */ public static long getOutputChannel(long guildId, OutputType outputType) { - String sql = "SELECT channel FROM output_channel WHERE guild = ? AND output_type = ?"; + String sql = "SELECT channel FROM output_channels WHERE guild = ? AND output_type = ?"; try { PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); preparedStatement.setLong(1, guildId); diff --git a/src/main/java/com/alttd/database/queries/commandOutputChannels/OutputType.java b/src/main/java/com/alttd/database/queries/commandOutputChannels/OutputType.java index 14b49a1..12724fa 100644 --- a/src/main/java/com/alttd/database/queries/commandOutputChannels/OutputType.java +++ b/src/main/java/com/alttd/database/queries/commandOutputChannels/OutputType.java @@ -2,5 +2,6 @@ package com.alttd.database.queries.commandOutputChannels; public enum OutputType { SUGGESTION, - SUGGESTION_REVIEW + SUGGESTION_REVIEW, + MOD_LOG } diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index 130539c..b0c1676 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -1,5 +1,6 @@ package com.alttd.listeners; +import com.alttd.buttonManager.ButtonManager; import com.alttd.commandManager.CommandManager; import com.alttd.modalManager.ModalManager; import com.alttd.util.Logger; @@ -19,7 +20,7 @@ public class JDAListener extends ListenerAdapter { @Override public void onReady(@NotNull ReadyEvent event) { Logger.info("JDA ready to register commands."); - jda.addEventListener(new CommandManager(jda, new ModalManager())); + jda.addEventListener(new CommandManager(jda, new ModalManager(new ButtonManager()))); } } diff --git a/src/main/java/com/alttd/modalManager/ModalManager.java b/src/main/java/com/alttd/modalManager/ModalManager.java index 5989370..552e85f 100644 --- a/src/main/java/com/alttd/modalManager/ModalManager.java +++ b/src/main/java/com/alttd/modalManager/ModalManager.java @@ -1,10 +1,13 @@ package com.alttd.modalManager; +import com.alttd.buttonManager.ButtonManager; import com.alttd.modalManager.modals.ModalSuggestion; +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.components.Modal; +import net.dv8tion.jda.api.requests.RestAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,9 +19,9 @@ public class ModalManager extends ListenerAdapter { private final List modals; - public ModalManager() { + public ModalManager(ButtonManager buttonManager) { modals = List.of( - new ModalSuggestion()); + new ModalSuggestion(buttonManager)); } @Override @@ -34,7 +37,7 @@ public class ModalManager extends ListenerAdapter { .setColor(Color.RED) .build()) .setEphemeral(true) - .queue(); + .queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } first.get().execute(event); diff --git a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java index 9e72005..ce113d0 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java @@ -1,23 +1,36 @@ package com.alttd.modalManager.modals; +import com.alttd.buttonManager.ButtonManager; import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; import com.alttd.database.queries.commandOutputChannels.OutputType; import com.alttd.modalManager.DiscordModal; 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.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.Modal; +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.ModalMapping; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import java.util.List; public class ModalSuggestion extends DiscordModal { + + private final ButtonManager buttonManager; + + public ModalSuggestion(ButtonManager buttonManager) { + this.buttonManager = buttonManager; + } + @Override public String getModalId() { return "suggestion"; @@ -27,8 +40,8 @@ public class ModalSuggestion extends DiscordModal { public void execute(ModalInteractionEvent event) { List modalMappings = event.getValues(); if (modalMappings.size() != 2) { - event.replyEmbeds(Util.genericErrorEmbed("Error", - "Found the wrong number of fields in your form input")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "Found the wrong number of fields in your form input")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } String title = modalMappings.get(0).getAsString(); @@ -36,28 +49,53 @@ public class ModalSuggestion extends DiscordModal { Guild guild = event.getGuild(); if (guild == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find this guild")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find this guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } + GuildMessageChannel channel = guild.getChannelById(GuildMessageChannel.class, CommandOutputChannels.getOutputChannel(guild.getIdLong(), OutputType.SUGGESTION_REVIEW)); if (channel == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", - "This guild does not have a suggestion review channel or it's not the right channel type")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "This guild does not have a suggestion review channel or it's not the right channel type")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } - //TODO add user tag in description - MessageEmbed reviewMessage = new EmbedBuilder().setTitle("New suggestion").appendDescription("USER").addField(title, desc, false).build(); - channel.sendMessageEmbeds(reviewMessage).queue(success -> success.addReaction(null /*TODO FIX EMOTE this should be buttons*/).queue(aa -> { - MessageEmbed responseEmbed = new EmbedBuilder().setTitle("Your submitted suggestion").addField(title, desc, false).build(); - event.replyEmbeds(responseEmbed).setEphemeral(true).queue(); - //TODO this should have something incase it errors - }), failure -> { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't submit suggestion for review")).setEphemeral(true).queue(); - //TODO include their suggestion in this error (you can send more than one embed) - }); + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command should only be executed from a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + MessageEmbed suggestionToPlayer = new EmbedBuilder().setTitle("Your suggestion").addField(title, desc, false).build(); + MessageEmbed reviewMessage = new EmbedBuilder() + .setTitle("New suggestion") + .appendDescription(member.getAsMention()) + .addField(title, desc, false) + .setAuthor(member.getEffectiveName(), null, member.getAvatarUrl()) + .setFooter(member.getIdLong() + "") + .build(); + channel.sendMessageEmbeds(reviewMessage).queue( + success -> addButtons(success, event.deferReply(true), suggestionToPlayer), + failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't submit suggestion for review."), suggestionToPlayer) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + + public void addButtons(Message message, ReplyCallbackAction replyCallbackAction, MessageEmbed suggestionToPlayer) { + Button suggestionReviewAccept = buttonManager.getButtonFor("suggestion_review_accept"); + Button suggestionReviewDeny = buttonManager.getButtonFor("suggestion_review_deny"); + if (suggestionReviewAccept == null || suggestionReviewDeny == null) { + replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to prepare your suggestion for review.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + message.editMessageComponents().setActionRow(suggestionReviewAccept, suggestionReviewDeny).queue( + success -> replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Your suggestion was submitted for review!"), suggestionToPlayer) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + failure -> replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Couldn't prepare your suggestion for review."), suggestionToPlayer) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); } @Override @@ -69,7 +107,7 @@ public class ModalSuggestion extends DiscordModal { .build(); TextInput body = TextInput.create("body", "Body", TextInputStyle.PARAGRAPH) - .setPlaceholder("Your concerns go here") + .setPlaceholder("Suggestion...") .setRequiredRange(30, 1024) .setRequired(true) .build(); diff --git a/src/main/java/com/alttd/util/Util.java b/src/main/java/com/alttd/util/Util.java index 7a912b0..686bb67 100644 --- a/src/main/java/com/alttd/util/Util.java +++ b/src/main/java/com/alttd/util/Util.java @@ -33,8 +33,12 @@ public class Util { .collect(Collectors.toList()); } + public static void ignoreSuccess(Object o) { + // IDK I thought this looked nicer in the .queue call + } + public static void handleFailure(Throwable failure) { - Logger.warning(failure.getMessage()); + Logger.severe(failure.getMessage()); } public static MessageEmbed guildOnlyCommand(String commandName) {