Added a way to reply to suggestions

This commit is contained in:
Teriuihi 2022-09-16 21:34:57 +02:00
parent 2676acc205
commit 2083f8ff35
10 changed files with 367 additions and 22 deletions

View File

@ -107,7 +107,7 @@ public class ButtonSuggestionReviewAccept extends DiscordButton {
}
public void sendSuggestionInForum(ForumChannel forumChannel, TextChannel modLog, MessageEmbed.Field field, MessageEmbed suggestionMessage, ButtonInteractionEvent event) {
MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue()).build();
MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue() + "\u200B").build();
forumChannel.createForumPost(field.getName(), messageCreateData).queue(success -> {
event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure);

View File

@ -3,6 +3,7 @@ package com.alttd.commandManager;
import com.alttd.commandManager.commands.AddCommand.CommandManage;
import com.alttd.commandManager.commands.*;
import com.alttd.commandManager.commands.PollCommand.CommandPoll;
import com.alttd.contextMenuManager.ContextMenuManager;
import com.alttd.database.Database;
import com.alttd.modalManager.ModalManager;
import com.alttd.util.Logger;
@ -29,12 +30,12 @@ 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) {
public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager) {
commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0))));
loadCommands();
Logger.info("Loading commands...");
commands = List.of(
new CommandManage(jda, this),
new CommandManage(jda, this, contextMenuManager),
new CommandHelp(jda, this),
new CommandPoll(jda, this),
new CommandSuggestion(jda, modalManager, this),

View File

@ -4,6 +4,7 @@ import com.alttd.commandManager.CommandManager;
import com.alttd.commandManager.DiscordCommand;
import com.alttd.commandManager.SubCommand;
import com.alttd.commandManager.SubOption;
import com.alttd.contextMenuManager.ContextMenuManager;
import com.alttd.util.Logger;
import com.alttd.util.Util;
import net.dv8tion.jda.api.JDA;
@ -23,7 +24,7 @@ public class CommandManage extends DiscordCommand {
private final HashMap<String, SubOption> subOptionsMap = new HashMap<>();
private final CommandData commandData;
public CommandManage(JDA jda, CommandManager commandManager) {
public CommandManage(JDA jda, CommandManager commandManager, ContextMenuManager contextMenuManager) {
commandData = Commands.slash(getName(), "Enable commands and assign permissions")
.addSubcommands(
new SubcommandData("enable", "Enable a command in a channel")
@ -33,8 +34,8 @@ public class CommandManage extends DiscordCommand {
);
commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR));
Util.registerSubOptions(subOptionsMap,
new SubCommandEnable(commandManager, null, this),
new SubCommandEnable(commandManager, null, this)
new SubCommandEnable(commandManager, contextMenuManager, null, this),
new SubCommandDisable(commandManager, null, this)
);
Util.registerCommand(commandManager, jda, commandData, getName());
}

View File

@ -1,20 +1,19 @@
package com.alttd.commandManager.commands.AddCommand;
import com.alttd.commandManager.*;
import com.alttd.contextMenuManager.ContextMenuManager;
import com.alttd.contextMenuManager.DiscordContextMenu;
import com.alttd.database.Database;
import com.alttd.templates.Parser;
import com.alttd.templates.Template;
import com.alttd.util.Logger;
import com.alttd.util.Util;
import com.google.protobuf.GeneratedMessageV3;
import com.mysql.cj.log.Log;
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.OptionMapping;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@ -23,10 +22,12 @@ import java.util.stream.Collectors;
public class SubCommandEnable extends SubCommand {
private final CommandManager commandManager;
private final ContextMenuManager contextMenuManager;
protected SubCommandEnable(CommandManager commandManager, SubCommandGroup parentGroup, DiscordCommand parent) {
protected SubCommandEnable(CommandManager commandManager, ContextMenuManager contextMenuManager, SubCommandGroup parentGroup, DiscordCommand parent) {
super(parentGroup, parent);
this.commandManager = commandManager;
this.contextMenuManager = contextMenuManager;
}
@Override
@ -50,11 +51,22 @@ public class SubCommandEnable extends SubCommand {
String commandName = option.getAsString();
DiscordCommand command = commandManager.getCommand(commandName);
if (command == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a command called [" + commandName + "].")).setEphemeral(true).queue();
if (command != null) {
tryEnableCommand(command, guild, commandName, event);
return;
}
DiscordContextMenu contextMenu = contextMenuManager.getContext(commandName);
if (contextMenu != null) {
tryEnableContextMenu(contextMenu, guild, commandName, event);
//todo stuff
return;
}
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a command called [" + commandName + "].")).setEphemeral(true).queue();
}
private void tryEnableCommand(DiscordCommand command, Guild guild, String commandName, SlashCommandInteractionEvent event) {
if (enableCommand(command, guild.getIdLong())) {
Util.registerCommand(guild, command.getCommandData(), command.getName());
event.replyEmbeds(Util.genericSuccessEmbed("Enabled command",
@ -71,6 +83,23 @@ public class SubCommandEnable extends SubCommand {
}
}
private void tryEnableContextMenu(DiscordContextMenu contextMenu, Guild guild, String commandName, SlashCommandInteractionEvent event) {
if (enableContextMenu(contextMenu, guild.getIdLong())) {
Util.registerCommand(guild, contextMenu.getUserContextInteraction(), contextMenu.getContextMenuId());
event.replyEmbeds(Util.genericSuccessEmbed("Enabled command",
Parser.parse("Successfully enabled <command> in <guild>!",
Template.of("command", commandName.toLowerCase()),
Template.of("guild", guild.getName())
))).setEphemeral(true).queue();
} else {
event.replyEmbeds(Util.genericErrorEmbed("Failed to enable command",
Parser.parse("Unable to enable <command> in <guild>, is it already enabled?",
Template.of("command", commandName.toLowerCase()),
Template.of("guild", guild.getName())
))).setEphemeral(true).queue();
}
}
private boolean enableCommand(DiscordCommand command, long guildId) {
if (!commandManager.enableCommand(command.getName(), new ScopeInfo(CommandScope.GUILD, guildId)))
return false;
@ -100,6 +129,35 @@ public class SubCommandEnable extends SubCommand {
return true;
}
private boolean enableContextMenu(DiscordContextMenu contextMenu, long guildId) {
if (!commandManager.enableCommand(contextMenu.getContextMenuId(), new ScopeInfo(CommandScope.GUILD, guildId)))
return false;
String sql = "INSERT INTO commands (command_name, scope, location_id) VALUES(?, ?, ?)";
PreparedStatement statement = null;
try {
statement = Database.getDatabase().getConnection().prepareStatement(sql);
statement.setString(1, contextMenu.getContextMenuId());
statement.setString(2, "GUILD");
statement.setLong(3, guildId);
if (statement.executeUpdate() == 0) {
Logger.warning("Unable to enable command: % for guild: %", contextMenu.getContextMenuId(), String.valueOf(guildId));
return false;
}
} catch (SQLException exception) {
Logger.sql(exception);
return false;
} finally {
try {
if (statement != null)
statement.close();
} catch (SQLException exception) {
Logger.sql(exception);
}
}
return true;
}
@Override
public void suggest(CommandAutoCompleteInteractionEvent event) {
OptionMapping option = event.getOption("command");
@ -110,13 +168,21 @@ public class SubCommandEnable extends SubCommand {
}
String commandName = option.getAsString().toLowerCase();
ScopeInfo scopeInfo = new ScopeInfo(CommandScope.GLOBAL, event.getGuild().getIdLong());
event.replyChoiceStrings(commandManager.getCommands().stream()
.map(DiscordCommand::getName)
.filter(name -> name.toLowerCase().startsWith(commandName))
.filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo))
.limit(25)
.collect(Collectors.toList()))
.queue();
List<String> collect = commandManager.getCommands().stream()
.map(DiscordCommand::getName)
.filter(name -> name.toLowerCase().startsWith(commandName))
.filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo))
.limit(25)
.collect(Collectors.toList());
collect.addAll(contextMenuManager.getContexts().stream()
.map(DiscordContextMenu::getContextMenuId)
.filter(name -> name.toLowerCase().startsWith(commandName))
.filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo))
.limit(25)
.collect(Collectors.toList()));
event.replyChoiceStrings(collect).queue();
}
@Override

View File

@ -0,0 +1,76 @@
package com.alttd.contextMenuManager;
import com.alttd.contextMenuManager.contextMenus.ContextMenuRespondSuggestion;
import com.alttd.modalManager.ModalManager;
import com.alttd.util.Util;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.RestAction;
import javax.annotation.Nonnull;
import java.awt.*;
import java.util.List;
import java.util.Optional;
public class ContextMenuManager extends ListenerAdapter {
private final List<DiscordContextMenu> contextMenus;
public ContextMenuManager(ModalManager modalManager) {
contextMenus = List.of(
new ContextMenuRespondSuggestion(modalManager)
);
}
public DiscordContextMenu getContext(String name) {
for (DiscordContextMenu contextMenu : contextMenus) {
if (contextMenu.getContextMenuId().equalsIgnoreCase(name))
return contextMenu;
}
return null;
}
public List<DiscordContextMenu> getContexts() {
return contextMenus;
}
@Override
public void onUserContextInteraction(@Nonnull UserContextInteractionEvent event) {
String name = event.getInteraction().getName();
Optional<DiscordContextMenu> first = contextMenus.stream()
.filter(discordModal -> discordModal.getContextMenuId().equalsIgnoreCase(name))
.findFirst();
if (first.isEmpty()) {
event.replyEmbeds(new EmbedBuilder()
.setTitle("Invalid command")
.setDescription("Unable to process user context interaction with id: [" + name + "].")
.setColor(Color.RED)
.build())
.setEphemeral(true)
.queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
first.get().execute(event);
}
@Override
public void onMessageContextInteraction(@Nonnull MessageContextInteractionEvent event) {
String name = event.getInteraction().getName();
Optional<DiscordContextMenu> first = contextMenus.stream()
.filter(discordModal -> discordModal.getContextMenuId().equalsIgnoreCase(name))
.findFirst();
if (first.isEmpty()) {
event.replyEmbeds(new EmbedBuilder()
.setTitle("Invalid command")
.setDescription("Unable to process user context interaction with id: [" + name + "].")
.setColor(Color.RED)
.build())
.setEphemeral(true)
.queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
first.get().execute(event);
}
}

View File

@ -0,0 +1,17 @@
package com.alttd.contextMenuManager;
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.build.CommandData;
public abstract class DiscordContextMenu {
public abstract String getContextMenuId();
public abstract void execute(UserContextInteractionEvent event);
public abstract void execute(MessageContextInteractionEvent event);
public abstract CommandData getUserContextInteraction();
}

View File

@ -0,0 +1,90 @@
package com.alttd.contextMenuManager.contextMenus;
import com.alttd.contextMenuManager.DiscordContextMenu;
import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels;
import com.alttd.database.queries.commandOutputChannels.OutputType;
import com.alttd.modalManager.ModalManager;
import com.alttd.modalManager.modals.ModalReplySuggestion;
import com.alttd.util.Util;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.unions.IThreadContainerUnion;
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
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.components.Modal;
import net.dv8tion.jda.api.requests.RestAction;
public class ContextMenuRespondSuggestion extends DiscordContextMenu {
private final ModalManager modalManager;
public ContextMenuRespondSuggestion(ModalManager modalManager) {
this.modalManager = modalManager;
}
@Override
public String getContextMenuId() {
return "Respond To Suggestion";
}
@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) {
Message message = event.getInteraction().getTarget();
if (!isSuggestion(message)) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "This is not a suggestion"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
Modal replySuggestion = modalManager.getModalFor("reply_suggestion");
if (replySuggestion == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find reply suggestion modal"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
ModalReplySuggestion.putMessage(event.getUser().getIdLong(), message); //TODO find a better way to do this
event.replyModal(replySuggestion).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
}
@Override
public CommandData getUserContextInteraction() {
return Commands.message(getContextMenuId())
.setGuildOnly(true)
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR));
}
public boolean isSuggestion(Message message) {
GuildChannel channel = CommandOutputChannels.getOutputChannel(message.getGuild(), OutputType.SUGGESTION);
if (channel == null)
return false;
MessageChannelUnion messageChannel = message.getChannel();
if (channel.getType().equals(ChannelType.FORUM)) {
if (messageChannel.getType() != ChannelType.GUILD_PUBLIC_THREAD) {
return false;
}
ThreadChannel threadChannel = messageChannel.asThreadChannel();
IThreadContainerUnion parentChannel = threadChannel.getParentChannel();
if (!parentChannel.getType().equals(ChannelType.FORUM))
return false;
return message.getIdLong() == messageChannel.getIdLong() && message.getAuthor().equals(message.getJDA().getSelfUser());
} else {
return channel.equals(messageChannel);
}
}
}

View File

@ -2,6 +2,7 @@ package com.alttd.listeners;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.commandManager.CommandManager;
import com.alttd.contextMenuManager.ContextMenuManager;
import com.alttd.modalManager.ModalManager;
import com.alttd.request.RequestManager;
import com.alttd.util.Logger;
@ -26,8 +27,9 @@ public class JDAListener extends ListenerAdapter {
Logger.info("JDA ready to register commands.");
ButtonManager buttonManager = new ButtonManager();
ModalManager modalManager = new ModalManager(buttonManager);
CommandManager commandManager = new CommandManager(jda, modalManager);
jda.addEventListener(buttonManager, modalManager, commandManager);
ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager);
CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager);
jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager);
// RequestManager.init();
}

