diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index c186f27..a03bc7e 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -34,6 +34,7 @@ public class CommandManager extends ListenerAdapter { commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0)))); loadCommands(); Logger.info("Loading commands..."); + CommandSetToggleableRoles commandSetToggleableRoles = new CommandSetToggleableRoles(jda, this); commands = List.of( new CommandManage(jda, this, contextMenuManager), new CommandHelp(jda, this), @@ -43,7 +44,9 @@ public class CommandManager extends ListenerAdapter { new CommandUpdateCommands(jda, this), new CommandEvidence(jda, modalManager, this), new CommandFlag(jda, this), - new CommandHistory(jda, this)); + new CommandHistory(jda, this), + commandSetToggleableRoles, + new CommandToggleRole(commandSetToggleableRoles, jda, this)); } @Override diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java b/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java new file mode 100644 index 0000000..5179d1b --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java @@ -0,0 +1,147 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.database.queries.QueriesToggleableRoles; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +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.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.awt.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandSetToggleableRoles extends DiscordCommand { + + private final HashMap> guildToRolesMap; + private final CommandData commandData; + + public CommandSetToggleableRoles(JDA jda, CommandManager commandManager) { + guildToRolesMap = QueriesToggleableRoles.getToggleableRoles(); + + commandData = Commands.slash(getName(), "Set which roles can be toggled") + .addOption(OptionType.ROLE, "role", "The role you want to toggle on/off", false) + .setDefaultPermissions(DefaultMemberPermissions.DISABLED); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "settoggleableroles"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + List options = event.getInteraction().getOptions(); + if (options.size() == 0) { + String toggleableRoles = getToggleableRoles(guild); + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle("Active roles") + .setColor(Color.GREEN) + .setDescription(toggleableRoles) + .build(); + + event.replyEmbeds(messageEmbed).setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + OptionMapping optionMapping = options.get(0); + Role role = optionMapping.getAsRole(); + if (containsRole(role)) { + if (!QueriesToggleableRoles.removeRoleToggleable(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to remove role from the database")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + removeRole(role); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Removed " + role.getAsMention() + " from the toggleable roles")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } else { + if (role.hasPermission(Permission.ADMINISTRATOR) || + role.hasPermission(Permission.MANAGE_ROLES) || + role.hasPermission(Permission.MANAGE_CHANNEL) || + role.hasPermission(Permission.MANAGE_THREADS) || + role.hasPermission(Permission.MANAGE_WEBHOOKS) || + role.hasPermission(Permission.MANAGE_SERVER) || + role.hasPermission(Permission.MANAGE_PERMISSIONS) || + role.hasPermission(Permission.MESSAGE_MANAGE) || + role.hasPermission(Permission.MODERATE_MEMBERS)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "For safety reason this bot can not add roles which have a manage or moderator permission")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + if (!QueriesToggleableRoles.addRoleToggleable(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to store role in the database")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + addRole(role); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Added " + role.getAsMention() + " to the toggleable roles")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + } + + private void addRole(Role role) { + long guild = role.getGuild().getIdLong(); + HashSet set = guildToRolesMap.getOrDefault(guild, new HashSet<>()); + set.add(role.getIdLong()); + guildToRolesMap.put(guild, set); + } + + private void removeRole(Role role) { + long guild = role.getGuild().getIdLong(); + HashSet set = guildToRolesMap.getOrDefault(guild, new HashSet<>()); + if (set.isEmpty()) + return; + set.remove(role.getIdLong()); + guildToRolesMap.put(guild, set); + } + + public boolean containsRole(Role role) { + return guildToRolesMap.getOrDefault(role.getGuild().getIdLong(), new HashSet<>()).contains(role.getIdLong()); + } + + public String getToggleableRoles(Guild guild) { + HashSet roleIds = guildToRolesMap.get(guild.getIdLong()); + return guild.getRoles().stream() + .filter(role -> roleIds.contains(role.getIdLong())) + .map(Role::getAsMention) + .collect(Collectors.joining("\n")); + } + + @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/CommandToggleRole.java b/src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java new file mode 100644 index 0000000..2d7f261 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java @@ -0,0 +1,108 @@ +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.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +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.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.awt.*; +import java.util.Collections; +import java.util.List; + +public class CommandToggleRole extends DiscordCommand { + + private final CommandSetToggleableRoles commandSetToggleableRoles; + private final CommandData commandData; + + public CommandToggleRole(CommandSetToggleableRoles commandSetToggleableRoles, JDA jda, CommandManager commandManager) { + this.commandSetToggleableRoles = commandSetToggleableRoles; + commandData = Commands.slash(getName(), "Toggle a role") + .addOption(OptionType.ROLE, "role", "The role you want to toggle on/off (run the command without this option to see all available roles)", false) + .setDefaultPermissions(DefaultMemberPermissions.ENABLED); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "togglerole"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + List options = event.getInteraction().getOptions(); + if (options.size() == 0) { + String toggleableRoles = commandSetToggleableRoles.getToggleableRoles(guild); + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle("Toggleable roles") + .setColor(Color.GREEN) + .setDescription(toggleableRoles) + .build(); + + event.replyEmbeds(messageEmbed).setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + OptionMapping optionMapping = options.get(0); + Role role = optionMapping.getAsRole(); + if (!commandSetToggleableRoles.containsRole(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This role is not toggleable!")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + if (member.getRoles().contains(role)) { + guild.removeRoleFromMember(member, role).queue(success -> + event.replyEmbeds(Util.genericSuccessEmbed("Role removed", "You no longer have " + role.getAsMention() + ".")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + error -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to manage your roles.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } else { + guild.addRoleToMember(member, role).queue(success -> + event.replyEmbeds(Util.genericSuccessEmbed("Role add", "You now have " + role.getAsMention() + ".")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + error -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to manage your roles.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), 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/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 546dd78..d1c0b3f 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -65,7 +65,7 @@ public class DatabaseTables { connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); - Logger.severe("Unable to create polls table, shutting down..."); + Logger.severe("Unable to create commands table, shutting down..."); } } @@ -81,7 +81,21 @@ public class DatabaseTables { connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); - Logger.severe("Unable to create polls table, shutting down..."); + Logger.severe("Unable to create output channel table, shutting down..."); + } + } + + private void createToggleableRolesTable() { + String sql = "CREATE TABLE IF NOT EXISTS toggleable_roles(" + + "guild BIGINT NOT NULL, " + + "role BIGINT NOT NULL, " + + "PRIMARY KEY (guild, role)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create toggleable roles table, shutting down..."); } } diff --git a/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java b/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java new file mode 100644 index 0000000..e9464dd --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java @@ -0,0 +1,67 @@ +package com.alttd.database.queries; + +import com.alttd.database.Database; +import net.dv8tion.jda.api.entities.Role; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; + +public class QueriesToggleableRoles { + + public static boolean addRoleToggleable(Role role) { + String sql = "INSERT INTO toggleable_roles (guild, role) VALUES (?, ?)"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, role.getGuild().getIdLong()); + preparedStatement.setLong(2, role.getIdLong()); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static boolean removeRoleToggleable(Role role) { + String sql = "DELETE FROM toggleable_roles WHERE guild = ? AND role = ?"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, role.getGuild().getIdLong()); + preparedStatement.setLong(2, role.getIdLong()); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static HashMap> getToggleableRoles() { + String sql = "SELECT * FROM toggleable_roles"; + try { + HashMap> map = new HashMap<>(); + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + ResultSet resultSet = preparedStatement.executeQuery(); + + while (resultSet.next()) { + long guild = resultSet.getLong("guild"); + long role = resultSet.getLong("role"); + HashSet roles = map.getOrDefault(guild, new HashSet<>()); + roles.add(role); + map.put(guild, roles); + } + + return map; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return null; + } + +}