diff --git a/pom.xml b/pom.xml index 1450722..4dd727e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.xtremcommunity.discord netneutrality - 1.0.0 + 2.0.0 netneutrality-discord-bot Discord bot jar @@ -108,7 +108,7 @@ net.dv8tion JDA - 4.4.0_350 + 5.0.0-alpha.18 diff --git a/src/main/java/netneutrality/NetNeutrality.java b/src/main/java/netneutrality/NetNeutrality.java index 80e804d..95acf1e 100644 --- a/src/main/java/netneutrality/NetNeutrality.java +++ b/src/main/java/netneutrality/NetNeutrality.java @@ -9,9 +9,10 @@ import net.dv8tion.jda.api.requests.GatewayIntent; import netneutrality.core.CommandManager; import netneutrality.core.LogHandler; import netneutrality.db.DBController; -import netneutrality.handler.hooks.guilds.ServerJoinHandler; -import netneutrality.handler.hooks.message.EditHandler; -import netneutrality.handler.hooks.message.MessageReceivedHandler; +import netneutrality.hooks.SlashCommandHooks; +import netneutrality.hooks.guilds.ServerJoinHandler; +import netneutrality.hooks.message.EditHandler; +import netneutrality.hooks.message.MessageReceivedHandler; import netneutrality.http.server.WebApi; import netneutrality.music.MusicManager; import netneutrality.thread.BaseThreadFactory; @@ -37,8 +38,9 @@ public class NetNeutrality { options.setDsn(sentryDsn); options.setEnvironment(env); options.setTracesSampleRate(1.0); - options.setDebug(env.equals("dev")); - options.setRelease("netneutrality@2.0.0-alpha"); + //options.setDebug(env.equals("dev")); + options.setDebug(false); + options.setRelease("netneutrality@2.0.0"); }); if ("dev".equals(env)) { @@ -50,6 +52,7 @@ public class NetNeutrality { CommandManager.init(); MusicManager.initPlayerManager(); jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler()); + jda.addEventListener(new SlashCommandHooks()); new WebApi(Integer.parseInt(Settings.getInstance().get("API_PORT", "9000"))); jda.getPresence().setActivity(Activity.watching("you")); @@ -58,11 +61,10 @@ public class NetNeutrality { } public static void initJDA() { - // When upgrading JDA -> GatewayIntent.MESSAGE_CONTENT try { jda = JDABuilder .createDefault(Settings.getInstance().get("BOT_TOKEN")) - .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES) + .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES, GatewayIntent.MESSAGE_CONTENT) .setActivity(Activity.watching("me load...")) .build(); jda.awaitReady(); diff --git a/src/main/java/netneutrality/command/AbstractCommand.java b/src/main/java/netneutrality/command/AbstractCommand.java index 252a597..68575d2 100644 --- a/src/main/java/netneutrality/command/AbstractCommand.java +++ b/src/main/java/netneutrality/command/AbstractCommand.java @@ -2,17 +2,34 @@ package netneutrality.command; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import netneutrality.core.context.MessageCommandContext; public abstract class AbstractCommand implements ICommand { + @Override + public boolean isSlashCommand() { + return true; + } + + @Override + public DefaultMemberPermissions getSlashCommandPermission() { + return DefaultMemberPermissions.ENABLED; + } + @Override public boolean canBeExecutedBy(Guild guild, Member member) { return true; } @Override - public boolean canBeExecutedFrom(Guild guild, TextChannel channel) { + public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) { return true; } + + @Override + public void execute(MessageCommandContext context) { + // Prefer slash commands + } } diff --git a/src/main/java/netneutrality/command/AbstractMusicCommand.java b/src/main/java/netneutrality/command/AbstractMusicCommand.java index 44bbf20..61ec61a 100644 --- a/src/main/java/netneutrality/command/AbstractMusicCommand.java +++ b/src/main/java/netneutrality/command/AbstractMusicCommand.java @@ -6,10 +6,7 @@ package netneutrality.command; * https://discord.gg/CdFcyQ */ -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.*; import netneutrality.core.GuildManager; import netneutrality.db.GSettings; import netneutrality.utils.UserFunction; @@ -21,6 +18,11 @@ public abstract class AbstractMusicCommand extends AbstractCommand { return false; } + @Override + public boolean isGuildOnly() { + return true; + } + @Override public CommandType getType() { return CommandType.MUSIC; @@ -49,7 +51,10 @@ public abstract class AbstractMusicCommand extends AbstractCommand { } @Override - public boolean canBeExecutedFrom(Guild guild, TextChannel channel) { + public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) { + if (channel.getType() != ChannelType.TEXT) + return false; + String channelDb = GuildManager.getManager(guild).getSetting(GSettings.MUSIC_CHANNEL); if (channelDb != null && !channelDb.isEmpty()) { diff --git a/src/main/java/netneutrality/command/CommandUsage.java b/src/main/java/netneutrality/command/CommandUsage.java deleted file mode 100644 index b5b82dc..0000000 --- a/src/main/java/netneutrality/command/CommandUsage.java +++ /dev/null @@ -1,103 +0,0 @@ -package netneutrality.command; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class CommandUsage { - - public static class Argument { - public enum Type { - OPTIONAL, REQUIRED - } - - private final Type type; - private final String[] values; - private final String description; - - public Argument(Type t, String[] values, String desc) { - if (t == Type.REQUIRED && (values == null || values.length == 0)) - throw new IllegalArgumentException("Command usage: values can't be null if type is required"); - - if (t == Type.OPTIONAL && (desc == null || desc.isEmpty())) - throw new IllegalArgumentException("Command usage: description can't be null if type is optional"); - - this.type = t; - this.values = values; - this.description = desc; - } - - public String[] getValues() { - return values; - } - - public String getDescription() { - return description; - } - - public Type getType() { - return type; - } - } - - private final AbstractCommand command; - private final Set args = new HashSet<>(); - - public CommandUsage(AbstractCommand command, Argument... arguments) { - this.command = command; - Collections.addAll(args, arguments); - } - - public CommandUsage addArgument(Argument argument) { - args.add(argument); - return this; - } - - public static CommandUsage create(AbstractCommand command, Argument... arguments) { - return new CommandUsage(command, arguments); - } - - public AbstractCommand getCommand() { - return command; - } - - public int getRequiredArguments() { - return args.size(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Available arguments: ").append(args.size()).append("\n"); - sb.append(command.getCommandName()).append(" "); - for (Argument arg : args) { - sb.append("<"); - if (arg.getType() == Argument.Type.OPTIONAL) { - sb.append("optional: "); - - if (arg.getValues() != null || arg.getValues().length != 0) - sb.append(Arrays.toString(arg.getValues())).append(": "); - - sb.append(arg.getDescription()); - } - - if (arg.getType() == Argument.Type.REQUIRED) { - sb.append("required: ").append(Arrays.toString(arg.getValues())); - - if (arg.getDescription() != null && !arg.getDescription().isEmpty()) - sb.append(": ").append(arg.getDescription()); - } - - sb.append("> "); - } - - return sb.toString(); - } -} diff --git a/src/main/java/netneutrality/command/ICommand.java b/src/main/java/netneutrality/command/ICommand.java index 91fed50..bb8b86c 100644 --- a/src/main/java/netneutrality/command/ICommand.java +++ b/src/main/java/netneutrality/command/ICommand.java @@ -8,51 +8,37 @@ package netneutrality.command; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.TextChannel; -import netneutrality.core.context.CommandContext; -import netneutrality.exceptions.CommandErrorException; -import netneutrality.exceptions.CommandExecutionException; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; public interface ICommand { - /** - * @return if the command needs administrator privilege - */ boolean needAdmin(); - /** - * @return if the command should be loaded at runtime - */ boolean isEnabled(); - /** - * The command name that a user will write in a channel - * - * @return the name of the command. - */ + boolean isGuildOnly(); + + boolean isSlashCommand(); + String getCommandName(); - /** - * @return all the aliases the command has - */ - String[] getAlias(); - - CommandUsage getUsage(); - String getDescription(); + DefaultMemberPermissions getSlashCommandPermission(); + + void registerSlashCommandOptions(SlashCommandData data); + CommandType getType(); boolean canBeExecutedBy(Guild guild, Member member); - boolean canBeExecutedFrom(Guild guild, TextChannel channel); + boolean canBeExecutedFrom(Guild guild, MessageChannel channel); - /** - * Command is executed with this function. - * - * @param context The context for the command - * @throws CommandErrorException Used to reply with a message in the text channel where the command was executed - * @throws CommandExecutionException Logs the error internally, without sending a message - */ - void execute(CommandContext context) throws CommandErrorException, CommandExecutionException; + void execute(MessageCommandContext context); + + void execute(SlashCommandContext context); } diff --git a/src/main/java/netneutrality/command/Visibility.java b/src/main/java/netneutrality/command/Visibility.java deleted file mode 100644 index 71b7d73..0000000 --- a/src/main/java/netneutrality/command/Visibility.java +++ /dev/null @@ -1,30 +0,0 @@ -package netneutrality.command; - -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.entities.PrivateChannel; -import net.dv8tion.jda.api.entities.TextChannel; - -public enum Visibility { - - PRIVATE, CHANNEL, BOTH; - - public static boolean check(MessageChannel channel, Visibility v) { - if (v == BOTH && (channel instanceof TextChannel || channel instanceof PrivateChannel)) - return true; - - if (v == PRIVATE && channel instanceof PrivateChannel) - return true; - - if (v == CHANNEL && channel instanceof TextChannel) - return true; - - return false; - } -} diff --git a/src/main/java/netneutrality/command/admin/BlacklistCommand.java b/src/main/java/netneutrality/command/admin/BlacklistCommand.java new file mode 100644 index 0000000..6762834 --- /dev/null +++ b/src/main/java/netneutrality/command/admin/BlacklistCommand.java @@ -0,0 +1,111 @@ +package netneutrality.command.admin; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +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.SlashCommandData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import netneutrality.command.AbstractCommand; +import netneutrality.command.CommandType; +import netneutrality.core.LogHandler; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; +import netneutrality.db.DBController; +import netneutrality.db.controllers.DBBlacklist; +import netneutrality.db.entities.OBlacklist; + +import java.util.ArrayList; + +public class BlacklistCommand extends AbstractCommand { + + @Override + public boolean needAdmin() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean isGuildOnly() { + return true; + } + + @Override + public String getCommandName() { + return "blacklist"; + } + + @Override + public String getDescription() { + return "Manages the blacklist of your server."; + } + + @Override + public DefaultMemberPermissions getSlashCommandPermission() { + return DefaultMemberPermissions.DISABLED; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + data.addSubcommands( + new SubcommandData("get", "Get all blacklisted words for this guild"), + new SubcommandData("add", "Add a word to the blacklist") + .addOption(OptionType.STRING, "word", "Word to add"), + new SubcommandData("remove", "Remove a word from the blacklist") + .addOption(OptionType.STRING, "word", "Word to remove") + ); + } + + @Override + public CommandType getType() { + return CommandType.ADMINISTRATION; + } + + @Override + public void execute(MessageCommandContext context) { + + } + + @Override + public void execute(SlashCommandContext context) { + String sub = context.getEvent().getCommandPath().split("/")[1]; + DBBlacklist controller = DBController.get(DBBlacklist.class); + String word = context.getEvent().getOption("word", "", OptionMapping::getAsString); + context.ackAndWait(true); + + switch (sub) { + case "get" -> { + + context.getHook().editOriginal("Retrieving list, please wait...").queue(t -> { + ArrayList blacklists = new ArrayList<>(); + try { + blacklists = controller.getBlacklistForGuild(context.getGuild()); + } catch (Exception e) { + LogHandler.log(e); + } + + StringBuilder sb = new StringBuilder(); + for (OBlacklist bl : blacklists) { + sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n"); + } + + t.editMessage("List of blacklisted words for this server [" + context.getGuild().getName() + "] :\n" + (blacklists.isEmpty() ? "No word found" : sb.toString())).queue(); + + }); + } + case "add" -> controller.add(word, context); + case "remove" -> controller.delete(word, context); + case "disable" -> controller.disable(word, context); + case "enable" -> controller.enable(word, context); + } + } +} diff --git a/src/main/java/netneutrality/command/admin/PurgeCommand.java b/src/main/java/netneutrality/command/admin/PurgeCommand.java new file mode 100644 index 0000000..a4ddd6d --- /dev/null +++ b/src/main/java/netneutrality/command/admin/PurgeCommand.java @@ -0,0 +1,95 @@ +package netneutrality.command.admin; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import net.dv8tion.jda.api.entities.GuildMessageChannel; +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.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import netneutrality.command.AbstractCommand; +import netneutrality.command.CommandType; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; +import netneutrality.utils.ChannelMessagePurger; + +public class PurgeCommand extends AbstractCommand { + + @Override + public boolean needAdmin() { + return true; + } + + @Override + public boolean isEnabled() { + return false; + } + + @Override + public boolean isGuildOnly() { + return true; + } + + @Override + public String getCommandName() { + return "purge"; + } + + @Override + public String getDescription() { + return "Delete messages from the channel you are currently on"; + } + + @Override + public DefaultMemberPermissions getSlashCommandPermission() { + return DefaultMemberPermissions.DISABLED; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + + data.addOptions( + new OptionData(OptionType.INTEGER, "to-purge", "Number of messages to delete (max 2500)", true) + .setRequiredRange(1, 2500)); + } + + @Override + public CommandType getType() { + return CommandType.ADMINISTRATION; + } + + @Override + public void execute(MessageCommandContext context) { + if (context.getArgs().length == 1) { + GuildMessageChannel messageChannel = context.getEvent().getGuildChannel(); + ChannelMessagePurger channelMessagePurger = ChannelMessagePurger.getPurger(messageChannel); + channelMessagePurger.purgeMessages(context, Integer.parseInt(context.getArgument(0))); + } else { + context.getChannel().sendMessage("Failed to delete messages!").queue(); + } + } + + @Override + public void execute(SlashCommandContext context) { + int toDelete = context.getEvent().getOption("to-purge", 0, OptionMapping::getAsInt); + context.ackAndWait(true); + + if (toDelete > 0) { + GuildMessageChannel messageChannel = context.getEvent().getGuildChannel(); + ChannelMessagePurger channelMessagePurger = ChannelMessagePurger.getPurger(messageChannel); + boolean lock = channelMessagePurger.canLockBeAcquired(); + channelMessagePurger.purgeMessages(context, toDelete); + + context.replyEphemeral("Processing purge command.. This can take some time."); + + if (!lock) { + context.replyEphemeral("Another purge is currently in progress on this channel, please wait before purging again"); + } + } + } +} diff --git a/src/main/java/netneutrality/command/basic/HelpCommand.java b/src/main/java/netneutrality/command/basic/HelpCommand.java index 94e9e2f..8ec34f4 100644 --- a/src/main/java/netneutrality/command/basic/HelpCommand.java +++ b/src/main/java/netneutrality/command/basic/HelpCommand.java @@ -6,15 +6,14 @@ package netneutrality.command.basic; * https://discord.gg/CdFcyQ */ -import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import netneutrality.command.AbstractCommand; import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.CommandManager; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.utils.MessageFunction; -import netneutrality.utils.UserFunction; import java.util.List; @@ -30,59 +29,47 @@ public class HelpCommand extends AbstractCommand { return true; } + @Override + public boolean isGuildOnly() { + return false; + } + @Override public String getCommandName() { return "help"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"command"}, "Name of command to get help with") - ); - } - @Override public String getDescription() { return "Get every command with its usage"; } + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + + } + @Override public CommandType getType() { return CommandType.UTILITY; } @Override - public void execute(CommandContext context) { + public void execute(MessageCommandContext context) { if (context.getArgs().length == 0) { - List help = MessageFunction.buildHelpMessage(CommandManager.commands, UserFunction.isAdmin(context.getGuildUser())); + List help = MessageFunction.buildHelpMessage(context.getGuildUser()); - context.reply(new MessageBuilder().append(context.getDiscordUser()).append(" help has been sent your way!").build()); + context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build()); context.replyPrivate(help); } - if (context.getArgs().length == 1) { - String cmd = context.getArgument(0); - - if (CommandManager.commands.containsKey(cmd) || CommandManager.commandsAlias.containsKey(cmd)) { - AbstractCommand c = CommandManager.commands.containsKey(cmd) ? CommandManager.commands.get(cmd) : CommandManager.commandsAlias.get(cmd); - - if (c.needAdmin() && !UserFunction.isAdmin(context.getGuildUser())) { - context.replyPrivate("Command [" + c.getCommandName() + "] requires admin privileges"); - } - - MessageEmbed help = MessageFunction.buildHelpMessage(c); - context.reply(new MessageBuilder().append(context.getDiscordUser()).append(" help has been sent your way!").build()); - context.replyPrivate(help); - } - } - + } + @Override + public void execute(SlashCommandContext context) { + List help = MessageFunction.buildHelpMessage(context.getGuildUser()); + context.replyEphemeral("Help has been sent in DM!"); + context.sendMessageInPrivate(help); } } diff --git a/src/main/java/netneutrality/command/basic/PingCommand.java b/src/main/java/netneutrality/command/basic/PingCommand.java index 6551022..8a943a9 100644 --- a/src/main/java/netneutrality/command/basic/PingCommand.java +++ b/src/main/java/netneutrality/command/basic/PingCommand.java @@ -6,10 +6,11 @@ package netneutrality.command.basic; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractCommand; import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; public class PingCommand extends AbstractCommand { @@ -23,35 +24,40 @@ public class PingCommand extends AbstractCommand { return true; } + @Override + public boolean isGuildOnly() { + return false; + } + @Override public String getCommandName() { return "ping"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { return "Pong!"; } + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + + } + @Override public CommandType getType() { return CommandType.UTILITY; } @Override - public void execute(CommandContext context) { + public void execute(MessageCommandContext context) { if (context.getArgs().length == 0) context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing())); } + @Override + public void execute(SlashCommandContext context) { + context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing())); + } + } diff --git a/src/main/java/netneutrality/command/bot/ConfigureCommand.java b/src/main/java/netneutrality/command/bot/ConfigureCommand.java index a038154..42b1cc8 100644 --- a/src/main/java/netneutrality/command/bot/ConfigureCommand.java +++ b/src/main/java/netneutrality/command/bot/ConfigureCommand.java @@ -8,18 +8,27 @@ package netneutrality.command.bot; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +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.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import netneutrality.command.AbstractCommand; import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; import netneutrality.core.GuildManager; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.db.GSettings; import netneutrality.music.MusicManager; -import java.util.Arrays; import java.util.List; +import java.util.Map; public class ConfigureCommand extends AbstractCommand { + @Override public boolean needAdmin() { return true; @@ -30,21 +39,14 @@ public class ConfigureCommand extends AbstractCommand { return true; } + @Override + public boolean isGuildOnly() { + return true; + } + @Override public String getCommandName() { - return "configure"; - } - - @Override - public String[] getAlias() { - return new String[]{"conf"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"music-role", "music-channel", "music-volume", "music-topic", "debug"}, "") - ); + return "config"; } @Override @@ -52,13 +54,33 @@ public class ConfigureCommand extends AbstractCommand { return "Used to configure the bot on this server"; } + @Override + public DefaultMemberPermissions getSlashCommandPermission() { + return DefaultMemberPermissions.DISABLED; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + data.addSubcommands( + new SubcommandData("music-volume", "Volume for the music player") + .addOptions(new OptionData(OptionType.INTEGER, "volume", "Integer between 0 and 100", true) + .setRequiredRange(0, 100)), + new SubcommandData("error-logging", "Send errors to #error and/or #log channel") + .addOption(OptionType.BOOLEAN, "debug", "Enable or disable the fonctionnality", true), + new SubcommandData("music-role", "Role needed to use music commands") + .addOption(OptionType.ROLE, "role", "Name of role", true), + new SubcommandData("music-channel", "Only allow music commands on this channel") + .addOption(OptionType.CHANNEL, "channel", "Leave empty to allow everywhere") + ); + } + @Override public CommandType getType() { return CommandType.ADMINISTRATION; } @Override - public void execute(CommandContext context) { + public void execute(MessageCommandContext context) { String[] args = context.getArgs(); GuildManager guildManager = GuildManager.getManager(context.getGuild()); @@ -86,7 +108,7 @@ public class ConfigureCommand extends AbstractCommand { context.reply("Successful reset of the role"); } - List roles = context.getMessage().getMentionedRoles(); + List roles = context.getMessage().getMentions().getRoles(); if (roles.size() == 1) { Role role = roles.get(0); if (role.getGuild().getId().equals(context.getGuild().getId())) { @@ -119,7 +141,7 @@ public class ConfigureCommand extends AbstractCommand { context.reply("Successful reset of the channel"); } - List channels = context.getMessage().getMentionedChannels(); + List channels = context.getMessage().getMentions().getChannels(TextChannel.class); if (channels.size() == 1) { TextChannel channel = channels.get(0); if (channel.getGuild().getId().equals(context.getGuild().getId())) { @@ -157,28 +179,6 @@ public class ConfigureCommand extends AbstractCommand { } } - // Music topic change - if (context.getArgument(0).equals("music-topic")) { - if (args.length == 1) { - boolean enabled = "1".equals(guildManager.getSetting(GSettings.MUSIC_TOPIC)); - - context.reply("Set currently playing track in text channel topic: %s", (enabled ? "enabled" : "disabled")); - context.reply("Use 'conf music-topic 0/1' to toggle between states (0 = disabled, 1 = enabled)."); - } - - if (args.length == 2) { - String str = args[1]; - boolean enabled = "1".equals(guildManager.getSetting(GSettings.MUSIC_TOPIC)); - if (str.equals("0") || str.equals("1")) { - guildManager.setSettingValue(GSettings.MUSIC_TOPIC, str); - enabled = "1".equals(str); - } - - context.reply("Set currently playing track in text channel topic is now %s for this guild", (enabled ? "enabled" : "disabled")); - - } - } - // Debug if (context.getArgument(0).equals("debug")) { if (args.length == 1) { @@ -204,34 +204,84 @@ public class ConfigureCommand extends AbstractCommand { } } + } + } - // Prefix - if (context.getArgument(0).equals("prefix")) { - String allowedPrefixes = "!$%?&*"; + @Override + public void execute(SlashCommandContext context) { + String sub = context.getEvent().getCommandPath().split("/")[1]; + GuildManager guildManager = GuildManager.getManager(context.getGuild()); + context.ackAndWait(true); - if (args.length == 1) { - String prefix = guildManager.getSetting(GSettings.PREFIX); + if ("music-role".equals(sub)) { + Role role = context.getEvent().getOption("role", OptionMapping::getAsRole); + assert role != null; - context.reply("Current prefix for commands: '%s'", prefix); - context.reply("Allowed prefixes: %s", Arrays.toString(allowedPrefixes.toCharArray())); + if (role.getPosition() == -1) { + guildManager.setSettingValue(GSettings.MUSIC_ROLE, null); + context.replyEphemeral("Everyone can now use music commands"); + return; + } - } + if (role.getGuild().getId().equals(context.getGuild().getId())) { + guildManager.setSettingValue(GSettings.MUSIC_ROLE, role.getId()); + context.replyEphemeral("Successfully set %s as the role needed for music command", role.getAsMention()); + return; + } + } - if (args.length == 2) { - String prefix = args[1].trim(); + if ("music-channel".equals(sub)) { + GuildChannelUnion channel = context.getEvent().getOption("channel", OptionMapping::getAsChannel); - if (prefix.length() == 1 && allowedPrefixes.contains(prefix)) { - guildManager.setSettingValue(GSettings.PREFIX, prefix); - - context.reply("Prefix for commands is now '%s'", prefix); - } else { - context.reply("Allowed prefixes: %s", Arrays.toString(allowedPrefixes.toCharArray())); - } + if (channel == null) { + guildManager.setSettingValue(GSettings.MUSIC_CHANNEL, null); + context.replyEphemeral("No channel restrictions for music commands"); + return; + } + if (channel instanceof TextChannel) { + if (channel.getGuild().getId().equals(context.getGuild().getId())) { + guildManager.setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId()); + context.replyEphemeral("Successfully set %s as the channel required to execute music command", channel.getAsMention()); + return; } } - } else { - context.reply(getUsage()); + + } + + if ("music-volume".equals(sub)) { + int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt); + guildManager.setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume)); + MusicManager.get(context.getGuild()).setVolume(volume); + + context.replyEphemeral("Default volume for music player is now %d", volume); + return; + } + + if ("debug".equals(sub)) { + boolean debug = context.getEvent().getOption("debug", true, OptionMapping::getAsBoolean); + + guildManager.setDebug(debug); + context.reply("Error logging is now %s for this guild", (debug ? "enabled" : "disabled")); + return; + } + + if ("show-all".equals(sub)) { + Map settings = guildManager.getSettings(); + MessageCreateBuilder builder = new MessageCreateBuilder(); + + for (GSettings o : GSettings.values()) { + String value = o.getDefaultValue(); + + if (settings.containsKey(o.getName())) { + value = settings.get(o.getName()); + } + + builder.addContent(o.getName()).addContent(": ").addContent(value); + builder.addContent("\n"); + } + + context.replyEphemeral(builder.build()); } } } diff --git a/src/main/java/netneutrality/command/crypto/BitcoinPriceCommand.java b/src/main/java/netneutrality/command/crypto/BitcoinPriceCommand.java deleted file mode 100644 index c30adaf..0000000 --- a/src/main/java/netneutrality/command/crypto/BitcoinPriceCommand.java +++ /dev/null @@ -1,101 +0,0 @@ -package netneutrality.command.crypto; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.LogHandler; -import netneutrality.core.context.CommandContext; -import netneutrality.http.WebModule; -import org.json.JSONException; -import org.json.JSONObject; - -import java.text.NumberFormat; -import java.util.Currency; -import java.util.Locale; - -public class BitcoinPriceCommand extends AbstractCommand { - - private final String baseURL = "https://min-api.cryptocompare.com/data/price"; - private final String crypto = "BTC"; - - @Override - public boolean needAdmin() { - return false; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public String getCommandName() { - return "bitcoin"; - } - - @Override - public String[] getAlias() { - return new String[]{"btc"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"currency"}, "defaults to USD") - ); - } - - @Override - public String getDescription() { - return "Get the price of bitcoin"; - } - - @Override - public CommandType getType() { - return CommandType.CRYPTO; - } - - @Override - public void execute(CommandContext context) { - String[] args = context.getArgs(); - String currency = "USD"; - - if (args.length == 1) { - try { - currency = Currency.getInstance(args[0].toUpperCase()).getCurrencyCode(); - } catch (IllegalArgumentException exception) { - context.reply("Please enter a valid currency"); - return; - } - } - - String finalCurrency = currency; - context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> { - String price; - Locale usa = new Locale("en", "US"); - NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa); - dollarFormat.setMaximumFractionDigits(24); - - try { - WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request(); - JSONObject json = http.getAsObject(); - if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) { - t.editMessage(String.format("Failed getting price of %s", crypto)).queue(); - return; - } - - price = dollarFormat.format(json.getDouble(finalCurrency)); - t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue(); - } catch (JSONException | IllegalArgumentException | ArithmeticException e) { - t.editMessage(String.format("Error getting price of %s!", crypto)).queue(); - LogHandler.log(e); - } - }); - } -} diff --git a/src/main/java/netneutrality/command/crypto/CryptoPriceCommand.java b/src/main/java/netneutrality/command/crypto/CryptoPriceCommand.java index bce1e27..ffa0098 100644 --- a/src/main/java/netneutrality/command/crypto/CryptoPriceCommand.java +++ b/src/main/java/netneutrality/command/crypto/CryptoPriceCommand.java @@ -6,11 +6,14 @@ package netneutrality.command.crypto; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractCommand; import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; import netneutrality.core.LogHandler; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.http.WebModule; import org.json.JSONException; import org.json.JSONObject; @@ -33,36 +36,34 @@ public class CryptoPriceCommand extends AbstractCommand { return true; } + @Override + public boolean isGuildOnly() { + return false; + } + @Override public String getCommandName() { return "crypto"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"crypto"}, "Symbol of crypto"), - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"currency"}, "defaults to USD") - ); - } - @Override public String getDescription() { return "Get the price of cryptocurrencies"; } + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + data.addOption(OptionType.STRING, "symbol", "The crypto symbol to fetch price for", true); + data.addOption(OptionType.STRING, "currency", "In which currency should the price be displayed"); + } + @Override public CommandType getType() { return CommandType.CRYPTO; } @Override - public void execute(CommandContext context) { + public void execute(MessageCommandContext context) { String[] args = context.getArgs(); String currency = "USD"; @@ -102,4 +103,46 @@ public class CryptoPriceCommand extends AbstractCommand { }); } } + + @Override + public void execute(SlashCommandContext context) { + Locale usa = new Locale("en", "US"); + NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa); + dollarFormat.setMaximumFractionDigits(24); + + String symbol = context.getEvent().getOption("symbol", OptionMapping::getAsString); + String currency = context.getEvent().getOption("currency", "USD", OptionMapping::getAsString); + + try { + currency = Currency.getInstance(currency).getCurrencyCode(); + } catch (IllegalArgumentException exception) { + context.reply("Please enter a valid currency"); + return; + } + + String finalCurrency = currency.toUpperCase(); + context.getEvent().deferReply().queue(interactionHook -> { + try { + + WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, symbol, finalCurrency)).request(); + JSONObject json = http.getAsObject(); + + if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) { + interactionHook.editOriginal("Crypto doesn't exist.. yet").queue(); + return; + } + + double value = json.getDouble(finalCurrency); + String price = dollarFormat.format(value); + + interactionHook.editOriginal(String.format("The current price of %s in %s is %s", symbol, finalCurrency, price)).queue(); + + } catch (JSONException | IllegalArgumentException | ArithmeticException e) { + interactionHook.editOriginal(String.format("Error getting price of %s!", symbol)).queue(); + LogHandler.log(e); + } + + + }); + } } diff --git a/src/main/java/netneutrality/command/crypto/EthereumPriceCommand.java b/src/main/java/netneutrality/command/crypto/EthereumPriceCommand.java deleted file mode 100644 index a29ccf8..0000000 --- a/src/main/java/netneutrality/command/crypto/EthereumPriceCommand.java +++ /dev/null @@ -1,101 +0,0 @@ -package netneutrality.command.crypto; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.LogHandler; -import netneutrality.core.context.CommandContext; -import netneutrality.http.WebModule; -import org.json.JSONException; -import org.json.JSONObject; - -import java.text.NumberFormat; -import java.util.Currency; -import java.util.Locale; - -public class EthereumPriceCommand extends AbstractCommand { - - private final String baseURL = "https://min-api.cryptocompare.com/data/price"; - private final String crypto = "ETH"; - - @Override - public boolean needAdmin() { - return false; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public String getCommandName() { - return "ethereum"; - } - - @Override - public String[] getAlias() { - return new String[]{"eth"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"currency"}, "defaults to USD") - ); - } - - @Override - public String getDescription() { - return "Get the ethereum price"; - } - - @Override - public CommandType getType() { - return CommandType.CRYPTO; - } - - @Override - public void execute(CommandContext context) { - String[] args = context.getArgs(); - String currency = "USD"; - - if (args.length == 1) { - try { - currency = Currency.getInstance(args[0].toUpperCase()).getCurrencyCode(); - } catch (IllegalArgumentException exception) { - context.reply("Please enter a valid currency"); - return; - } - } - - String finalCurrency = currency; - context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> { - String price; - Locale usa = new Locale("en", "US"); - NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa); - dollarFormat.setMaximumFractionDigits(24); - - try { - WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request(); - JSONObject json = http.getAsObject(); - if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) { - t.editMessage(String.format("Failed getting price of %s", crypto)).queue(); - return; - } - - price = dollarFormat.format(json.getDouble(finalCurrency)); - t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue(); - } catch (JSONException | IllegalArgumentException e) { - t.editMessage(String.format("Error getting price of %s!", crypto)).queue(); - LogHandler.log(e); - } - }); - } -} diff --git a/src/main/java/netneutrality/command/crypto/LitecoinPriceCommand.java b/src/main/java/netneutrality/command/crypto/LitecoinPriceCommand.java deleted file mode 100644 index c782d67..0000000 --- a/src/main/java/netneutrality/command/crypto/LitecoinPriceCommand.java +++ /dev/null @@ -1,101 +0,0 @@ -package netneutrality.command.crypto; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.LogHandler; -import netneutrality.core.context.CommandContext; -import netneutrality.http.WebModule; -import org.json.JSONException; -import org.json.JSONObject; - -import java.text.NumberFormat; -import java.util.Currency; -import java.util.Locale; - -public class LitecoinPriceCommand extends AbstractCommand { - - private final String baseURL = "https://min-api.cryptocompare.com/data/price"; - private final String crypto = "LTC"; - - @Override - public boolean needAdmin() { - return false; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public String getCommandName() { - return "litecoin"; - } - - @Override - public String[] getAlias() { - return new String[]{"ltc"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"currency"}, "defaults to USD") - ); - } - - @Override - public String getDescription() { - return "Get the litecoin price"; - } - - @Override - public CommandType getType() { - return CommandType.CRYPTO; - } - - @Override - public void execute(CommandContext context) { - String[] args = context.getArgs(); - String currency = "USD"; - - if (args.length == 1) { - try { - currency = Currency.getInstance(args[0].toUpperCase()).getCurrencyCode(); - } catch (IllegalArgumentException exception) { - context.reply("Please enter a valid currency"); - return; - } - } - - String finalCurrency = currency; - context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> { - String price; - Locale usa = new Locale("en", "US"); - NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa); - dollarFormat.setMaximumFractionDigits(24); - - try { - WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request(); - JSONObject json = http.getAsObject(); - if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) { - t.editMessage(String.format("Failed getting price of %s", crypto)).queue(); - return; - } - - price = dollarFormat.format(json.getDouble(finalCurrency)); - t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue(); - } catch (JSONException | IllegalArgumentException | ArithmeticException e) { - t.editMessage(String.format("Error getting price of %s!", crypto)).queue(); - LogHandler.log(e); - } - }); - } -} diff --git a/src/main/java/netneutrality/command/message/BlacklistCommand.java b/src/main/java/netneutrality/command/message/BlacklistCommand.java deleted file mode 100644 index d442c2e..0000000 --- a/src/main/java/netneutrality/command/message/BlacklistCommand.java +++ /dev/null @@ -1,93 +0,0 @@ -package netneutrality.command.message; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.LogHandler; -import netneutrality.core.context.CommandContext; -import netneutrality.db.DBController; -import netneutrality.db.controllers.DBBlacklist; -import netneutrality.db.entities.OBlacklist; - -import java.util.ArrayList; - -public class BlacklistCommand extends AbstractCommand { - - @Override - public boolean needAdmin() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public String getCommandName() { - return "blacklist"; - } - - @Override - public String[] getAlias() { - return new String[]{"bl"}; - } - - @Override - public CommandUsage getUsage() { - return null; - } - - @Override - public String getDescription() { - return "Manages the blacklist of your server."; - } - - @Override - public CommandType getType() { - return CommandType.ADMINISTRATION; - } - - @Override - public void execute(CommandContext context) { - String[] args = context.getArgs(); - DBBlacklist controller = DBController.get(DBBlacklist.class); - - if (args.length > 0 && args.length < 3) { - String word = (args.length == 2 ? args[1] : ""); - - switch (args[0]) { - case "get" -> { - - context.getChannel().sendMessage("Retrieving list, please wait...").queue(t -> { - ArrayList blacklists = new ArrayList<>(); - try { - blacklists = controller.getBlacklistForGuild(context); - } catch (Exception e) { - LogHandler.log(e); - } - - StringBuilder sb = new StringBuilder(); - for (OBlacklist bl : blacklists) { - sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n"); - } - - t.editMessage("List of blacklisted words for this server [" + context.getGuild().getName() + "] :\n" + (blacklists.isEmpty() ? "No word found" : sb.toString())).queue(); - - }); - } - case "add" -> controller.add(word, context); - case "remove" -> controller.delete(word, context); - case "disable" -> controller.disable(word, context); - case "enable" -> controller.enable(word, context); - } - } - } - -} diff --git a/src/main/java/netneutrality/command/message/PruneCommand.java b/src/main/java/netneutrality/command/message/PruneCommand.java deleted file mode 100644 index 6cc7848..0000000 --- a/src/main/java/netneutrality/command/message/PruneCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -package netneutrality.command.message; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; -import netneutrality.utils.ChannelFunction; - -public class PruneCommand extends AbstractCommand { - - @Override - public boolean needAdmin() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public String getCommandName() { - return "prune"; - } - - @Override - public String[] getAlias() { - return new String[]{"purge"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"number"}, "Max 2499") - ); - } - - @Override - public String getDescription() { - return "Delete n messages from the channel you are currently on"; - } - - @Override - public CommandType getType() { - return CommandType.ADMINISTRATION; - } - - @Override - public void execute(CommandContext context) { - if (context.getArgs().length == 1) - ChannelFunction.bulkDeleteMessage(context.getChannel(), Integer.parseInt(context.getArgument(0))); - else - context.getChannel().sendMessage("Failed to delete messages!").queue(); - } - -} diff --git a/src/main/java/netneutrality/command/music/BitconnectCommand.java b/src/main/java/netneutrality/command/music/BitconnectCommand.java index c66aeba..b2e0269 100644 --- a/src/main/java/netneutrality/command/music/BitconnectCommand.java +++ b/src/main/java/netneutrality/command/music/BitconnectCommand.java @@ -7,10 +7,11 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; import netneutrality.command.CommandType; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; public class BitconnectCommand extends AbstractMusicCommand { @@ -25,19 +26,14 @@ public class BitconnectCommand extends AbstractMusicCommand { return "bitconnect"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { - return null; + return "Ready to invest?"; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + } @Override @@ -46,10 +42,19 @@ public class BitconnectCommand extends AbstractMusicCommand { } @Override - public void execute(CommandContext context) { + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true); - context.getMessage().delete().queue(); + context.deleteMessage(); + } + + @Override + public void execute(SlashCommandContext context) { + MusicManager player = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true); + context.reply("Ready to invest?"); } } diff --git a/src/main/java/netneutrality/command/music/DeleteQueueCommand.java b/src/main/java/netneutrality/command/music/ClearQueueCommand.java similarity index 67% rename from src/main/java/netneutrality/command/music/DeleteQueueCommand.java rename to src/main/java/netneutrality/command/music/ClearQueueCommand.java index a44a62a..54a1b96 100644 --- a/src/main/java/netneutrality/command/music/DeleteQueueCommand.java +++ b/src/main/java/netneutrality/command/music/ClearQueueCommand.java @@ -7,15 +7,15 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; -import netneutrality.exceptions.CommandErrorException; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MessageFunction; import netneutrality.utils.MusicUtils; -public class DeleteQueueCommand extends AbstractMusicCommand { +public class ClearQueueCommand extends AbstractMusicCommand { @Override public boolean needAdmin() { @@ -29,19 +29,7 @@ public class DeleteQueueCommand extends AbstractMusicCommand { @Override public String getCommandName() { - return "deletequeue"; - } - - @Override - public String[] getAlias() { - return new String[] {"delq", "delqueue", "clear"}; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.OPTIONAL, new String[] {"track #", "all"}, "") - ); + return "clearqueue"; } @Override @@ -50,7 +38,12 @@ public class DeleteQueueCommand extends AbstractMusicCommand { } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (!MusicUtils.checkIfConnected(player, context)) @@ -58,7 +51,7 @@ public class DeleteQueueCommand extends AbstractMusicCommand { if (context.getArgs().length == 0) { context.reply("Select the track you want to delete from the queue (track# or all)"); - context.reply(MessageFunction.buildViewQueueMessage(player, context.getGuildUser(), true)); + context.reply(MessageFunction.buildViewQueueMessage(player, true)); } if (context.getArgs().length > 0) { @@ -88,9 +81,26 @@ public class DeleteQueueCommand extends AbstractMusicCommand { } } catch (NumberFormatException e) { - throw new CommandErrorException("Please enter a number", context, e); + context.reply("Please enter a number"); } } } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (!MusicUtils.checkIfConnected(manager, context)) + return; + + int queueSize = manager.getQueueSize(); + if (queueSize > 0) { + manager.getScheduler().getQueueManager().clearQueue(); + context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : ""))); + } else { + context.reply("Queue is empty!"); + } + } } diff --git a/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java b/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java index 57ee083..68f4738 100644 --- a/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java +++ b/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java @@ -7,9 +7,10 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MessageFunction; import netneutrality.utils.MusicUtils; @@ -23,17 +24,7 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand { @Override public String getCommandName() { - return "playing"; - } - - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; + return "nowplaying"; } @Override @@ -42,14 +33,29 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand { } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + // no arguments + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) { - context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, context.getGuildUser())); + context.reply(MessageFunction.buildCurrentlyPlayingMessage(player)); } else { - context.reply("Player is not playing any tracks right now"); + context.reply("Bot isn't playing any tracks right now"); } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (MusicUtils.checkIfConnected(manager, context) && manager.isCurrentlyPlaying()) { + context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager)); + } + } } diff --git a/src/main/java/netneutrality/command/music/JoinCommand.java b/src/main/java/netneutrality/command/music/JoinCommand.java index c12784e..10d044a 100644 --- a/src/main/java/netneutrality/command/music/JoinCommand.java +++ b/src/main/java/netneutrality/command/music/JoinCommand.java @@ -8,9 +8,10 @@ package netneutrality.command.music; */ import net.dv8tion.jda.api.entities.VoiceChannel; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.exceptions.CommandExecutionException; import netneutrality.music.MusicManager; @@ -26,30 +27,25 @@ public class JoinCommand extends AbstractMusicCommand { return "join"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { - return "Make the bot join the current voice channel (if no music is playing, bot will disconnect automatically)"; + return "Make the bot join your current voice channel"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (context.getGuildUser().getVoiceState() == null) { throw new CommandExecutionException("Voice state is null, this should not happen", context, null); } - if (!context.getGuildUser().getVoiceState().inVoiceChannel()) { + if (!context.getGuildUser().getVoiceState().inAudioChannel()) { context.reply("You are not in a voice channel"); return; } @@ -59,11 +55,37 @@ public class JoinCommand extends AbstractMusicCommand { return; } - VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel(); + VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel(); if (vChannel != null) { player.connect(vChannel); context.reply("Joining current voice channel"); } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager player = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (context.getGuildUser().getVoiceState() == null) { + throw new CommandExecutionException("Voice state is null, this should not happen", context, null); + } + + if (!context.getGuildUser().getVoiceState().inAudioChannel()) { + context.reply("You are not in a voice channel"); + return; + } + + if (player.isConnected()) { + context.reply("Player is already in a voice channel"); + return; + } + + VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel(); + if (vChannel != null) { + player.connect(vChannel); + context.reply("Joining current voice channel"); + } + } } diff --git a/src/main/java/netneutrality/command/music/LeaveCommand.java b/src/main/java/netneutrality/command/music/LeaveCommand.java index 0b1df1f..f62e07d 100644 --- a/src/main/java/netneutrality/command/music/LeaveCommand.java +++ b/src/main/java/netneutrality/command/music/LeaveCommand.java @@ -7,13 +7,15 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; public class LeaveCommand extends AbstractMusicCommand { + @Override public boolean needAdmin() { return true; @@ -29,23 +31,18 @@ public class LeaveCommand extends AbstractMusicCommand { return "leave"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { return "Make the bot leave the voice channel"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (MusicUtils.checkIfConnected(player, context)) { @@ -53,4 +50,15 @@ public class LeaveCommand extends AbstractMusicCommand { context.reply("Leaving voice channel and clearing queue"); } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager player = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (MusicUtils.checkIfConnected(player, context)) { + player.leave(); + context.reply("Leaving voice channel and clearing queue"); + } + } } diff --git a/src/main/java/netneutrality/command/music/PlayCommand.java b/src/main/java/netneutrality/command/music/PlayCommand.java index 9c219ac..0ef8ec5 100644 --- a/src/main/java/netneutrality/command/music/PlayCommand.java +++ b/src/main/java/netneutrality/command/music/PlayCommand.java @@ -7,9 +7,12 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; public class PlayCommand extends AbstractMusicCommand { @@ -24,34 +27,32 @@ public class PlayCommand extends AbstractMusicCommand { return "play"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create( - this, - new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"url"}, "Youtube/Soundcloud link or search string") - ); - } - @Override public String getDescription() { return "Get the party going!"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + data.addOption(OptionType.STRING, "query", "URL or search query", true); + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (context.getArgs().length > 0) { player.play(context.getMessageText(), context, false, false); - } else { - //TODO: resume queue - context.reply(getUsage()); } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + String query = context.getEvent().getOption("query", OptionMapping::getAsString); + context.ackAndWait(); + + manager.play(query, context, false, false); + } } diff --git a/src/main/java/netneutrality/command/music/QueueCommand.java b/src/main/java/netneutrality/command/music/QueueCommand.java new file mode 100644 index 0000000..5bc534c --- /dev/null +++ b/src/main/java/netneutrality/command/music/QueueCommand.java @@ -0,0 +1,180 @@ +package netneutrality.command.music; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import net.dv8tion.jda.api.entities.Member; +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.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import netneutrality.command.AbstractMusicCommand; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; +import netneutrality.music.MusicManager; +import netneutrality.utils.MessageFunction; +import netneutrality.utils.MusicUtils; + +public class QueueCommand extends AbstractMusicCommand { + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public String getCommandName() { + return "queue"; + } + + @Override + public String getDescription() { + return "Manage the music queue"; + } + + @Override + public DefaultMemberPermissions getSlashCommandPermission() { + return DefaultMemberPermissions.DISABLED; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + data.addSubcommands( + new SubcommandData("clear-all", "Clear all queued tracks"), + new SubcommandData("delete-track", "Remove a single track from the queue") + .addOptions(new OptionData(OptionType.INTEGER, "position", "Position of track", true) + .setMinValue(1)), + new SubcommandData("show-queue", "Display current queue"), + new SubcommandData("shuffle", "Shuffle the queue"), + new SubcommandData("from-user", "View tracks added by a user") + .addOption(OptionType.USER, "user", "Which user?", true), + new SubcommandData("remove-from-user", "Remove all tracks added by a user") + .addOption(OptionType.USER, "user", "Which user?", true) + ); + } + + @Override + public void execute(MessageCommandContext context) { + MusicManager player = MusicManager.get(context.getGuild()); + + if (!MusicUtils.checkIfConnected(player, context)) + return; + + if (context.getArgs().length == 0) { + context.reply("Select the track you want to delete from the queue (track# or all)"); + context.reply(MessageFunction.buildViewQueueMessage(player, true)); + } + + if (context.getArgs().length > 0) { + + int queueSize = player.getQueueSize(); + if (context.getArgument(0).equals("all")) { + + if (queueSize > 0) { + player.getScheduler().getQueueManager().clearQueue(); + context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : ""))); + } else { + context.reply("Queue is empty!"); + } + } else { + + try { + int num = Integer.parseInt(context.getArgument(0)); + + if (num > 0) { + if (player.removeFromQueue(num)) { + context.reply("Removed track #%d from the queue", num); + } else { + context.reply("Error while removing track #%d from the queue", num); + } + } else { + context.reply("Number out of range"); + } + + } catch (NumberFormatException e) { + context.reply("Please enter a number"); + } + } + } + } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + String sub = context.getEvent().getCommandPath().split("/")[1]; + + if (!MusicUtils.checkIfConnected(manager, context)) { + return; + } + + switch (sub) { + case "clear-all" -> { + int queueSize = manager.getQueueSize(); + + if (queueSize > 0) { + manager.getScheduler().getQueueManager().clearQueue(); + context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : ""))); + } else { + context.reply("Queue is already empty!"); + } + } + + case "delete-track" -> { + int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt); + + if (position > 0) { + if (manager.removeFromQueue(position)) { + context.reply("Removed track #%d from the queue", position); + } else { + context.reply("Position is out of bounds"); + } + } + } + + case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager, true)); + + case "shuffle" -> { + manager.getScheduler().getQueueManager().shuffle(); + context.reply("Queue is now shuffled."); + } + + case "from-user" -> { + Member user = context.getEvent().getOption("user", OptionMapping::getAsMember); + if (user == null) { + context.reply("The user isn't from this guild"); + break; + } + + context.reply(MessageFunction.buildUserTracksMessage(manager, user)); + } + + case "remove-from-user" -> { + Member member = context.getEvent().getOption("user", OptionMapping::getAsMember); + if (member == null) { + context.reply("The user isn't from this guild"); + break; + } + + int size = manager.getScheduler() + .getQueueManager() + .getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester())) + .size(); + + if (size == 0) { + context.reply("No tracks added by user %s", member.getEffectiveName()); + } else { + manager.getScheduler().getQueueManager().removeTracksFromUser(member); + context.reply("Removed all tracks from user %s", member.getEffectiveName()); + } + } + } + + + } +} diff --git a/src/main/java/netneutrality/command/music/ShuffleCommand.java b/src/main/java/netneutrality/command/music/ShuffleCommand.java new file mode 100644 index 0000000..57e3a2a --- /dev/null +++ b/src/main/java/netneutrality/command/music/ShuffleCommand.java @@ -0,0 +1,45 @@ +package netneutrality.command.music;/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import netneutrality.command.AbstractMusicCommand; +import netneutrality.core.context.SlashCommandContext; +import netneutrality.music.MusicManager; +import netneutrality.utils.MusicUtils; + +public class ShuffleCommand extends AbstractMusicCommand { + @Override + public boolean isEnabled() { + return true; + } + + @Override + public String getCommandName() { + return "shuffle"; + } + + @Override + public String getDescription() { + return "Shuffle the music queue"; + } + + @Override + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (MusicUtils.checkIfConnected(manager, context)) { + manager.getScheduler().getQueueManager().shuffle(); + context.reply("Queue is now shuffled."); + } + } +} diff --git a/src/main/java/netneutrality/command/music/SkipCommand.java b/src/main/java/netneutrality/command/music/SkipCommand.java index 18a6ae6..ac22c57 100644 --- a/src/main/java/netneutrality/command/music/SkipCommand.java +++ b/src/main/java/netneutrality/command/music/SkipCommand.java @@ -7,9 +7,13 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; import netneutrality.utils.UserFunction; @@ -26,30 +30,19 @@ public class SkipCommand extends AbstractMusicCommand { return "skip"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create( - this, - new CommandUsage.Argument( - CommandUsage.Argument.Type.OPTIONAL, - new String[]{"number"}, - "Number of tracks to skip" - ) - ); - } - @Override public String getDescription() { - return "Used to skip to the next track in the queue (if no admin privileges, vote skip instead)"; + return "Skip to the next track in the queue"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + data.addOptions(new OptionData(OptionType.INTEGER, "skip", "Number of tracks to skip") + .setMinValue(1)); + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (!MusicUtils.checkIfConnected(player, context)) @@ -59,14 +52,14 @@ public class SkipCommand extends AbstractMusicCommand { if (UserFunction.isAdmin(context.getGuildUser()) && context.getArgs().length > 0) { try { int skips = Integer.parseInt(context.getArgument(0)); - player.skip(skips); + player.getScheduler().getQueueManager().skip(skips); context.reply("Skipping %s track%s", skips, (skips > 1 ? "s" : "")); } catch (NumberFormatException e) { context.reply("Please enter how many track(s) to skip"); } } else { - player.skip(1); + player.getScheduler().getQueueManager().skip(1); context.reply("Skipping current track"); } } else { @@ -74,4 +67,21 @@ public class SkipCommand extends AbstractMusicCommand { } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + int skips = context.getEvent().getOption("skip", 0, OptionMapping::getAsInt); + context.ackAndWait(); + + if (!MusicUtils.checkIfConnected(manager, context)) { + if (skips > 0) { + context.reply("Skipping %s track%s", skips, (skips > 1 ? "s" : "")); + manager.getScheduler().getQueueManager().skip(skips); + } else { + context.reply("Skipping current track"); + manager.getScheduler().getQueueManager().skip(1); + } + } + } } diff --git a/src/main/java/netneutrality/command/music/StopCommand.java b/src/main/java/netneutrality/command/music/StopCommand.java index 89adceb..4d869e2 100644 --- a/src/main/java/netneutrality/command/music/StopCommand.java +++ b/src/main/java/netneutrality/command/music/StopCommand.java @@ -7,9 +7,10 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; import netneutrality.utils.UserFunction; @@ -26,23 +27,18 @@ public class StopCommand extends AbstractMusicCommand { return "stop"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { return "Stop the music player, keeping the current queue."; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (!MusicUtils.checkIfConnected(player, context)) @@ -55,4 +51,15 @@ public class StopCommand extends AbstractMusicCommand { context.reply("Stopping current track"); } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (MusicUtils.checkIfConnected(manager, context)) { + manager.stopTrack(); + context.reply("Stopping music playback"); + } + } } diff --git a/src/main/java/netneutrality/command/music/ViewQueueCommand.java b/src/main/java/netneutrality/command/music/ViewQueueCommand.java index 87acb1e..429930a 100644 --- a/src/main/java/netneutrality/command/music/ViewQueueCommand.java +++ b/src/main/java/netneutrality/command/music/ViewQueueCommand.java @@ -6,9 +6,10 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MessageFunction; import netneutrality.utils.MusicUtils; @@ -25,33 +26,44 @@ public class ViewQueueCommand extends AbstractMusicCommand { return "viewqueue"; } - @Override - public String[] getAlias() { - return new String[]{"viewq", "queue"}; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { return "Get the current queue for the music player"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (MusicUtils.checkIfConnected(player, context)) { int queueSize = player.getQueueSize(); if (queueSize > 0) { - context.reply(MessageFunction.buildViewQueueMessage(player, context.getGuildUser(), true)); + context.reply(MessageFunction.buildViewQueueMessage(player, true)); } else { context.reply("Queue is empty!"); } } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + context.ackAndWait(); + + if (MusicUtils.checkIfConnected(manager, context)) { + int queueSize = manager.getQueueSize(); + + if (queueSize > 0) { + context.reply(MessageFunction.buildViewQueueMessage(manager, true)); + } else { + context.reply("Queue is empty!"); + } + } + } } diff --git a/src/main/java/netneutrality/command/music/VolumeCommand.java b/src/main/java/netneutrality/command/music/VolumeCommand.java index 4010c1a..29bd9d2 100644 --- a/src/main/java/netneutrality/command/music/VolumeCommand.java +++ b/src/main/java/netneutrality/command/music/VolumeCommand.java @@ -7,9 +7,13 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; import netneutrality.utils.UserFunction; @@ -26,23 +30,19 @@ public class VolumeCommand extends AbstractMusicCommand { return "volume"; } - @Override - public String[] getAlias() { - return new String[] { "vol" }; - } - - @Override - public CommandUsage getUsage() { - return CommandUsage.create(this, new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"0-100"}, "")); - } - @Override public String getDescription() { return "Sets the volume for the music player"; } @Override - public void execute(CommandContext context) { + public void registerSlashCommandOptions(SlashCommandData data) { + data.addOptions(new OptionData(OptionType.INTEGER, "volume", "Playback volume for this session", true) + .setRequiredRange(0, 100)); + } + + @Override + public void execute(MessageCommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); if (!MusicUtils.checkIfConnected(player, context)) @@ -63,4 +63,15 @@ public class VolumeCommand extends AbstractMusicCommand { } } } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt); + + if (MusicUtils.checkIfConnected(manager, context)) { + context.reply("Volume set to %s", volume); + manager.setVolume(volume); + } + } } diff --git a/src/main/java/netneutrality/command/music/VoteSkipCommand.java b/src/main/java/netneutrality/command/music/VoteSkipCommand.java index 65606b7..a27c612 100644 --- a/src/main/java/netneutrality/command/music/VoteSkipCommand.java +++ b/src/main/java/netneutrality/command/music/VoteSkipCommand.java @@ -6,9 +6,10 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import netneutrality.command.AbstractMusicCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.context.CommandContext; +import netneutrality.core.context.MessageCommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; @@ -24,27 +25,31 @@ public class VoteSkipCommand extends AbstractMusicCommand { return "voteskip"; } - @Override - public String[] getAlias() { - return new String[0]; - } - - @Override - public CommandUsage getUsage() { - return null; - } - @Override public String getDescription() { return "Cast a vote to skip the currently playing track"; } @Override - public void execute(CommandContext context) { - MusicManager player = MusicManager.get(context.getGuild()); + public void registerSlashCommandOptions(SlashCommandData data) { - if (MusicUtils.checkIfConnected(player, context)) { - player.voteSkip(context); + } + + @Override + public void execute(MessageCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + + if (MusicUtils.checkIfConnected(manager, context)) { + manager.voteSkip(context); + } + } + + @Override + public void execute(SlashCommandContext context) { + MusicManager manager = MusicManager.get(context.getGuild()); + + if (MusicUtils.checkIfConnected(manager, context)) { + manager.voteSkip(context); } } } diff --git a/src/main/java/netneutrality/core/CommandManager.java b/src/main/java/netneutrality/core/CommandManager.java index c6ee03b..5cb70f4 100644 --- a/src/main/java/netneutrality/core/CommandManager.java +++ b/src/main/java/netneutrality/core/CommandManager.java @@ -7,6 +7,11 @@ package netneutrality.core; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction; +import netneutrality.NetNeutrality; import netneutrality.command.AbstractCommand; import org.reflections.Reflections; import org.slf4j.Logger; @@ -14,26 +19,27 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Set; public class CommandManager { private final static Logger logger = LoggerFactory.getLogger(CommandManager.class); public static final HashMap commands = new HashMap<>(); - public static final HashMap commandsAlias = new HashMap<>(); public static void init() { initCommands(); - initAlias(); + registerSlashCommands(); } public static boolean isValidCommand(String commandString) { - return commands.containsKey(commandString) || commandsAlias.containsKey(commandString); + return commands.containsKey(commandString); } public static AbstractCommand getCommand(String commandString) { - return commands.containsKey(commandString) ? commands.get(commandString) : commandsAlias.get(commandString); + return commands.get(commandString); } private static void initCommands() { @@ -65,14 +71,23 @@ public class CommandManager { logger.info("Loaded " + commands.size() + " commands!"); } - private static void initAlias() { + private static void registerSlashCommands() { + CommandListUpdateAction commandListUpdateAction = NetNeutrality.getJDA().updateCommands(); + List slashCommands = new ArrayList<>(); for (AbstractCommand command : commands.values()) { - for (String alias : command.getAlias()) { - if (!commandsAlias.containsKey(alias)) - commandsAlias.put(alias, command); + if (!command.isEnabled() || !command.isSlashCommand()) { + continue; } - } - } + SlashCommandData data = Commands.slash(command.getCommandName(), command.getDescription()); + data.setDefaultPermissions(command.getSlashCommandPermission()); + data.setGuildOnly(command.isGuildOnly()); + command.registerSlashCommandOptions(data); + + slashCommands.add(data); + } + + commandListUpdateAction.addCommands(slashCommands).queue(); + } } diff --git a/src/main/java/netneutrality/core/GuildManager.java b/src/main/java/netneutrality/core/GuildManager.java index 63e951d..32708b4 100644 --- a/src/main/java/netneutrality/core/GuildManager.java +++ b/src/main/java/netneutrality/core/GuildManager.java @@ -17,8 +17,6 @@ import netneutrality.db.controllers.DBMessage; import netneutrality.db.entities.OBlacklist; import netneutrality.db.entities.OGuild; import netneutrality.db.entities.OGuildSetting; -import netneutrality.handler.ratelimiter.IMessageRateLimiter; -import netneutrality.handler.ratelimiter.MessageRateLimiterImpl; import netneutrality.thread.ThreadManager; import netneutrality.utils.JpaUtils; import netneutrality.utils.MessageFunction; @@ -42,12 +40,10 @@ public class GuildManager { private final Set blacklistCache = new HashSet<>(); private final Guild guild; private OGuild guildEntity; - private final IMessageRateLimiter rateLimiter; private GuildManager(Guild guild) { this.guild = guild; this.guildEntity = loadGuildFromDb(guild.getId()); - this.rateLimiter = new MessageRateLimiterImpl(); managers.put(guild.getId(), this); reloadBlacklistCache(); @@ -61,7 +57,6 @@ public class GuildManager { this.guild = temp; this.guildEntity = loadGuildFromDb(guild.getId()); - this.rateLimiter = new MessageRateLimiterImpl(); managers.put(guild.getId(), this); reloadBlacklistCache(); @@ -97,10 +92,6 @@ public class GuildManager { return guildEntity.getDebug(); } - public IMessageRateLimiter getRateLimiter() { - return rateLimiter; - } - public Guild getGuild() { return guild; } diff --git a/src/main/java/netneutrality/core/LogHandler.java b/src/main/java/netneutrality/core/LogHandler.java index 2165707..3208447 100644 --- a/src/main/java/netneutrality/core/LogHandler.java +++ b/src/main/java/netneutrality/core/LogHandler.java @@ -10,7 +10,6 @@ import io.sentry.Sentry; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.TextChannel; import netneutrality.core.context.CommandContext; -import netneutrality.exceptions.CommandErrorException; import netneutrality.exceptions.CommandException; import netneutrality.exceptions.CommandExecutionException; import netneutrality.utils.MessageFunction; @@ -27,12 +26,6 @@ public class LogHandler { * @param e The exceptions to log */ public static void log(Exception e) { - if (e instanceof CommandErrorException) { - sendMessageToChannel((CommandErrorException) e); - logInternally((CommandErrorException) e); - return; - } - if (e instanceof CommandExecutionException) { Sentry.captureException(e); logInternally((CommandExecutionException) e); @@ -82,9 +75,4 @@ public class LogHandler { } } } - - private static void sendMessageToChannel(CommandErrorException error) { - CommandContext context = error.getCommandContext(); - context.reply(error.getMessage()); - } } diff --git a/src/main/java/netneutrality/core/context/CommandContext.java b/src/main/java/netneutrality/core/context/CommandContext.java index 40d3395..cf74a97 100644 --- a/src/main/java/netneutrality/core/context/CommandContext.java +++ b/src/main/java/netneutrality/core/context/CommandContext.java @@ -1,231 +1,51 @@ -package netneutrality.core.context; -/* +package netneutrality.core.context;/* * Copyright (c) anthorob * * https://xtremcommunity.com/ * https://discord.gg/CdFcyQ */ -import io.sentry.Breadcrumb; -import io.sentry.Sentry; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.api.events.Event; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; import netneutrality.command.AbstractCommand; -import netneutrality.command.CommandUsage; -import netneutrality.core.GuildManager; -import netneutrality.core.LogHandler; -import netneutrality.exceptions.CommandErrorException; -import netneutrality.exceptions.CommandExecutionException; -import netneutrality.thread.ThreadManager; -import netneutrality.utils.Settings; -import netneutrality.utils.UserFunction; -import java.util.Collection; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; +public interface CommandContext { -public class CommandContext { + AbstractCommand getCommand(); - private static final ExecutorService commandExecutor = ThreadManager.getExecutor("command-execution", ThreadManager.ExecutorType.FIXED); + Event getEvent(); - private AbstractCommand command; - private final Message message; - private final Guild guild; - private final TextChannel channel; - private final Member member; - private final User user; - private final JDA jda; + Guild getGuild(); - public CommandContext(Message message, Member member, User user, Guild guild, TextChannel channel, JDA jda) { - this.message = message; - this.member = member; - this.guild = guild; - this.channel = channel; - this.user = user; - this.jda = jda; + MessageChannel getChannel(); - io.sentry.protocol.User sentryUser = new io.sentry.protocol.User(); - sentryUser.setId(member.getId()); - sentryUser.setUsername(member.getEffectiveName()); - Sentry.setUser(sentryUser); - Sentry.addBreadcrumb(Breadcrumb.info("New context for message id " + message.getId())); - } + Member getGuildUser(); - public static CommandContext createContext(GuildMessageReceivedEvent event) { - Message message = event.getMessage(); - Member member = event.getMember(); - User user = event.getAuthor(); - Guild guild = event.getGuild(); - TextChannel channel = event.getChannel(); - JDA jda = event.getJDA(); + User getDiscordUser(); - return new CommandContext(message, member, user, guild, channel, jda); - } + JDA getJDA(); - public String getArgument(int index) { - return getArgs()[index]; - } + void deleteMessage(); - public String[] getArgs() { - String msg = message.getContentDisplay().trim(); - msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); - String cmd = msg.split(" ", 2)[0]; - String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s"); + void reply(MessageCreateData message); - if (args.length == 1 && args[0].isEmpty()) - args = new String[0]; + void reply(String message); - return args; - } + void reply(MessageEmbed... embeds); - public String getMessageText() { - String msg = message.getContentDisplay().trim(); - msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); - String cmd = msg.split(" ", 2)[0]; - - return msg.replaceFirst("(" + cmd + ")(\s)?", ""); - } - - public Message getMessage() { - return message; - } - - public Guild getGuild() { - return guild; - } - - public TextChannel getChannel() { - return channel; - } - - public Member getGuildUser() { - return member; - } - - public User getDiscordUser() { - return user; - } - - public JDA getJDA() { - return jda; - } - - public AbstractCommand getCommand() { - return command; - } - - public GuildManager getGuildManager() { - return GuildManager.getManager(guild); - } - - public void reply(CommandUsage usage) { - reply(new MessageBuilder(usage.toString()).build()); - } - - public void reply(String msg) { - reply(new MessageBuilder(msg).build()); - } - - public void reply(String format, Object... arguments) { + default void reply(String format, Object... arguments) { reply(String.format(format, arguments)); } - public void reply(Message msg) { - if (channel != null && channel.canTalk()) { - channel.sendMessage(msg).queue(); - } + void replyToMessage(String message); + + default void replyToMessage(MessageCreateData message) { + reply(message); } - public void reply(MessageEmbed embed, MessageEmbed... embeds) { - if (channel != null && channel.canTalk()) { - channel.sendMessageEmbeds(embed, embeds).queue(); - } - } - - public void reply(Collection embeds) { - if (channel != null && channel.canTalk()) { - channel.sendMessageEmbeds(embeds).queue(); - } - } - - public void replyToMessage(String msg) { - if (channel != null && channel.canTalk() && message != null) { - replyToMessage(new MessageBuilder(msg).build()); - } - } - - public void replyToMessage(String format, Object... arguments) { + default void replyToMessage(String format, Object... arguments) { replyToMessage(String.format(format, arguments)); } - - public void replyToMessage(Message msg) { - if (channel != null && channel.canTalk() && message != null) { - message.reply(msg).queue(); - } - } - - public void replyPrivate(String msg) { - replyPrivate(new MessageBuilder(msg).build()); - } - - public void replyPrivate(Message msg) { - user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue(); - } - - public void replyPrivate(MessageEmbed embed, MessageEmbed... embeds) { - user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed, embeds)).queue(); - } - - public void replyPrivate(Collection embeds) { - user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue(); - } - - public void deleteMessage() { - if (message != null) { - message.delete().queueAfter(1, TimeUnit.SECONDS); - } - } - - public boolean isMessageFromOwner() { - return member.isOwner(); - } - - public boolean isMessageFromCreator() { - String creatorId = Settings.getInstance().getCreatorId(); - - return creatorId.equals(member.getId()); - } - - public void dispatch(AbstractCommand command) { - this.command = command; - //TODO: Add usage check - - Sentry.configureScope(scope -> { - scope.setContexts("command", command.getCommandName()); - scope.setContexts("isAdmin", UserFunction.isAdmin(member)); - scope.setContexts("canBeExecuted", command.canBeExecutedBy(guild, member)); - scope.setTag("channel", channel.getId()); - scope.setTag("message", message.getId()); - scope.setTag("guild", guild.getId()); - }); - - try { - if (command.needAdmin() && !UserFunction.isAdmin(member)) { - reply("You are not powerful enough, scrub"); - return; - } - - if (command.canBeExecutedBy(guild, member)) { - if (command.canBeExecutedFrom(guild, channel)) { - command.execute(this); - } - } - } catch (CommandErrorException | CommandExecutionException e) { - LogHandler.log(e); - } catch (Exception e) { - LogHandler.log(new CommandExecutionException("Error during command execution", this, e)); - } - } } diff --git a/src/main/java/netneutrality/core/context/MessageCommandContext.java b/src/main/java/netneutrality/core/context/MessageCommandContext.java new file mode 100644 index 0000000..294ffcd --- /dev/null +++ b/src/main/java/netneutrality/core/context/MessageCommandContext.java @@ -0,0 +1,225 @@ +package netneutrality.core.context; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import io.sentry.Breadcrumb; +import io.sentry.Sentry; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import netneutrality.command.AbstractCommand; +import netneutrality.core.GuildManager; +import netneutrality.core.LogHandler; +import netneutrality.exceptions.CommandExecutionException; +import netneutrality.thread.ThreadManager; +import netneutrality.utils.Settings; +import netneutrality.utils.UserFunction; + +import java.util.Collection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +public class MessageCommandContext implements CommandContext { + + private static final ExecutorService commandExecutor = ThreadManager.getExecutor("command-execution", ThreadManager.ExecutorType.FIXED); + + private AbstractCommand command; + private final MessageReceivedEvent event; + private final Message message; + private final Guild guild; + private final MessageChannel channel; + private final Member member; + private final User user; + private final JDA jda; + + public MessageCommandContext(MessageReceivedEvent event, Message message, Member member, User user, Guild guild, MessageChannel channel, JDA jda) { + this.event = event; + this.message = message; + this.member = member; + this.guild = guild; + this.channel = channel; + this.user = user; + this.jda = jda; + + io.sentry.protocol.User sentryUser = new io.sentry.protocol.User(); + sentryUser.setId(member.getId()); + sentryUser.setUsername(member.getEffectiveName()); + Sentry.setUser(sentryUser); + Sentry.addBreadcrumb(Breadcrumb.info("New context for message id " + message.getId())); + } + + public static MessageCommandContext createContext(MessageReceivedEvent event) { + Message message = event.getMessage(); + Member member = event.getMember(); + User user = event.getAuthor(); + Guild guild = event.getGuild(); + MessageChannel channel = event.getChannel(); + JDA jda = event.getJDA(); + + return new MessageCommandContext(event, message, member, user, guild, channel, jda); + } + + @Override + public MessageReceivedEvent getEvent() { + return event; + } + + public String getArgument(int index) { + return getArgs()[index]; + } + + public String[] getArgs() { + String msg = message.getContentDisplay().trim(); + msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); + String cmd = msg.split(" ", 2)[0]; + String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s"); + + if (args.length == 1 && args[0].isEmpty()) + args = new String[0]; + + return args; + } + + public String getMessageText() { + String msg = message.getContentDisplay().trim(); + msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); + String cmd = msg.split(" ", 2)[0]; + + return msg.replaceFirst("(" + cmd + ")(\s)?", ""); + } + + public Message getMessage() { + return message; + } + + public Guild getGuild() { + return guild; + } + + public MessageChannel getChannel() { + return channel; + } + + public Member getGuildUser() { + return member; + } + + public User getDiscordUser() { + return user; + } + + public JDA getJDA() { + return jda; + } + + public AbstractCommand getCommand() { + return command; + } + + public GuildManager getGuildManager() { + return GuildManager.getManager(guild); + } + + public void reply(String message) { + reply(MessageCreateData.fromContent(message)); + } + + public void reply(String format, Object... arguments) { + reply(String.format(format, arguments)); + } + + public void reply(MessageEmbed... embeds) { + reply(MessageCreateData.fromEmbeds(embeds)); + } + + public void reply(MessageCreateData msg) { + if (channel != null && channel.canTalk()) { + channel.sendMessage(msg).queue(); + } + } + + @Override + public void replyToMessage(String message) { + replyToMessage(MessageCreateData.fromContent(message)); + } + + @Override + public void replyToMessage(String format, Object... arguments) { + replyToMessage(String.format(format, arguments)); + } + + public void replyToMessage(MessageCreateData msg) { + if (channel != null && channel.canTalk() && message != null) { + message.reply(msg).queue(); + } + } + + public void replyPrivate(String message) { + replyPrivate(MessageCreateData.fromContent(message)); + } + + public void replyPrivate(MessageCreateData msg) { + user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue(); + } + + public void replyPrivate(MessageEmbed... embeds) { + replyPrivate(MessageCreateData.fromEmbeds(embeds)); + } + + public void replyPrivate(Collection embeds) { + user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue(); + } + + @Override + public void deleteMessage() { + if (message != null) { + message.delete().queueAfter(1, TimeUnit.SECONDS); + } + } + + public boolean isMessageFromOwner() { + return member.isOwner(); + } + + public boolean isMessageFromCreator() { + String creatorId = Settings.getInstance().getCreatorId(); + + return creatorId.equals(member.getId()); + } + + public void dispatch(AbstractCommand command) { + this.command = command; + //TODO: Add usage check + + Sentry.configureScope(scope -> { + scope.setContexts("command", command.getCommandName()); + scope.setContexts("isAdmin", UserFunction.isAdmin(member)); + scope.setContexts("canBeExecuted", command.canBeExecutedBy(guild, member)); + scope.setTag("channel", channel.getId()); + scope.setTag("message", message.getId()); + scope.setTag("guild", guild.getId()); + }); + + try { + if (command.needAdmin() && !UserFunction.isAdmin(member)) { + reply("You are not powerful enough, scrub"); + return; + } + + if (command.canBeExecutedBy(guild, member)) { + if (command.canBeExecutedFrom(guild, channel)) { + command.execute(this); + } + } + } catch (CommandExecutionException e) { + LogHandler.log(e); + } catch (Exception e) { + LogHandler.log(new CommandExecutionException("Error during command execution", this, e)); + } + } +} diff --git a/src/main/java/netneutrality/core/context/SlashCommandContext.java b/src/main/java/netneutrality/core/context/SlashCommandContext.java new file mode 100644 index 0000000..bfab32e --- /dev/null +++ b/src/main/java/netneutrality/core/context/SlashCommandContext.java @@ -0,0 +1,203 @@ +package netneutrality.core.context;/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import io.sentry.Sentry; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import net.dv8tion.jda.api.utils.messages.MessageEditData; +import netneutrality.command.AbstractCommand; +import netneutrality.core.CommandManager; +import netneutrality.core.LogHandler; +import netneutrality.exceptions.CommandExecutionException; +import netneutrality.utils.UserFunction; + +import java.util.Collection; + +public class SlashCommandContext implements CommandContext { + + private final SlashCommandInteractionEvent event; + private AbstractCommand command; + private final JDA jda; + private final Guild guild; + private final MessageChannel channel; + private final Member member; + private final User user; + + private SlashCommandContext(JDA jda, SlashCommandInteractionEvent event, Guild guild, MessageChannel channel, Member member, User user) { + this.jda = jda; + this.event = event; + this.guild = guild; + this.channel = channel; + this.member = member; + this.user = user; + } + + public static SlashCommandContext createContext(SlashCommandInteractionEvent event) { + Member member = event.getMember(); + User user = event.getUser(); + Guild guild = event.getGuild(); + MessageChannel channel = event.getChannel(); + JDA jda = event.getJDA(); + + return new SlashCommandContext(jda, event, guild, channel, member, user); + } + + @Override + public SlashCommandInteractionEvent getEvent() { + return event; + } + + @Override + public AbstractCommand getCommand() { + return command; + } + + @Override + public JDA getJDA() { + return jda; + } + + @Override + public Guild getGuild() { + return guild; + } + + @Override + public MessageChannel getChannel() { + return channel; + } + + @Override + public Member getGuildUser() { + return member; + } + + @Override + public User getDiscordUser() { + return user; + } + + @Override + public void deleteMessage() { + // not implemented + } + + public void sendMessageInPrivate(String msg) { + sendMessageInPrivate(new MessageCreateBuilder().setContent(msg).build()); + } + + public void sendMessageInPrivate(MessageCreateData msg) { + user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue(); + } + + public void sendMessageInPrivate(MessageEmbed embed, MessageEmbed... embeds) { + user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed, embeds)).queue(); + } + + public void sendMessageInPrivate(Collection embeds) { + user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue(); + } + + public void reply(MessageCreateData message) { + if (event.isAcknowledged()) { + getHook().editOriginal(MessageEditData.fromCreateData(message)).queue(); + //getHook().sendMessage(message).setEphemeral(false).queue(); + return; + } + + event.reply(message).queue(); + } + + public void reply(MessageEmbed... embeds) { + reply(MessageCreateData.fromEmbeds(embeds)); + } + + @Override + public void reply(String message) { + reply(MessageCreateData.fromContent(message)); + } + + public void replyEphemeral(MessageCreateData message) { + if (event.isAcknowledged()) { + getHook().editOriginal(MessageEditData.fromCreateData(message)).queue(); + //getHook().sendMessage(message).setEphemeral(true).queue(); + return; + } + + event.reply(message).setEphemeral(true).queue(); + } + + public void replyEphemeral(String format, Object... arguments) { + replyEphemeral(String.format(format, arguments)); + } + + public void replyEphemeral(String message) { + replyEphemeral(MessageCreateData.fromContent(message)); + } + + @Override + public void replyToMessage(String message) { + replyEphemeral(message); + } + + public InteractionHook getHook() { + if (!event.isAcknowledged()) + throw new RuntimeException("Interaction isn't acknowledged yet"); + + return event.getHook(); + } + + public void ackAndWait(boolean ephemeral) { + if (event.isAcknowledged()) + throw new RuntimeException("Interaction is already acknowledged, please use getHook() instead."); + + event.deferReply(ephemeral).queue(); + } + + public void ackAndWait() { + ackAndWait(false); + } + + public void dispatchToCommand(String commandName) { + if (!CommandManager.isValidCommand(commandName)) { + return; + } + + AbstractCommand command = CommandManager.getCommand(commandName); + this.command = command; + + Sentry.configureScope(scope -> { + scope.setContexts("command", command.getCommandName()); + scope.setContexts("isAdmin", UserFunction.isAdmin(member)); + scope.setContexts("canBeExecuted", command.canBeExecutedBy(guild, member)); + scope.setTag("channel", channel.getId()); + scope.setTag("interactionId", event.getInteraction().getId()); + scope.setTag("guild", guild.getId()); + }); + + try { + if (command.needAdmin() && !UserFunction.isAdmin(member)) { + replyEphemeral("You are not powerful enough, scrub"); + return; + } + + if (command.canBeExecutedBy(guild, member)) { + command.execute(this); + } + + } catch (CommandExecutionException exception) { + LogHandler.log(exception); + } catch (Exception exception) { + LogHandler.log(new CommandExecutionException("Error during command execution", this, exception)); + } + + } +} diff --git a/src/main/java/netneutrality/db/GSettings.java b/src/main/java/netneutrality/db/GSettings.java index 8ec59c0..75bb292 100644 --- a/src/main/java/netneutrality/db/GSettings.java +++ b/src/main/java/netneutrality/db/GSettings.java @@ -6,17 +6,11 @@ package netneutrality.db; * https://discord.gg/CdFcyQ */ - -import netneutrality.utils.Settings; - public enum GSettings { //TODO: description - PREFIX("PREFIX", Settings.getInstance().get("BOT_PREFIX", "$")), MUSIC_ROLE("MUSIC_ROLE", ""), MUSIC_CHANNEL("MUSIC_CHANNEL", ""), - MUSIC_VOLUME("MUSIC_VOLUME", "30"), - MUSIC_TOPIC("MUSIC_TOPIC", "0"), - BLACKLIST_CHANNELS("BLACKLIST_EXCLUDE_CHANNELS", ""); + MUSIC_VOLUME("MUSIC_VOLUME", "30"); private final String name; private final String defaultValue; diff --git a/src/main/java/netneutrality/db/controllers/DBBlacklist.java b/src/main/java/netneutrality/db/controllers/DBBlacklist.java index 725316e..b4cf87a 100644 --- a/src/main/java/netneutrality/db/controllers/DBBlacklist.java +++ b/src/main/java/netneutrality/db/controllers/DBBlacklist.java @@ -6,6 +6,7 @@ package netneutrality.db.controllers; * https://discord.gg/CdFcyQ */ +import net.dv8tion.jda.api.entities.Guild; import netneutrality.core.GuildManager; import netneutrality.core.context.CommandContext; import netneutrality.db.AbstractController; @@ -104,8 +105,8 @@ public class DBBlacklist extends AbstractController { session.close(); } - public ArrayList getBlacklistForGuild(CommandContext context) { - GuildManager guildManager = GuildManager.getManager(context.getGuild()); + public ArrayList getBlacklistForGuild(Guild guild) { + GuildManager guildManager = GuildManager.getManager(guild); Session session = getSessionFactory().openSession(); OGuild guildEntity = guildManager.getGuildEntity(); diff --git a/src/main/java/netneutrality/exceptions/CommandErrorException.java b/src/main/java/netneutrality/exceptions/CommandErrorException.java deleted file mode 100644 index 0802ea2..0000000 --- a/src/main/java/netneutrality/exceptions/CommandErrorException.java +++ /dev/null @@ -1,27 +0,0 @@ -package netneutrality.exceptions; - -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import netneutrality.core.context.CommandContext; - -/** - * Used to reply with a message in the text channel where the command was executed - */ -public class CommandErrorException extends CommandException { - - /** - * Used to reply with a message in the text channel where the command was executed - * - * @param message Message to send to the channel - * @param context The command context - */ - public CommandErrorException(String message, CommandContext context, Throwable cause) { - super(message, context, cause); - - } -} diff --git a/src/main/java/netneutrality/handler/hooks/message/ReactionHandler.java b/src/main/java/netneutrality/handler/hooks/message/ReactionHandler.java deleted file mode 100644 index b309f44..0000000 --- a/src/main/java/netneutrality/handler/hooks/message/ReactionHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package netneutrality.handler.hooks.message; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.events.message.guild.GuildMessageUpdateEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class ReactionHandler extends ListenerAdapter { - - @Override - public void onGuildMessageReactionAdd(@NotNull GuildMessageReactionAddEvent event) { - Member member = event.getMember(); - } - - @Override - public void onGuildMessageReactionRemove(@NotNull GuildMessageReactionRemoveEvent event) { - super.onGuildMessageReactionRemove(event); - } - - @Override - public void onGuildMessageUpdate(@NotNull GuildMessageUpdateEvent event) { - super.onGuildMessageUpdate(event); - } -} diff --git a/src/main/java/netneutrality/handler/ratelimiter/IMessageRateLimiter.java b/src/main/java/netneutrality/handler/ratelimiter/IMessageRateLimiter.java deleted file mode 100644 index 4ce8d7b..0000000 --- a/src/main/java/netneutrality/handler/ratelimiter/IMessageRateLimiter.java +++ /dev/null @@ -1,16 +0,0 @@ -package netneutrality.handler.ratelimiter; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.TextChannel; - -public interface IMessageRateLimiter { - - void check(Member member, Message message, TextChannel channel); -} diff --git a/src/main/java/netneutrality/handler/ratelimiter/MessageRateLimiterImpl.java b/src/main/java/netneutrality/handler/ratelimiter/MessageRateLimiterImpl.java deleted file mode 100644 index bd0d641..0000000 --- a/src/main/java/netneutrality/handler/ratelimiter/MessageRateLimiterImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package netneutrality.handler.ratelimiter; -/* - * Copyright (c) anthorob - * - * https://xtremcommunity.com/ - * https://discord.gg/CdFcyQ - */ - -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.TextChannel; - -import java.time.OffsetDateTime; - -//TODO: to remove? or atleast make it useful -public class MessageRateLimiterImpl implements IMessageRateLimiter { - private final int messagePerSecond = 5; - private static Member lastSender; - private static OffsetDateTime timestamp; - private static final long cooldown = 500000000L; // 1 seconds = 1000000000 nanoseconds - - public MessageRateLimiterImpl() { - - } - - @Override - public void check(Member member, Message message, TextChannel channel) { - - if (lastSender == null || timestamp == null) { - lastSender = member; - timestamp = message.getTimeCreated(); - return; - } - - if (member.getUser().getId().equals(lastSender.getUser().getId())) { - if (message.getTimeCreated().minusNanos(cooldown).isBefore(timestamp)) { - lastSender = member; - timestamp = message.getTimeCreated(); - message.delete().queue(); - return; - } - } - - lastSender = member; - timestamp = message.getTimeCreated(); - } -} diff --git a/src/main/java/netneutrality/hooks/SlashCommandHooks.java b/src/main/java/netneutrality/hooks/SlashCommandHooks.java new file mode 100644 index 0000000..598f733 --- /dev/null +++ b/src/main/java/netneutrality/hooks/SlashCommandHooks.java @@ -0,0 +1,23 @@ +package netneutrality.hooks; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import netneutrality.core.context.SlashCommandContext; +import org.jetbrains.annotations.NotNull; + +public class SlashCommandHooks extends ListenerAdapter { + + @Override + public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) { + String commandName = event.getName(); + + SlashCommandContext commandContext = SlashCommandContext.createContext(event); + commandContext.dispatchToCommand(commandName); + } +} diff --git a/src/main/java/netneutrality/handler/hooks/guilds/ServerJoinHandler.java b/src/main/java/netneutrality/hooks/guilds/ServerJoinHandler.java similarity index 93% rename from src/main/java/netneutrality/handler/hooks/guilds/ServerJoinHandler.java rename to src/main/java/netneutrality/hooks/guilds/ServerJoinHandler.java index de6a6a7..f556970 100644 --- a/src/main/java/netneutrality/handler/hooks/guilds/ServerJoinHandler.java +++ b/src/main/java/netneutrality/hooks/guilds/ServerJoinHandler.java @@ -1,4 +1,4 @@ -package netneutrality.handler.hooks.guilds; +package netneutrality.hooks.guilds; /* * Copyright (c) anthorob diff --git a/src/main/java/netneutrality/handler/hooks/message/EditHandler.java b/src/main/java/netneutrality/hooks/message/EditHandler.java similarity index 97% rename from src/main/java/netneutrality/handler/hooks/message/EditHandler.java rename to src/main/java/netneutrality/hooks/message/EditHandler.java index 03cc806..4637c14 100644 --- a/src/main/java/netneutrality/handler/hooks/message/EditHandler.java +++ b/src/main/java/netneutrality/hooks/message/EditHandler.java @@ -1,4 +1,4 @@ -package netneutrality.handler.hooks.message; +package netneutrality.hooks.message; /* * Copyright (c) anthorob diff --git a/src/main/java/netneutrality/handler/hooks/message/MessageReceivedHandler.java b/src/main/java/netneutrality/hooks/message/MessageReceivedHandler.java similarity index 65% rename from src/main/java/netneutrality/handler/hooks/message/MessageReceivedHandler.java rename to src/main/java/netneutrality/hooks/message/MessageReceivedHandler.java index 62bb276..9946879 100644 --- a/src/main/java/netneutrality/handler/hooks/message/MessageReceivedHandler.java +++ b/src/main/java/netneutrality/hooks/message/MessageReceivedHandler.java @@ -1,4 +1,4 @@ -package netneutrality.handler.hooks.message; +package netneutrality.hooks.message; /* * Copyright (c) anthorob * @@ -8,39 +8,42 @@ package netneutrality.handler.hooks.message; import io.sentry.Breadcrumb; import io.sentry.Sentry; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +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.TextChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import netneutrality.command.AbstractCommand; import netneutrality.core.CommandManager; import netneutrality.core.GuildManager; -import netneutrality.core.context.CommandContext; -import netneutrality.db.GSettings; +import netneutrality.core.context.MessageCommandContext; import netneutrality.utils.Settings; import org.jetbrains.annotations.NotNull; public class MessageReceivedHandler extends ListenerAdapter { @Override - public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { + public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (event.getAuthor().isBot()) return; if (event.isWebhookMessage()) return; + if (!event.isFromGuild()) + return; + Message message = event.getMessage(); Member member = event.getMember(); Guild guild = event.getGuild(); - TextChannel channel = event.getChannel(); + TextChannel channel = (TextChannel) event.getChannel(); Sentry.addBreadcrumb(Breadcrumb.info("New message from " + member.getEffectiveName())); - CommandContext context = CommandContext.createContext(event); + MessageCommandContext context = MessageCommandContext.createContext(event); - GuildManager.getManager(guild).getRateLimiter().check(member, message, channel); GuildManager.getManager(guild).checkAndLogMessage(message, member, channel); String rawMessage = message.getContentDisplay().trim(); - //String prefix = Settings.getInstance().get("BOT_PREFIX", "$"); - String prefix = GuildManager.getManager(guild).getSetting(GSettings.PREFIX); + String prefix = Settings.getInstance().get("BOT_PREFIX", "$"); if (rawMessage.startsWith(prefix)) { rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim(); @@ -54,8 +57,6 @@ public class MessageReceivedHandler extends ListenerAdapter { context.dispatch(command); } - } else { - GuildManager.getManager(guild).checkBlacklist(message, channel); } } } diff --git a/src/main/java/netneutrality/hooks/message/ReactionHandler.java b/src/main/java/netneutrality/hooks/message/ReactionHandler.java new file mode 100644 index 0000000..abcf8b2 --- /dev/null +++ b/src/main/java/netneutrality/hooks/message/ReactionHandler.java @@ -0,0 +1,32 @@ +package netneutrality.hooks.message; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.message.MessageUpdateEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +public class ReactionHandler extends ListenerAdapter { + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + Member member = event.getMember(); + } + + @Override + public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { + + } + + @Override + public void onMessageUpdate(@NotNull MessageUpdateEvent event) { + + } +} diff --git a/src/main/java/netneutrality/handler/hooks/voice/VoiceEventHandler.java b/src/main/java/netneutrality/hooks/voice/VoiceEventHandler.java similarity index 95% rename from src/main/java/netneutrality/handler/hooks/voice/VoiceEventHandler.java rename to src/main/java/netneutrality/hooks/voice/VoiceEventHandler.java index 7a42370..3bc75c3 100644 --- a/src/main/java/netneutrality/handler/hooks/voice/VoiceEventHandler.java +++ b/src/main/java/netneutrality/hooks/voice/VoiceEventHandler.java @@ -1,4 +1,4 @@ -package netneutrality.handler.hooks.voice; +package netneutrality.hooks.voice; /* * Copyright (c) anthorob * diff --git a/src/main/java/netneutrality/http/server/WebApi.java b/src/main/java/netneutrality/http/server/WebApi.java index ccb21d3..8a01775 100644 --- a/src/main/java/netneutrality/http/server/WebApi.java +++ b/src/main/java/netneutrality/http/server/WebApi.java @@ -13,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/src/main/java/netneutrality/music/MusicManager.java b/src/main/java/netneutrality/music/MusicManager.java index de0ea5e..f0f3b8f 100644 --- a/src/main/java/netneutrality/music/MusicManager.java +++ b/src/main/java/netneutrality/music/MusicManager.java @@ -10,7 +10,6 @@ package netneutrality.music; import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; -import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager; import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager; import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager; import com.sedmelluq.discord.lavaplayer.track.AudioTrack; @@ -21,6 +20,7 @@ import net.dv8tion.jda.api.entities.VoiceChannel; import netneutrality.core.GuildManager; import netneutrality.core.LogHandler; import netneutrality.core.context.CommandContext; +import netneutrality.core.context.SlashCommandContext; import netneutrality.db.GSettings; import netneutrality.exceptions.CommandExecutionException; import netneutrality.music.loadable.LoadableSong; @@ -30,7 +30,9 @@ import netneutrality.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -43,7 +45,6 @@ public class MusicManager { private final static ScheduledExecutorService executor = (ScheduledExecutorService) ThreadManager.getExecutor("music", ThreadManager.ExecutorType.SCHEDULED); private final static AudioPlayerManager playerManager = new DefaultAudioPlayerManager(); - private final static Map sources = new HashMap<>(); private final Guild guild; private final AudioPlayer player; private final TrackScheduler scheduler; @@ -75,12 +76,9 @@ public class MusicManager { YoutubeAudioSourceManager youtubeSource = new YoutubeAudioSourceManager(true, Settings.getInstance().get("GG_EMAIL", ""), Settings.getInstance().get("GG_PASS", "")); - SoundCloudAudioSourceManager soundcloudSource = SoundCloudAudioSourceManager.createDefault(); - sources.put("youtube", youtubeSource); - sources.put("soundcloud", soundcloudSource); playerManager.registerSourceManager(youtubeSource); - playerManager.registerSourceManager(soundcloudSource); + playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault()); } public boolean isNotConnectedTo(VoiceChannel vChannel) { @@ -115,7 +113,7 @@ public class MusicManager { } public synchronized Member getRequesterOfCurrentTrack() { - QueueManager.TrackInfo info = scheduler.getQueueManager().getQueue().peek(); + QueueManager.TrackInfo info = scheduler.getQueueManager().peekHeadOfQueue(); if (info == null) return null; @@ -152,9 +150,8 @@ public class MusicManager { public static void disconnectPlayers() { for (MusicManager mm : managers.values()) { - mm.leave(); + mm.getScheduler().getQueueManager().clearQueue(); mm.player.destroy(); - mm.scheduler.getQueueManager().getQueue().clear(); } } @@ -175,7 +172,7 @@ public class MusicManager { public void play(List songs, CommandContext context, boolean needToBeConnected, boolean silent) { Member member = context.getGuildUser(); - TextChannel channel = context.getChannel(); + TextChannel channel = (TextChannel) context.getChannel(); if (member.getVoiceState() == null) { logger.error("Guild {}: Voice state is null", guild.getId()); @@ -185,12 +182,12 @@ public class MusicManager { return; } - if (!member.getVoiceState().inVoiceChannel()) { + if (!member.getVoiceState().inAudioChannel()) { context.replyToMessage("You are not in a voice channel"); return; } - VoiceChannel vChannel = member.getVoiceState().getChannel(); + VoiceChannel vChannel = (VoiceChannel) member.getVoiceState().getChannel(); if (needToBeConnected && isNotConnectedTo(vChannel)) { context.replyToMessage("The player is not in your voice channel"); return; @@ -215,23 +212,9 @@ public class MusicManager { player.stopTrack(); } - public synchronized void skip(int nbTrack) { - if (!scheduler.getQueueManager().getQueue().isEmpty()) { - if (nbTrack >= scheduler.getQueueManager().getQueue().size()) { - nbTrack = 1; - } - - logger.debug("Guild {}: skipping tracks, toSkip={}", guild.getId(), nbTrack); - scheduler.getQueueManager().skipAndPlay(nbTrack); - } else { - leave(); - } - } - public synchronized void voteSkip(CommandContext context) { - Queue queue = scheduler.getQueueManager().getQueue(); - VoiceChannel voiceChannel = guild.getAudioManager().getConnectedChannel(); - QueueManager.TrackInfo info = queue.peek(); + VoiceChannel voiceChannel = (VoiceChannel) guild.getAudioManager().getConnectedChannel(); + QueueManager.TrackInfo info = scheduler.getQueueManager().peekHeadOfQueue(); if (!isCurrentlyPlaying() || voiceChannel == null) { return; @@ -246,7 +229,7 @@ public class MusicManager { if (connected < 3) { context.reply("Skipping current track since not enough people are connected to the voice channel."); - skip(1); + scheduler.getQueueManager().skip(1); return; } @@ -258,7 +241,7 @@ public class MusicManager { if (info.getSkips().size() > needed) { context.reply("Skipping current track: %s", info.getTrack().getInfo().title); - skip(1); + scheduler.getQueueManager().skip(1); } else { context.reply("Adding your vote to skip the current track. Current votes: %d/%d", info.getSkips().size(), (int) Math.ceil(needed)); } @@ -269,14 +252,10 @@ public class MusicManager { } public int getQueueSize() { - synchronized (scheduler.getQueueManager().getQueue()) { - return scheduler.getQueueManager().getQueue().size(); - } - + return scheduler.getQueueManager().getQueueSize(); } public TrackScheduler getScheduler() { return scheduler; } - } diff --git a/src/main/java/netneutrality/music/QueueManager.java b/src/main/java/netneutrality/music/QueueManager.java index 550449f..b2f4518 100644 --- a/src/main/java/netneutrality/music/QueueManager.java +++ b/src/main/java/netneutrality/music/QueueManager.java @@ -14,10 +14,11 @@ import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.LinkedBlockingDeque; +import java.util.function.Predicate; public class QueueManager { - static class TrackInfo { + public static class TrackInfo { private final AudioTrack track; private final Member member; @@ -101,9 +102,9 @@ public class QueueManager { this.queue = new LinkedBlockingDeque<>(); } - public Queue getQueue() { + public List getQueue() { synchronized (queue) { - return queue; + return queue.stream().toList(); } } @@ -115,15 +116,65 @@ public class QueueManager { return guild; } + public int getQueueSize() { + synchronized (queue) { + return queue.size(); + } + } + + public List getQueueWithFilter(Predicate filter) { + synchronized (queue) { + return queue.stream().filter(filter).toList(); + } + } + + public TrackInfo peekHeadOfQueue() { + synchronized (queue) { + return queue.peek(); + } + } + + public TrackInfo pollHeadOfQueue() { + synchronized (queue) { + return queue.poll(); + } + } + + public boolean isEmpty() { + synchronized (queue) { + return queue.isEmpty(); + } + } + public void clearQueue() { - scheduler.getMusicManager().leave(); scheduler.getMusicManager().stopLoading(); + scheduler.getMusicManager().leave(); synchronized (queue) { queue.clear(); } } + public void removeTracksFromUser(Member member) { + synchronized (queue) { + int size = queue.stream().filter(trackInfo -> member.equals(trackInfo.getRequester())).toList().size(); + + if (size > 0) { + if (size == queue.size()) { + clearQueue(); + return; + } + + TrackInfo first = queue.peek(); + queue.removeIf(trackInfo -> member.equals(trackInfo.getRequester())); + + if (first != null && member.equals(first.getRequester())) { + skip(1); + } + } + } + } + public ArrayList getAllQueuedTrack() { synchronized (queue) { ArrayList tracks = new ArrayList<>(); @@ -156,7 +207,7 @@ public class QueueManager { } } - public void nextTrack(AudioPlayer player) { + public void playNextTrack(AudioPlayer player) { synchronized (queue) { TrackInfo last = queue.poll(); TrackInfo next = queue.peek(); @@ -164,7 +215,7 @@ public class QueueManager { if (next != null) { if (last != null && last.getTrack().equals(next.getTrack())) { logger.debug("Guild {}: duplicate track in queue found, skipping to next. last={}, next={}", guild.getId(), last.getTrack().getIdentifier(), next.getTrack().getIdentifier()); - nextTrack(player); + playNextTrack(player); } else { player.startTrack(next.getTrack(), false); } @@ -175,12 +226,23 @@ public class QueueManager { } - public void skipAndPlay(int nb) { - if (nb == 0) + public void skip(int nbTrack) { + if (nbTrack == 0) return; synchronized (queue) { - for (int i = 0; i < nb; i++) { + + if (queue.isEmpty()) { + scheduler.getMusicManager().leave(); + return; + } + + if (nbTrack > queue.size()) { + nbTrack = 1; + } + + logger.debug("Guild {}: skipping tracks, toSkip={}", guild.getId(), nbTrack); + for (int i = 0; i < nbTrack; i++) { TrackInfo last = queue.poll(); } @@ -190,8 +252,8 @@ public class QueueManager { } else { player.stopTrack(); } - } + } } public void shuffle() { @@ -212,17 +274,17 @@ public class QueueManager { } } - protected boolean removeFromQueue(int index) { + public boolean removeFromQueue(int index) { synchronized (queue) { if (queue.size() > 0 && index <= queue.size()) { logger.debug("Guild {}: removing index {} from queue", guild.getId(), index - 1); if (index == 1) { - skipAndPlay(1); + skip(1); return true; } - ArrayList newQueue = new ArrayList<>(queue); + List newQueue = new ArrayList<>(queue); newQueue.remove(index - 1); queue.clear(); diff --git a/src/main/java/netneutrality/music/TrackLoadingHandler.java b/src/main/java/netneutrality/music/TrackLoadingHandler.java index 6d7f853..b61c956 100644 --- a/src/main/java/netneutrality/music/TrackLoadingHandler.java +++ b/src/main/java/netneutrality/music/TrackLoadingHandler.java @@ -30,7 +30,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler { private boolean loadingDisabled = false; private final Guild guild; private final TrackScheduler scheduler; - private final CommandContext context; + private final CommandContext context; public TrackLoadingHandler(CommandContext context, MusicManager manager, String identifier, SourceFinder.AudioSourceType type, boolean silent) { this.identifier = identifier; @@ -66,8 +66,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler { scheduler.getQueueManager().queue(audioTrack, context.getGuildUser()); if (!silent) { - scheduler.sendMessageToChannel(MessageFunction.buildAddingTrackMessage(audioTrack, context.getGuildUser(), type)); - // context.reply(MessageFunction.buildAddingTrackMessage(audioTrack, context.getGuildUser(), type)); + context.reply(MessageFunction.buildAddingTrackMessage(audioTrack, type)); } } @@ -88,8 +87,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler { scheduler.getQueueManager().queue(track, context.getGuildUser()); if (!silent) { - scheduler.sendMessageToChannel(MessageFunction.buildAddingTrackMessage(track, context.getGuildUser(), type)); - // context.reply(MessageFunction.buildAddingTrackMessage(track, context.getGuildUser(), type)); + context.reply(MessageFunction.buildAddingTrackMessage(track, type)); } return; @@ -97,8 +95,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler { context.deleteMessage(); if (!silent) { - scheduler.sendMessageToChannel(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, context.getGuildUser(), identifier, type)); - // context.reply(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, context.getGuildUser(), identifier, type)); + context.reply(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, identifier, type)); } for (AudioTrack track : audioPlaylist.getTracks()) { @@ -106,9 +103,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler { break; } - synchronized (scheduler.getQueueManager().getQueue()) { - scheduler.getQueueManager().queue(track, context.getGuildUser()); - } + scheduler.getQueueManager().queue(track, context.getGuildUser()); } } } diff --git a/src/main/java/netneutrality/music/TrackScheduler.java b/src/main/java/netneutrality/music/TrackScheduler.java index da3031b..dad5daf 100644 --- a/src/main/java/netneutrality/music/TrackScheduler.java +++ b/src/main/java/netneutrality/music/TrackScheduler.java @@ -99,13 +99,11 @@ public class TrackScheduler extends AudioEventAdapter { // check if mayStartNext if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) { - queueManager.nextTrack(player); + queueManager.playNextTrack(player); } if (endReason == AudioTrackEndReason.STOPPED) { - synchronized (queueManager.getQueue()) { - queueManager.getQueue().poll(); - } + queueManager.pollHeadOfQueue(); } // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true). @@ -127,6 +125,6 @@ public class TrackScheduler extends AudioEventAdapter { public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) { logger.debug("Guild {}: currently playing track is stuck: {}", guild.getId(), track.getIdentifier()); sendMessageToChannel("Current track is stuck, skipping to next"); - queueManager.nextTrack(player); + queueManager.playNextTrack(player); } } diff --git a/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java b/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java index 2a22301..f24a2e6 100644 --- a/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java +++ b/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java @@ -25,12 +25,12 @@ public class MusicPlayerAutoDisconnectThread implements Runnable { @Override public void run() { Guild guild = manager.getGuild(); - VoiceChannel channel = guild.getAudioManager().getConnectedChannel(); + VoiceChannel channel = (VoiceChannel) guild.getAudioManager().getConnectedChannel(); if (channel != null) { List members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList(); - if (members.size() == 0 || manager.getScheduler().getQueueManager().getQueue().isEmpty()) { + if (members.size() == 0 || manager.getScheduler().getQueueManager().isEmpty()) { LoggerFactory.getLogger(MusicPlayerAutoDisconnectThread.class).debug("Guild {}: auto-disconnect player from voice channel: {}", guild.getId(), channel.getId()); manager.leave(); } diff --git a/src/main/java/netneutrality/utils/ChannelFunction.java b/src/main/java/netneutrality/utils/ChannelFunction.java deleted file mode 100644 index abe112f..0000000 --- a/src/main/java/netneutrality/utils/ChannelFunction.java +++ /dev/null @@ -1,95 +0,0 @@ -package netneutrality.utils; - -import io.sentry.ISpan; -import io.sentry.ITransaction; -import io.sentry.Sentry; -import io.sentry.SpanStatus; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.TextChannel; -import netneutrality.thread.ThreadManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.OffsetDateTime; -import java.util.List; - -public class ChannelFunction { - - private static final Logger logger = LoggerFactory.getLogger(ChannelFunction.class); - - public static void bulkDeleteMessage(TextChannel channel, int toDelete) { - - if (toDelete >= 2500) return; - - ThreadManager.getExecutor("bulk-delete", ThreadManager.ExecutorType.FIXED).submit(() -> { - ITransaction transaction = Sentry.startTransaction(String.format("bulkDeleteMessage(%s, %d)", channel.getId(), toDelete), "task"); - transaction.setData("channel", channel.getId()); - transaction.setData("toDelete", toDelete); - transaction.setTag("operation", "bulk-delete"); - - try { - - int toDeleteFinal = toDelete + 1; - int iteration = toDeleteFinal / 100; - for (int i = 0; i < iteration; i++) { - ISpan innerSpan = transaction.startChild("task", "Processing iteration " + (i + 1) + "/" + iteration); - try { - List messages = channel.getHistory().retrievePast(100).complete(); - deleteFromList(messages, channel, innerSpan); - toDeleteFinal -= 100; - innerSpan.setStatus(SpanStatus.OK); - } catch (Exception e) { - innerSpan.setThrowable(e); - innerSpan.setStatus(SpanStatus.INTERNAL_ERROR); - throw e; - } finally { - innerSpan.finish(); - } - } - - if (toDeleteFinal > 0) { - ISpan innerSpan = transaction.startChild("task", String.format("Processing final iteration (%d messages)", toDeleteFinal)); - try { - List messages = channel.getHistory().retrievePast(toDeleteFinal).complete(); - deleteFromList(messages, channel, innerSpan); - innerSpan.setStatus(SpanStatus.OK); - } catch (Exception e) { - innerSpan.setThrowable(e); - innerSpan.setStatus(SpanStatus.INTERNAL_ERROR); - throw e; - } finally { - innerSpan.finish(); - } - - - innerSpan.finish(); - } - - transaction.setStatus(SpanStatus.OK); - } catch (Exception e) { - transaction.setThrowable(e); - transaction.setStatus(SpanStatus.INTERNAL_ERROR); - } finally { - transaction.finish(); - } - }); - } - - private synchronized static void deleteFromList(List messages, TextChannel channel, ISpan span) { - ISpan innerSpan = span.startChild("delete", "Executing delete of: " + messages.size() + " messages"); - try { - channel.deleteMessages(messages).complete(); - innerSpan.setStatus(SpanStatus.OK); - } catch (IllegalArgumentException e) { - innerSpan.setThrowable(e); - innerSpan.setStatus(SpanStatus.OUT_OF_RANGE); - messages.forEach(m -> { - if (!m.getTimeCreated().isBefore(OffsetDateTime.now().minusWeeks(2))) { - m.delete().queue(); - } - }); - } finally { - innerSpan.finish(); - } - } -} diff --git a/src/main/java/netneutrality/utils/ChannelMessagePurger.java b/src/main/java/netneutrality/utils/ChannelMessagePurger.java new file mode 100644 index 0000000..2cdd5ee --- /dev/null +++ b/src/main/java/netneutrality/utils/ChannelMessagePurger.java @@ -0,0 +1,171 @@ +package netneutrality.utils; +/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import io.sentry.ISpan; +import io.sentry.ITransaction; +import io.sentry.Sentry; +import io.sentry.SpanStatus; +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageHistory; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import netneutrality.core.context.CommandContext; +import netneutrality.thread.ThreadManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class ChannelMessagePurger { + + private static final Logger logger = LoggerFactory.getLogger(ChannelMessagePurger.class); + private static final Map instances = new HashMap<>(); + private final GuildMessageChannel channel; + private final Lock lock; + + private ChannelMessagePurger(GuildMessageChannel channel) { + this.channel = channel; + this.lock = new ReentrantLock(); + } + + public static ChannelMessagePurger getPurger(GuildMessageChannel channel) { + return instances.computeIfAbsent(channel, ChannelMessagePurger::new); + } + + public boolean canLockBeAcquired() { + boolean acquired = lock.tryLock(); + if (acquired) + lock.unlock(); + + return acquired; + } + + public void purgeMessages(CommandContext context, int toPurge) { + ThreadManager.getExecutor("bulk-delete", ThreadManager.ExecutorType.FIXED).submit(() -> { + if (lock.tryLock()) { + try { + int total = countAllChannelMessages(channel); + int finalToPurge = toPurge; + + if (finalToPurge > total) { + finalToPurge = total; + } + + safePurge(context, finalToPurge); + } finally { + lock.unlock(); + } + } + }); + } + + public void purgeChannel(CommandContext context) { + ThreadManager.getExecutor("bulk-delete", ThreadManager.ExecutorType.FIXED).submit(() -> { + if (lock.tryLock()) { + try { + int total = countAllChannelMessages(channel); + context.reply("Number of messages to purge: %d", total); + + safePurge(context, total); + } finally { + lock.unlock(); + } + } + }); + } + + private synchronized int countAllChannelMessages(GuildMessageChannel channel) { + logger.info("Guild {}: counting all messages from channel {}", channel.getGuild().getId(), channel.getId()); + MessageHistory history = channel.getHistory(); + int totalCount = 0; + int iterationCount = 0; + + while (totalCount == iterationCount) { + history.retrievePast(100).complete(); + + iterationCount += 100; + totalCount = history.size(); + } + logger.info("Guild {}: Counted {} messages from channel {}", channel.getGuild().getId(), totalCount, channel.getId()); + + return totalCount; + } + + private synchronized void safePurge(CommandContext context, int toPurge) { + ITransaction transaction = Sentry.startTransaction(String.format("purge messages from guild %s)", channel.getGuild().getId()), "task"); + transaction.setData("channel", channel.getId()); + transaction.setData("toPurge", toPurge); + + if (toPurge == 1) { + toPurge += 1; + } + + int tracker = toPurge; + int iteration = tracker / 100; + boolean olderThan2Weeks = false; + context.replyToMessage("Purging iterations to do: %d", iteration); + + try { + for (int i = 0; i < iteration; i++) { + ISpan innerSpan = transaction.startChild("retrieve-messages", "Processing iteration " + (i + 1) + "/" + iteration); + try { + List messages = channel.getHistory().retrievePast(100).complete(); + channel.deleteMessages(messages).complete(); + tracker -= 100; + innerSpan.setStatus(SpanStatus.OK); + } catch (IllegalArgumentException ex) { + olderThan2Weeks = true; + } catch (Exception e) { + innerSpan.setThrowable(e); + innerSpan.setStatus(SpanStatus.INTERNAL_ERROR); + } finally { + innerSpan.finish(); + } + } + + if (tracker > 0) { + ISpan innerSpan = transaction.startChild("retrieve-messages", String.format("Processing final iteration (%d messages)", tracker)); + try { + List messages = channel.getHistory().retrievePast(tracker).complete(); + channel.deleteMessages(messages).complete(); + innerSpan.setStatus(SpanStatus.OK); + } catch (IllegalArgumentException ex) { + olderThan2Weeks = true; + } catch (Exception e) { + innerSpan.setThrowable(e); + innerSpan.setStatus(SpanStatus.INTERNAL_ERROR); + } finally { + innerSpan.finish(); + } + + + innerSpan.finish(); + } + + } catch (Exception e) { + transaction.setThrowable(e); + transaction.setStatus(SpanStatus.INTERNAL_ERROR); + } finally { + transaction.finish(); + MessageCreateBuilder builder = new MessageCreateBuilder() + .addContent("Finished purging ") + .addContent(String.valueOf(toPurge)) + .addContent(" messages."); + + if (olderThan2Weeks) { + builder.addContent("\n").addContent("Messages older than two weeks weren't deleted."); + } + context.reply(builder.build()); + lock.unlock(); + } + } +} diff --git a/src/main/java/netneutrality/utils/MessageFunction.java b/src/main/java/netneutrality/utils/MessageFunction.java index a6069ca..8fbca70 100644 --- a/src/main/java/netneutrality/utils/MessageFunction.java +++ b/src/main/java/netneutrality/utils/MessageFunction.java @@ -14,20 +14,22 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.*; import netneutrality.command.AbstractCommand; import netneutrality.command.CommandType; +import netneutrality.core.CommandManager; import netneutrality.core.context.CommandContext; import netneutrality.exceptions.CommandException; -import netneutrality.exceptions.CommandExecutionException; import netneutrality.music.MusicManager; +import netneutrality.music.QueueManager; import netneutrality.music.SourceFinder; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.*; import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.*; +import java.util.Set; public class MessageFunction { @@ -35,7 +37,7 @@ public class MessageFunction { public static Member getStartingMention(Message message) { if (isStartingWithMention(message)) { - return message.getMentionedMembers().get(0); + return message.getMentions().getMembers().get(0); } return null; @@ -45,7 +47,7 @@ public class MessageFunction { if (!message.isFromType(ChannelType.TEXT)) return false; - List mentions = message.getMentionedMembers(); + List mentions = message.getMentions().getMembers(); for (Member member : mentions) { if (message.getContentDisplay().startsWith("@" + member.getUser().getName())) @@ -55,9 +57,8 @@ public class MessageFunction { return false; } - public static MessageEmbed buildCurrentlyPlayingMessage(MusicManager manager, Member user) { + public static MessageEmbed buildCurrentlyPlayingMessage(MusicManager manager) { AudioTrack current = manager.getCurrentlyPlaying(); - Member requester = (manager.getRequesterOfCurrentTrack() == null ? user : manager.getRequesterOfCurrentTrack()); EmbedBuilder message = new EmbedBuilder(); message.setAuthor("Link to track", String.format("https://youtu.be/%s", current.getIdentifier())); @@ -66,15 +67,13 @@ public class MessageFunction { message.setColor(Color.BLUE); message.addField("Track title: ", current.getInfo().title, false); message.addField("Duration: ", DurationFormatUtils.formatDuration(current.getPosition(), "**HH:mm:ss**", true) + " / " + DurationFormatUtils.formatDuration(current.getDuration(), "**HH:mm:ss**", true), false); - message.setFooter("Requested by " + requester.getEffectiveName(), requester.getAvatarUrl()); message.setTimestamp(OffsetDateTime.now()); return message.build(); } - public static MessageEmbed buildAddingTrackMessage(AudioTrack track, Member member, SourceFinder.AudioSourceType type) { + public static MessageEmbed buildAddingTrackMessage(AudioTrack track, SourceFinder.AudioSourceType type) { EmbedBuilder message = new EmbedBuilder(); - String avatarUrl = StringUtils.isEmpty(member.getAvatarUrl()) ? member.getUser().getAvatarUrl() : member.getAvatarUrl(); String url = track.getIdentifier(); String thumbnail = null; @@ -90,15 +89,13 @@ public class MessageFunction { message.setColor(Color.BLUE); message.addField("Track title: ", String.format("[**`%s`**](%s)", track.getInfo().title, url), false); message.addField("Duration: ", DurationFormatUtils.formatDuration(track.getDuration(), "**HH:mm:ss**", true), false); - message.setFooter("Requested by " + member.getEffectiveName(), avatarUrl); message.setTimestamp(OffsetDateTime.now()); return message.build(); } - public static MessageEmbed buildAddingPlaylistMessage(AudioPlaylist playlist, Member member, String identifier, SourceFinder.AudioSourceType type) { + public static MessageEmbed buildAddingPlaylistMessage(AudioPlaylist playlist, String identifier, SourceFinder.AudioSourceType type) { EmbedBuilder message = new EmbedBuilder(); - String avatarUrl = StringUtils.isEmpty(member.getAvatarUrl()) ? member.getUser().getAvatarUrl() : member.getAvatarUrl(); String url = identifier; String thumbnail = null; @@ -114,14 +111,12 @@ public class MessageFunction { message.setColor(Color.BLUE); message.addField("Playlist: ", String.format("[**`%s`**](%s)", playlist.getName(), url), false); message.addField("Number of tracks: ", String.valueOf(playlist.getTracks().size()), false); - message.setFooter("Requested by " + member.getEffectiveName(), avatarUrl); message.setTimestamp(OffsetDateTime.now()); return message.build(); } - public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member member, boolean currentlyPlaying) { - String avatarUrl = StringUtils.isEmpty(member.getAvatarUrl()) ? member.getUser().getAvatarUrl() : member.getAvatarUrl(); + public static MessageEmbed buildViewQueueMessage(MusicManager manager, boolean currentlyPlaying) { ArrayList tracks = manager.getScheduler().getQueueManager().getAllQueuedTrack(); AudioTrack current = manager.getCurrentlyPlaying(); int numberofTracksShown = Math.min(tracks.size(), 10); @@ -143,56 +138,55 @@ public class MessageFunction { if (index == 10) break; } - message.setFooter("Requested by " + member.getEffectiveName(), avatarUrl); message.setTimestamp(OffsetDateTime.now()); return message.build(); } - public static MessageEmbed buildHelpMessage(AbstractCommand command) { + public static MessageEmbed buildUserTracksMessage(MusicManager manager, Member member) { + List tracks = manager.getScheduler() + .getQueueManager() + .getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester())); + AudioTrack current = manager.getCurrentlyPlaying(); + int numberofTracksShown = Math.min(tracks.size(), 10); + EmbedBuilder message = new EmbedBuilder(); - message.setTitle(":information_source: Help for command " + command.getCommandName()); + message.setTitle(String.format("Tracks added by %s (showing %d out of %d items):", member.getAsMention(), numberofTracksShown, tracks.size())); message.setColor(Color.BLUE); - message.addField("Command", command.getCommandName(), false); - message.addField("Description", command.getDescription(), false); - message.addField("Aliases", ((command.getAlias() == null || command.getAlias().length == 0) ? "None" : Arrays.toString(command.getAlias()).replaceAll("[\\[\\]]", "")), false); - message.addField("Usage", (command.getUsage() == null ? "No arguments defined" : command.getUsage().toString()), false); + + int index = 0; + for (QueueManager.TrackInfo track : tracks) { + AudioTrackInfo info = track.getTrack().getInfo(); + + if (current != null && current.getInfo().title.equals(info.title)) { + message.addField("Track #" + ++index + " (Currently playing)", info.title, false); + } else { + message.addField("Track #" + ++index, info.title, false); + } + + if (index == 10) break; + } + message.setTimestamp(OffsetDateTime.now()); return message.build(); } - public static ArrayList buildHelpMessage(HashMap commands, boolean admin) { - - SortedSet keys = new TreeSet<>(commands.keySet()); + public static ArrayList buildHelpMessage(Member member) { HashMap help = new HashMap<>(); - for (String commandName : keys) { - AbstractCommand command = commands.get(commandName); + for (AbstractCommand command : CommandManager.commands.values()) { if (command.getType() == null) continue; String description = "No description yet"; - StringBuilder availAlias = new StringBuilder(); if (command.isEnabled() && command.getType() != CommandType.UNLISTED) { - if (command.getType() == CommandType.ADMINISTRATION && !admin) + if (command.getType() == CommandType.ADMINISTRATION && !UserFunction.isAdmin(member)) continue; if (command.getDescription() != null) description = command.getDescription(); - if (command.getAlias() != null && command.getAlias().length > 0) { - availAlias.append("("); - for (String str : command.getAlias()) { - availAlias.append(str).append(","); - } - - if (!availAlias.isEmpty()) { - availAlias.deleteCharAt(availAlias.lastIndexOf(",")); - availAlias.append(")"); - } - } - EmbedBuilder message; if (help.containsKey(command.getType())) { message = help.get(command.getType()); @@ -205,9 +199,8 @@ public class MessageFunction { message.setColor(Color.BLUE); } - message.addField(String.format("%s %s", command.getCommandName(), availAlias), description, false); + message.addField(String.format("%s", command.getCommandName()), description, false); } - } ArrayList messages = new ArrayList<>(); diff --git a/src/main/java/netneutrality/utils/MusicUtils.java b/src/main/java/netneutrality/utils/MusicUtils.java index a4caf74..dbee39b 100644 --- a/src/main/java/netneutrality/utils/MusicUtils.java +++ b/src/main/java/netneutrality/utils/MusicUtils.java @@ -16,10 +16,11 @@ public class MusicUtils { public static boolean checkIfConnected(MusicManager manager, CommandContext context) { if (context.getGuildUser().getVoiceState() == null) { + context.reply("Voice state is null, this should not happen"); throw new CommandExecutionException("Voice state is null, this should not happen", context, null); } - if (!context.getGuildUser().getVoiceState().inVoiceChannel()) { + if (!context.getGuildUser().getVoiceState().inAudioChannel()) { context.reply("You are not in a voice channel"); return false; } @@ -29,7 +30,7 @@ public class MusicUtils { return false; } - VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel(); + VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel(); if (manager.isNotConnectedTo(vChannel)) { context.reply("The bot isn't in your voice channel"); return false;