View File

@ -2,6 +2,7 @@ package com.alttd.modalManager;
import com.alttd.buttonManager.ButtonManager;
import com.alttd.modalManager.modals.ModalEvidence;
import com.alttd.modalManager.modals.ModalReplySuggestion;
import com.alttd.modalManager.modals.ModalSuggestion;
import com.alttd.util.Util;
import net.dv8tion.jda.api.EmbedBuilder;
@ -23,7 +24,8 @@ public class ModalManager extends ListenerAdapter {
public ModalManager(ButtonManager buttonManager) {
modals = List.of(
new ModalSuggestion(buttonManager),
new ModalEvidence());
new ModalEvidence(),
new ModalReplySuggestion());
}
@Override

View File

@ -0,0 +1,90 @@
package com.alttd.modalManager.modals;
import com.alttd.modalManager.DiscordModal;
import com.alttd.util.Util;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
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.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 java.util.HashMap;
public class ModalReplySuggestion extends DiscordModal {
private static final HashMap<Long, Message> 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);
}
@Override
public String getModalId() {
return "reply_suggestion";
}
@Override
public void execute(ModalInteractionEvent event) {
ModalMapping modalMapping = event.getInteraction().getValue("response");
if (modalMapping == null) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find response in modal"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
String response = modalMapping.getAsString();
if (response.isEmpty()) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "Response in modal is empty"))
.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 = 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);
return;
}
String[] split = message.getContentRaw().split("\u200B");
if (split.length == 0) {
event.replyEmbeds(Util.genericErrorEmbed("Error", "The suggestion to be edited has no content"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
return;
}
event.editMessage(split[0] + "\u200B\n\n" + "Response by: " + member.getAsMention() + response)
.queue(success -> event.replyEmbeds(Util.genericSuccessEmbed("Success", "Responded to the suggestion!"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure),
failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to edit the suggestion"))
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure));
}
@Override
public Modal getModal() {
TextInput body = TextInput.create("response", "Response", TextInputStyle.PARAGRAPH)
.setPlaceholder("Response...")
.setRequiredRange(10, 1024)
.setRequired(true)
.build();
return Modal.create(getModalId(), "Suggestion Response")
.addActionRows(ActionRow.of(body))
.build();
}
}