Implement slash commands

Update JDA to 5.0.0-alpha.18
Simplify commands by removing aliases and usage
Remove prefix config option
Separate skip and voteskip commands
Add new shuffle command
Remove duplicate crypto commands
This commit is contained in:
anthorob committed 2022-09-18 20:04:57 -04:00
1 parent b5b0e209c7
commit 63556d6081
59 files changed
+1830 -1523

No files matched your search

+2 -2
View File
@@ -6,7 +6,7 @@
<groupId>com.xtremcommunity.discord</groupId>
<artifactId>netneutrality</artifactId>
<version>1.0.0</version>
<version>2.0.0</version>
<name>netneutrality-discord-bot</name>
<description>Discord bot</description>
<packaging>jar</packaging>
@@ -108,7 +108,7 @@
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>4.4.0_350</version>
<version>5.0.0-alpha.18</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.sedmelluq</groupId>-->
@@ -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("[email protected]");
//options.setDebug(env.equals("dev"));
options.setDebug(false);
options.setRelease("[email protected]");
});
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();
@@ -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
}
}
@@ -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()) {
@@ -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<Argument> 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();
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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<OBlacklist> 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);
}
}
}
@@ -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");
}
}
}
}
@@ -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<MessageEmbed> help = MessageFunction.buildHelpMessage(CommandManager.commands, UserFunction.isAdmin(context.getGuildUser()));
List<MessageEmbed> 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<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
context.replyEphemeral("Help has been sent in DM!");
context.sendMessageInPrivate(help);
}
}
@@ -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()));
}
}
@@ -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<Role> roles = context.getMessage().getMentionedRoles();
List<Role> 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<TextChannel> channels = context.getMessage().getMentionedChannels();
List<TextChannel> 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 ("music-channel".equals(sub)) {
GuildChannelUnion channel = context.getEvent().getOption("channel", OptionMapping::getAsChannel);
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;
}
}
}
if (args.length == 2) {
String prefix = args[1].trim();
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);
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()));
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<String, String> 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());
}
} else {
context.reply(getUsage());
builder.addContent(o.getName()).addContent(": ").addContent(value);
builder.addContent("\n");
}
context.replyEphemeral(builder.build());
}
}
}
@@ -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);
}
});
}
}
@@ -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);
}
});
}
}
@@ -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);
}
});
}
}
@@ -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);
}
});
}
}
@@ -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<OBlacklist> 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);
}
}
}
}
@@ -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();
}
}
@@ -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?");
}
}
@@ -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!");
}
}
}
@@ -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));
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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);
}
}
@@ -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());
}
}
}
}
}
@@ -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.");
}
}
}
@@ -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);
}
}
}
}
@@ -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");
}
}
}
@@ -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!");
}
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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<String, AbstractCommand> commands = new HashMap<>();
public static final HashMap<String, AbstractCommand> 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<CommandData> 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();
}
}
@@ -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<String> 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;
}
@@ -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());
}
}
@@ -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<? extends MessageEmbed> 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<? extends MessageEmbed> 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));
}
}
}
@@ -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<? extends MessageEmbed> 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));
}
}
}
@@ -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<? extends MessageEmbed> 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));
}
}
}
@@ -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;
@@ -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<OBlacklist> getBlacklistForGuild(CommandContext context) {
GuildManager guildManager = GuildManager.getManager(context.getGuild());
public ArrayList<OBlacklist> getBlacklistForGuild(Guild guild) {
GuildManager guildManager = GuildManager.getManager(guild);
Session session = getSessionFactory().openSession();
OGuild guildEntity = guildManager.getGuildEntity();
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
@@ -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();
}
}
@@ -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);
}
}
@@ -1,4 +1,4 @@
package netneutrality.handler.hooks.guilds;
package netneutrality.hooks.guilds;
/*
* Copyright (c) anthorob
@@ -1,4 +1,4 @@
package netneutrality.handler.hooks.message;
package netneutrality.hooks.message;
/*
* Copyright (c) anthorob
@@ -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);
}
}
}
@@ -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) {
}
}
@@ -1,4 +1,4 @@
package netneutrality.handler.hooks.voice;
package netneutrality.hooks.voice;
/*
* Copyright (c) anthorob
*
@@ -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;
@@ -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<String, AudioSourceManager> 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<LoadableSong> 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<QueueManager.TrackInfo> 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;
}
}
@@ -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<TrackInfo> getQueue() {
public List<TrackInfo> 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<TrackInfo> getQueueWithFilter(Predicate<? super TrackInfo> 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<AudioTrack> getAllQueuedTrack() {
synchronized (queue) {
ArrayList<AudioTrack> 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,9 +252,9 @@ public class QueueManager {
} else {
player.stopTrack();
}
}
}
}
public void shuffle() {
synchronized (queue) {
@@ -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<TrackInfo> newQueue = new ArrayList<>(queue);
List<TrackInfo> newQueue = new ArrayList<>(queue);
newQueue.remove(index - 1);
queue.clear();
@@ -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,12 +103,10 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
break;
}
synchronized (scheduler.getQueueManager().getQueue()) {
scheduler.getQueueManager().queue(track, context.getGuildUser());
}
}
}
}
@Override
public void noMatches() {
@@ -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);
}
}
@@ -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<Member> 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();
}
@@ -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<Message> 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<Message> 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<Message> 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();
}
}
}
@@ -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<GuildMessageChannel, ChannelMessagePurger> 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<Message> 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<Message> 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();
}
}
}
@@ -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<Member> mentions = message.getMentionedMembers();
List<Member> 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<AudioTrack> 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<QueueManager.TrackInfo> 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<MessageEmbed> buildHelpMessage(HashMap<String, AbstractCommand> commands, boolean admin) {
SortedSet<String> keys = new TreeSet<>(commands.keySet());
public static ArrayList<MessageEmbed> buildHelpMessage(Member member) {
HashMap<CommandType, EmbedBuilder> 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<MessageEmbed> messages = new ArrayList<>();
@@ -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;