diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index 910be31..4e791b2 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -5,6 +5,7 @@ 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.listeners.ChatListener; import com.alttd.modalManager.ModalManager; import com.alttd.util.Logger; import net.dv8tion.jda.api.EmbedBuilder; @@ -30,7 +31,7 @@ public class CommandManager extends ListenerAdapter { private final List commands; private final HashMap> commandList = new HashMap<>(); - public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager) { + public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager, ChatListener chatListener) { commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0)))); loadCommands(); Logger.info("Loading commands..."); @@ -49,7 +50,8 @@ public class CommandManager extends ListenerAdapter { new CommandSeen(jda, this), commandSetToggleableRoles, new CommandToggleRole(commandSetToggleableRoles, jda, this), - new CommandRemindMe(jda, this, modalManager)); + new CommandRemindMe(jda, this, modalManager), + new CommandSoftLock(jda, this, chatListener)); } @Override diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSoftLock.java b/src/main/java/com/alttd/commandManager/commands/CommandSoftLock.java new file mode 100644 index 0000000..81bdb53 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandSoftLock.java @@ -0,0 +1,128 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.listeners.ChatListener; +import com.alttd.util.Util; +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.channel.concrete.TextChannel; +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.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 java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandSoftLock extends DiscordCommand { + + private final CommandData commandData; + private final ChatListener chatListener; + + public CommandSoftLock(JDA jda, CommandManager commandManager, ChatListener chatListener) { + this.chatListener = chatListener; + this.commandData = Commands.slash(getName(), "Auto delete all messages in a channel") + .addOption(OptionType.STRING, "state", "Set the soft lock \"on\" or \"off\"", true, true) + .addOption(OptionType.CHANNEL, "channel", "Channel to change soft lock state for", true) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)) + .setGuildOnly(true); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "softlock"; + } + + private final List states = List.of("on", "off"); + @Override + public void execute(SlashCommandInteractionEvent event) { + String state = event.getOption("state", OptionMapping::getAsString); + Guild guild = event.getGuild(); + if (guild == null || state == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command can only be used in a guild")) + .setEphemeral(true).queue(); + return; + } + GuildChannelUnion channel = event.getOption("channel", OptionMapping::getAsChannel); + if (channel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "No valid channel was specified")) + .setEphemeral(true).queue(); + return; + } + if (!channel.getType().isMessage()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "No valid text channel was specified")) + .setEphemeral(true).queue(); + return; + } + long guildId = event.getGuild().getIdLong(); + long channelId = event.getChannel().getIdLong(); + if (!event.getGuild().getSelfMember().getPermissions(channel).contains(Permission.MESSAGE_MANAGE)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "I can't manage messages in <#" + channelId + ">")) + .setEphemeral(true).queue(); + return; + } + state = state.toLowerCase(); + switch (state) { + case "on" -> { + if (chatListener.containsChannel(guildId, channelId)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "<#" + channelId + ">" + " is already locked")) + .setEphemeral(true).queue(); + break; + } + if (!chatListener.lockChannel(guildId, channelId)) + event.replyEmbeds(Util.genericErrorEmbed("Error", "<#" + channelId + ">" + " could not be locked")) + .setEphemeral(true).queue(); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Soft locked " + "<#" + channelId + ">" + "!")) + .setEphemeral(true).queue(); + } + case "off" -> { + if (!chatListener.containsChannel(guildId, channelId)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "<#" + channelId + ">" + " isn't locked")) + .setEphemeral(true).queue(); + break; + } + if (!chatListener.unlockChannel(guildId, channelId)) + event.replyEmbeds(Util.genericErrorEmbed("Error", "<#" + channelId + ">" + " could not be unlocked")) + .setEphemeral(true).queue(); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Unlocked " + "<#" + channelId + ">")) + .setEphemeral(true).queue(); + } + default -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid state")) + .setEphemeral(true).queue(); + } + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + String state = event.getOption("state", OptionMapping::getAsString); + Guild guild = event.getGuild(); + if (guild == null || state == null) { + event.replyChoiceStrings(new ArrayList<>()).queue(); + return; + } + String finalState = state.toLowerCase(); + event.replyChoiceStrings(states.stream() + .filter(s -> s.startsWith(finalState)) + .collect(Collectors.toList()) + ).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 b285402..24437ab 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -121,6 +121,20 @@ public class DatabaseTables { } } + private void createLockedChannelsTable() { + String sql = "CREATE TABLE IF NOT EXISTS locked_channels(" + + "guild_id BIGINT NOT NULL, " + + "channel_id BIGINT NOT NULL, " + + "PRIMARY KEY (channel_id)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create locked channels table, shutting down..."); + } + } + public static void createTables(Connection connection) { if (instance == null) instance = new DatabaseTables(connection); diff --git a/src/main/java/com/alttd/database/queries/QueriesLockedChannels.java b/src/main/java/com/alttd/database/queries/QueriesLockedChannels.java new file mode 100644 index 0000000..5eedc5a --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesLockedChannels.java @@ -0,0 +1,65 @@ +package com.alttd.database.queries; + +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 QueriesLockedChannels { + + public static boolean addLockedChannel(long guildId, long channelId) { + String sql = "INSERT INTO locked_channels (guild_id, channel_id) VALUES (?, ?)"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, guildId); + preparedStatement.setLong(2, channelId); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static boolean removeLockedChannel(long guildId, long channelId) { + String sql = "DELETE FROM locked_channels WHERE guild_id = ? AND channel_id = ?"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, guildId); + preparedStatement.setLong(2, channelId); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static HashMap> getLockedChannels() { + String sql = "SELECT * FROM locked_channels"; + try { + HashMap> map = new HashMap<>(); + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + ResultSet resultSet = preparedStatement.executeQuery(); + + while (resultSet.next()) { + long guildId = resultSet.getLong("guild_id"); + long channelId = resultSet.getLong("channel_id"); + HashSet channels = map.getOrDefault(guildId, new HashSet<>()); + channels.add(channelId); + map.put(guildId, channels); + } + + return map; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/com/alttd/listeners/ChatListener.java b/src/main/java/com/alttd/listeners/ChatListener.java new file mode 100644 index 0000000..3ee5712 --- /dev/null +++ b/src/main/java/com/alttd/listeners/ChatListener.java @@ -0,0 +1,63 @@ +package com.alttd.listeners; + +import com.alttd.database.queries.QueriesLockedChannels; +import com.alttd.util.Logger; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Objects; + +public class ChatListener extends ListenerAdapter { + + private final HashMap> lockedChannels; + + public ChatListener() { + HashMap> tmp = QueriesLockedChannels.getLockedChannels(); + lockedChannels = Objects.requireNonNullElseGet(tmp, HashMap::new); + if (tmp == null) + Logger.severe("Unable to load data from Locked Channels table"); + } + + public synchronized boolean lockChannel(long guildId, long channelId) { + HashSet channels = lockedChannels.getOrDefault(guildId, new HashSet<>()); + if (channels.contains(channelId)) + return false; + if (!QueriesLockedChannels.addLockedChannel(guildId, channelId)) + return false; + channels.add(channelId); + lockedChannels.put(guildId, channels); + return true; + } + + public synchronized boolean unlockChannel(long guildId, long channelId) { + if (!lockedChannels.containsKey(guildId)) + return false; + HashSet channels = lockedChannels.get(guildId); + if (!channels.contains(channelId)) + return false; + if (!QueriesLockedChannels.removeLockedChannel(guildId, channelId)) + return false; + channels.remove(channelId); + lockedChannels.put(guildId, channels); + return true; + } + + public synchronized boolean containsChannel(long guildId, long channelId) { + if (!lockedChannels.containsKey(guildId)) + return false; + HashSet channels = lockedChannels.get(guildId); + return channels.contains(channelId); + } + + @Override + public void onMessageReceived(@Nonnull MessageReceivedEvent event) { + long guildId = event.getGuild().getIdLong(); + long channelId = event.getChannel().getIdLong(); + if (containsChannel(guildId, channelId) && event.getMember() != null && !event.getMember().isOwner()) + event.getMessage().delete().queue(); + } + +} diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index d117d58..001ff6e 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -26,11 +26,12 @@ public class JDAListener extends ListenerAdapter { @Override public void onReady(@NotNull ReadyEvent event) { Logger.info("JDA ready to register commands."); + ChatListener chatListener = new ChatListener(); ButtonManager buttonManager = new ButtonManager(); ModalManager modalManager = new ModalManager(buttonManager); ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager); - CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager); - jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager); + CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, chatListener); + jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager, chatListener); ReminderScheduler reminderScheduler = ReminderScheduler.getInstance(jda); if (reminderScheduler == null) { Logger.severe("Unable to start reminder scheduler!");