Implement built-in tasks, improve message commands with arguments, fix command context in DM, better help message

This commit is contained in:
anthorob committed 2024-11-21 01:33:55 -05:00
1 parent e0ef3f9915
commit e6afe6e4c1
39 files changed
+436 -213

No files matched your search

@@ -23,4 +23,9 @@ public enum CommandType {
public int getId() { public int getId() {
return id; return id;
} }
@Override
public String toString() {
return description;
}
} }
@@ -6,13 +6,23 @@ package me.bobert.netneutrality.command.basic;
*/ */
import me.bobert.netneutrality.command.AbstractCommand; import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.Command;
import me.bobert.netneutrality.command.CommandType; import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.context.MessageCommandContext; import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext; import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandManager;
import me.bobert.netneutrality.core.context.Context;
import me.bobert.netneutrality.core.tasks.BuiltInTask;
import me.bobert.netneutrality.tasks.help.BotInfo;
import me.bobert.netneutrality.tasks.help.CommandHelp;
import me.bobert.netneutrality.tasks.help.ReportBug;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.MessageEmbed; 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.SlashCommandData;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import java.util.List; import java.util.List;
@@ -47,6 +57,7 @@ public class HelpCommand extends AbstractCommand {
@Override @Override
public void registerSlashCommandOptions(SlashCommandData data) { public void registerSlashCommandOptions(SlashCommandData data) {
data.addOption(OptionType.STRING, "command", "The name of the command you want help with", false);
} }
@Override @Override
@@ -56,21 +67,36 @@ public class HelpCommand extends AbstractCommand {
@Override @Override
public CommandExecutionResult execute(MessageCommandContext context) { public CommandExecutionResult execute(MessageCommandContext context) {
if (context.getArgs().length == 0) {
List<MessageEmbed> help = MessageFunction.buildHelpMessage();
context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build()); context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build());
context.sendMessageInPrivate(help); sendHelp(context, context.getArgument(0));
}
return CommandExecutionResult.none(); return CommandExecutionResult.none();
} }
@Override @Override
public CommandExecutionResult execute(SlashCommandContext context) { public CommandExecutionResult execute(SlashCommandContext context) {
List<MessageEmbed> help = MessageFunction.buildHelpMessage(); sendHelp(context, context.getEvent().getOption("command", null, OptionMapping::getAsString));
context.sendMessageInPrivate(help);
return CommandExecutionResult.success("Help has been sent in DM!"); return CommandExecutionResult.success("Help has been sent in DM!");
} }
public void sendHelp(Context context, String commandStr) {
if (commandStr == null) {
Button commands = BuiltInTask.createButton(CommandHelp.class).primary("Commands");
Button info = BuiltInTask.createButton(BotInfo.class).primary("Info");
Button reportbug = BuiltInTask.createButton(ReportBug.class).danger("Report bug");
ActionRow row = ActionRow.of(commands, info, reportbug);
context.sendMessageInPrivate("Thanks for using the Net Neutrality Bot! What do you need help with?", List.of(row));
return;
}
Command command = CommandManager.getCommand(commandStr);
if (command == null) {
context.reply("Command does not exist");
return;
}
context.reply(MessageFunction.buildCommandHelpMessage(command));
}
} }
@@ -51,7 +51,7 @@ public class PingCommand extends AbstractCommand {
@Override @Override
public CommandExecutionResult execute(MessageCommandContext context) { public CommandExecutionResult execute(MessageCommandContext context) {
if (context.getArgs().length == 0) if (context.getArgs().isEmpty())
context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing())); context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -64,14 +64,14 @@ public class ConfigureCommand extends AbstractAdminCommand {
@Override @Override
public CommandExecutionResult execute(MessageCommandContext context) { public CommandExecutionResult execute(MessageCommandContext context) {
String[] args = context.getArgs(); Map<Integer, String> args = context.getArgs();
GuildManager guildManager = GuildManager.getManager(context.getGuild()); GuildManager guildManager = GuildManager.getManager(context.getGuild());
if (args.length > 0) { if (!args.isEmpty()) {
// Role for music commands // Role for music commands
if (context.getArgument(0).equals("music-role")) { if (context.getArgument(0).equals("music-role")) {
if (args.length == 1) { if (args.size() == 1) {
String roleDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE); String roleDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
Role role = null; Role role = null;
if (!roleDb.isEmpty()) { if (!roleDb.isEmpty()) {
@@ -82,7 +82,7 @@ public class ConfigureCommand extends AbstractAdminCommand {
context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'"); context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'");
} }
if (args.length == 2) { if (args.size() == 2) {
if (context.getArgument(1).equals("reset")) { if (context.getArgument(1).equals("reset")) {
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE).isEmpty()) { if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE).isEmpty()) {
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null); guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
@@ -104,7 +104,7 @@ public class ConfigureCommand extends AbstractAdminCommand {
// Role for music commands // Role for music commands
if (context.getArgument(0).equals("music-channel")) { if (context.getArgument(0).equals("music-channel")) {
if (args.length == 1) { if (args.size() == 1) {
String channelDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL); String channelDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
TextChannel channel = null; TextChannel channel = null;
if (!channelDb.isEmpty()) { if (!channelDb.isEmpty()) {
@@ -115,7 +115,7 @@ public class ConfigureCommand extends AbstractAdminCommand {
context.reply("Reset channel using 'conf music-channel reset' or set it using 'conf music-channel #'"); context.reply("Reset channel using 'conf music-channel reset' or set it using 'conf music-channel #'");
} }
if (args.length == 2) { if (args.size() == 2) {
if (context.getArgument(1).equals("reset")) { if (context.getArgument(1).equals("reset")) {
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL).isEmpty()) { if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL).isEmpty()) {
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null); guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
@@ -137,16 +137,16 @@ public class ConfigureCommand extends AbstractAdminCommand {
// Default volume for music player // Default volume for music player
if (context.getArgument(0).equals("music-volume")) { if (context.getArgument(0).equals("music-volume")) {
if (args.length == 1) { if (args.size() == 1) {
int volume = Integer.parseInt(guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME)); int volume = Integer.parseInt(guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME));
context.reply("Default volume for music player: %d", volume); context.reply("Default volume for music player: %d", volume);
context.reply("Set using 'conf music-volume <value between 0 and 100>'"); context.reply("Set using 'conf music-volume <value between 0 and 100>'");
} }
if (args.length == 2) { if (args.size() == 2) {
try { try {
int volume = Integer.parseInt(args[1]); int volume = Integer.parseInt(args.get(1));
if (volume > 0 && volume <= 100) { if (volume > 0 && volume <= 100) {
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume)); guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
@@ -164,15 +164,15 @@ public class ConfigureCommand extends AbstractAdminCommand {
// Debug // Debug
if (context.getArgument(0).equals("debug")) { if (context.getArgument(0).equals("debug")) {
if (args.length == 1) { if (args.size() == 1) {
boolean debug = guildManager.isDebugEnabled(); boolean debug = guildManager.isDebugEnabled();
context.reply("Error logging is currently %s for this guild.", (debug ? "enabled" : "disabled")); context.reply("Error logging is currently %s for this guild.", (debug ? "enabled" : "disabled"));
context.reply("Use 'conf debug 0/1' to toggle between states (0 = disabled, 1 = enabled)."); context.reply("Use 'conf debug 0/1' to toggle between states (0 = disabled, 1 = enabled).");
} }
if (args.length == 2) { if (args.size() == 2) {
String str = args[1]; String str = args.get(1);
boolean debug = guildManager.isDebugEnabled(); boolean debug = guildManager.isDebugEnabled();
if (str.equals("0") || str.equalsIgnoreCase("disable")) { if (str.equals("0") || str.equalsIgnoreCase("disable")) {
debug = false; debug = false;
@@ -21,6 +21,7 @@ import org.json.JSONObject;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.Currency; import java.util.Currency;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
public class CryptoPriceCommand extends AbstractCommand { public class CryptoPriceCommand extends AbstractCommand {
@@ -59,27 +60,27 @@ public class CryptoPriceCommand extends AbstractCommand {
@Override @Override
public CommandType getType() { public CommandType getType() {
return CommandType.CRYPTO; return CommandType.UTILITY;
} }
@Override @Override
public CommandExecutionResult execute(MessageCommandContext context) { public CommandExecutionResult execute(MessageCommandContext context) {
String[] args = context.getArgs(); Map<Integer, String> args = context.getArgs();
String currency = "USD"; String currency = "USD";
if (args.length == 0) { if (args.isEmpty()) {
return CommandExecutionResult.warn("Usage: crypto <symbol> <currency>"); return CommandExecutionResult.warn("Usage: crypto <symbol> <currency>");
} }
if (args.length == 2) { if (args.size() == 2) {
try { try {
currency = Currency.getInstance(args[1].toUpperCase()).getCurrencyCode(); currency = Currency.getInstance(args.get(1).toUpperCase()).getCurrencyCode();
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
return CommandExecutionResult.warn("Please enter a valid currency"); return CommandExecutionResult.warn("Please enter a valid currency");
} }
} }
String crypto = args[0].toUpperCase(); String crypto = args.get(0).toUpperCase();
String finalCurrency = currency; String finalCurrency = currency;
context.async("Retreiving price, please wait...", asyncMessage -> { context.async("Retreiving price, please wait...", asyncMessage -> {
@@ -41,7 +41,7 @@ public class PlayCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) { public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild()); MusicManager manager = MusicManager.get(context.getGuild());
if (context.getArgs().length == 0) { if (context.getArgs().isEmpty()) {
return CommandExecutionResult.warn("Please specify a query"); return CommandExecutionResult.warn("Please specify a query");
} }
@@ -50,7 +50,7 @@ public class SkipCommand extends AbstractMusicCommand {
} }
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) { if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
if (UserFunction.isAdmin(context.getGuildUser()) && context.getArgs().length > 0) { if (UserFunction.isAdmin(context.getGuildUser()) && !context.getArgs().isEmpty()) {
try { try {
int skips = Integer.parseInt(context.getArgument(0)); int skips = Integer.parseInt(context.getArgument(0));
return manager.getQueueManager().skip(skips); return manager.getQueueManager().skip(skips);
@@ -28,7 +28,7 @@ public class AsyncContext extends AbstractContext {
} }
public AsyncContext(Message message) { public AsyncContext(Message message) {
super(message.getJDA(), message.getGuild(), message.getChannel(), message.getMember(), message.getAuthor()); super(message.getJDA(), message.isFromGuild() ? message.getGuild() : null, message.getChannel(), message.getMember(), message.getAuthor());
this.hook = null; this.hook = null;
this.message = message; this.message = message;
} }
@@ -14,7 +14,9 @@ import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -23,7 +25,7 @@ public class MessageCommandContext extends CommandContext {
private final Message message; private final Message message;
public MessageCommandContext(MessageReceivedEvent event) { public MessageCommandContext(MessageReceivedEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getAuthor()); super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getAuthor());
this.event = event; this.event = event;
this.message = event.getMessage(); this.message = event.getMessage();
} }
@@ -34,24 +36,31 @@ public class MessageCommandContext extends CommandContext {
} }
public String getArgument(int index) { public String getArgument(int index) {
return getArgs()[index]; return getArgs().get(index);
} }
public String[] getArgs() { public Map<Integer, String> getArgs() {
String msg = message.getContentDisplay().trim(); Map<Integer, String> args = new HashMap<>();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0];
String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split(" ");
if (args.length == 1 && args[0].isEmpty()) String msg = message.getContentDisplay().trim();
args = new String[0]; msg = msg.substring(Settings.getInstance().getPrefix().length()).trim();
String cmd = msg.split(" ", 2)[0];
String[] array = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split(" ");
for (int i = 0; i < array.length; i++) {
args.put(i, array[i]);
}
if (args.size() == 1 && args.get(0).isEmpty()) {
args.remove(0);
}
return args; return args;
} }
public String getMessageText() { public String getMessageText() {
String msg = message.getContentDisplay().trim(); String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim(); msg = msg.substring(Settings.getInstance().getPrefix().length()).trim();
String cmd = msg.split(" ", 2)[0]; String cmd = msg.split(" ", 2)[0];
return msg.replaceFirst("(" + cmd + ")( )?", ""); return msg.replaceFirst("(" + cmd + ")( )?", "");
@@ -123,7 +132,7 @@ public class MessageCommandContext extends CommandContext {
@Override @Override
public String parseCommandName() { public String parseCommandName() {
String rawMessage = message.getContentDisplay().trim(); String rawMessage = message.getContentDisplay().trim();
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$"); String prefix = Settings.getInstance().getPrefix();
if (rawMessage.startsWith(prefix)) { if (rawMessage.startsWith(prefix)) {
rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim(); rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim();
@@ -137,7 +146,7 @@ public class MessageCommandContext extends CommandContext {
@Override @Override
public String getSubCommand() { public String getSubCommand() {
if (getArgs().length > 0) { if (!getArgs().isEmpty()) {
return getArgument(0); return getArgument(0);
} }
@@ -7,8 +7,6 @@ package me.bobert.netneutrality.context.tasks;
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.interactions.components.LayoutComponent;
@@ -19,20 +17,12 @@ import java.util.function.Consumer;
public class EmojiTaskContext extends TaskContext { public class EmojiTaskContext extends TaskContext {
private final MessageReactionAddEvent event; private final MessageReactionAddEvent event;
private final EmojiUnion emoji;
public EmojiTaskContext(MessageReactionAddEvent event) { public EmojiTaskContext(MessageReactionAddEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser()); super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getUser());
this.event = event; this.event = event;
this.emoji = event.getEmoji(); addContextData("emoji", event.getEmoji());
} addContextData("emoji-code", event.getEmoji().getAsReactionCode());
public String getEmoji() {
return emoji.getAsReactionCode();
}
public MessageReaction getReaction() {
return event.getReaction();
} }
@Override @Override
@@ -15,6 +15,7 @@ public class ModalTaskContext extends TaskContext implements InteractionContext
public ModalTaskContext(ModalInteractionEvent event) { public ModalTaskContext(ModalInteractionEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser()); super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
this.event = event; this.event = event;
addContextData("values", event.getValues());
} }
@Override @Override
@@ -20,7 +20,6 @@ import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData; import net.dv8tion.jda.api.utils.messages.MessageEditData;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -69,11 +68,19 @@ public interface Context {
reply(messageCreateData, false, List.of(), asyncMessage -> {}); reply(messageCreateData, false, List.of(), asyncMessage -> {});
} }
default void reply(MessageCreateData messageCreateData, List<LayoutComponent> components) {
reply(messageCreateData, false, components, asyncMessage -> {});
}
default void reply(String message) { default void reply(String message) {
reply(MessageCreateData.fromContent(message)); reply(MessageCreateData.fromContent(message));
} }
default void reply(MessageEmbed... embeds) { default void reply(MessageEmbed embed) {
reply(List.of(embed));
}
default void reply(List<MessageEmbed> embeds) {
reply(MessageCreateData.fromEmbeds(embeds)); reply(MessageCreateData.fromEmbeds(embeds));
} }
@@ -103,13 +110,17 @@ public interface Context {
} }
default void edit(MessageEmbed embed) { default void edit(MessageEmbed embed) {
edit(MessageEditData.fromEmbeds(embed)); edit(List.of(embed), List.of());
} }
default void edit(MessageEmbed embeds, List<LayoutComponent> components) { default void edit(List<MessageEmbed> embeds, List<LayoutComponent> components) {
edit(MessageEditData.fromEmbeds(embeds), components); edit(MessageEditData.fromEmbeds(embeds), components);
} }
default void edit(MessageEmbed embed, List<LayoutComponent> components) {
edit(List.of(embed), components);
}
default void edit(String format, Object... arguments) { default void edit(String format, Object... arguments) {
edit(String.format(format, arguments)); edit(String.format(format, arguments));
} }
@@ -154,11 +165,15 @@ public interface Context {
} }
default void sendMessageInPrivate(String message) { default void sendMessageInPrivate(String message) {
sendMessageInPrivate(MessageCreateData.fromContent(message)); sendMessageInPrivate(message, List.of());
} }
default void sendMessageInPrivate(MessageEmbed... embeds) { default void sendMessageInPrivate(String message, List<LayoutComponent> components) {
sendMessageInPrivate(Arrays.asList(embeds)); sendMessageInPrivate(MessageCreateData.fromContent(message), components);
}
default void sendMessageInPrivate(MessageEmbed embed) {
sendMessageInPrivate(List.of(embed));
} }
default void sendMessageInPrivate(List<MessageEmbed> embeds) { default void sendMessageInPrivate(List<MessageEmbed> embeds) {
@@ -12,10 +12,15 @@ import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.interactions.modals.Modal;
import java.util.HashMap;
import java.util.Map;
public abstract class TaskContext extends AbstractContext { public abstract class TaskContext extends AbstractContext {
private final Map<String, Object> contextData;
public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) { public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
super(jda, guild, channel, member, user); super(jda, guild, channel, member, user);
this.contextData = new HashMap<>();
} }
@Override @Override
@@ -29,4 +34,12 @@ public abstract class TaskContext extends AbstractContext {
} }
public abstract String getId(); public abstract String getId();
public void addContextData(String key, Object value) {
contextData.put(key, value);
}
public Object getContextData(String key) {
return contextData.get(key);
}
} }
@@ -0,0 +1,36 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.CommandContext;
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
@Override
public String getName() {
return getId();
}
@Override
public boolean isInsertable() {
return true;
}
@Override
public boolean oneshot() {
return false;
}
@Override
public boolean isExpired() {
return false;
}
@Override
public void replyToInteraction(CommandContext context) {
// Task is loaded at runtime, this will never be called
}
}
@@ -27,7 +27,7 @@ public abstract class AbstractPersistableTask extends AbstractTask implements Pe
@Override @Override
public void replyToInteraction(CommandContext context) { public void replyToInteraction(CommandContext context) {
// Task loaded from database, this will never be called
} }
@Override @Override
@@ -25,6 +25,11 @@ public abstract class AbstractTask implements Task {
return creationTime; return creationTime;
} }
@Override
public TaskButtonCreator createButton() {
return new TaskButtonCreator(getId());
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@@ -0,0 +1,23 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import java.lang.reflect.InvocationTargetException;
/**
* Represents a task that has a known ID and is loaded at runtime.
* This can be any task and is not necessarily a button.
*/
public interface BuiltInTask extends Task {
static TaskButtonCreator createButton(Class<? extends BuiltInTask> clazz) {
try {
BuiltInTask task = clazz.getConstructor().newInstance();
return new TaskButtonCreator(task.getId());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
@@ -8,11 +8,11 @@ package me.bobert.netneutrality.core.tasks;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.WithChildren; import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
public interface Task { public interface Task {
/** /**
* <p>This method should generate a unique ID to differentiate it from other tasks.</p> * <p>This method should generate a unique ID to differentiate it from other tasks.</p>
* <p> * <p>
@@ -59,4 +59,30 @@ public interface Task {
boolean isExpired(); boolean isExpired();
OffsetDateTime getCreationTime(); OffsetDateTime getCreationTime();
TaskButtonCreator createButton();
class TaskButtonCreator {
private final String id;
public TaskButtonCreator(String id) {
this.id = id;
}
public Button primary(String label) {
return Button.primary(id, label);
}
public Button secondary(String label) {
return Button.secondary(id, label);
}
public Button success(String label) {
return Button.success(id, label);
}
public Button danger(String label) {
return Button.danger(id, label);
}
}
} }
@@ -21,28 +21,22 @@ import me.bobert.netneutrality.thread.CleanupTaskThread;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import java.util.Collection; import java.lang.reflect.InvocationTargetException;
import java.util.HashMap; import java.lang.reflect.Modifier;
import java.util.List; import java.util.*;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class TaskManager { public class TaskManager {
private static final Logger logger = LogManager.getLogger(TaskManager.class); private static final Logger logger = LogManager.getLogger(TaskManager.class);
private static final ScheduledExecutorService executor = ThreadManager.getScheduledExecutor();
private static TaskManager instance; private static TaskManager instance;
private final TaskService taskService; private final TaskService taskService;
private final Map<String, Task> tasks; private final Map<String, Task> tasks;
private final Map<String, PersistableTask> persistableTasks;
private final Map<String, CommandContext> deletableTasksContext; private final Map<String, CommandContext> deletableTasksContext;
private ScheduledFuture<?> cleanupThread;
private TaskManager() { private TaskManager() {
this.tasks = new HashMap<>(); this.tasks = new HashMap<>();
this.persistableTasks = new HashMap<>();
this.deletableTasksContext = new HashMap<>(); this.deletableTasksContext = new HashMap<>();
this.taskService = new TaskService(this); this.taskService = new TaskService(this);
} }
@@ -56,7 +50,10 @@ public class TaskManager {
} }
public void init() { public void init() {
loadBuiltInTasks();
taskService.loadTasks(); taskService.loadTasks();
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
} }
public TaskService getTaskService() { public TaskService getTaskService() {
@@ -64,37 +61,25 @@ public class TaskManager {
} }
public Task getTask(String id) { public Task getTask(String id) {
Task task = tasks.get(id); return tasks.get(id);
if (task == null) {
task = persistableTasks.get(id);
}
return task;
} }
public Collection<Task> getTasks() { public Collection<Task> getTasks() {
return tasks.values(); return tasks.values();
} }
public Map<String, PersistableTask> getPersistableTaskMap() { public Map<String, Task> getTaskMap() {
return persistableTasks; return tasks;
} }
public boolean exists(String id) { public boolean exists(String id) {
boolean exists = tasks.containsKey(id); return tasks.containsKey(id);
if (!exists) {
exists = persistableTasks.containsKey(id);
}
return exists;
} }
public void insert(CommandContext context, Task task) { public void insert(CommandContext context, Task task) {
String id = task.getId(); String id = task.getId();
if (tasks.containsKey(id) || persistableTasks.containsKey(id)) { if (tasks.containsKey(id)) {
context.reply("A task with the same id already exists!"); context.reply("A task with the same id already exists!");
return; return;
} }
@@ -108,12 +93,11 @@ public class TaskManager {
if (task.isInsertable()) { if (task.isInsertable()) {
logger.info("Inserting task: {}", task); logger.info("Inserting task: {}", task);
if (task instanceof PersistableTask) { if (task instanceof PersistableTask persistableTask) {
taskService.persistTask((PersistableTask) task); taskService.persistTask(persistableTask);
persistableTasks.put(id, (PersistableTask) task);
} else {
tasks.put(id, task);
} }
tasks.put(id, task);
} }
if (task instanceof WithChildren taskWithChildren) { if (task instanceof WithChildren taskWithChildren) {
@@ -123,10 +107,6 @@ public class TaskManager {
insert(context, child); insert(context, child);
} }
} }
if (!tasks.isEmpty() && cleanupThread == null) {
cleanupThread = executor.scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
}
} }
public void removeTask(Task task) { public void removeTask(Task task) {
@@ -136,19 +116,11 @@ public class TaskManager {
logger.debug("Removing task: {}", task); logger.debug("Removing task: {}", task);
tasks.remove(id); tasks.remove(id);
} }
if (tasks.isEmpty() && cleanupThread != null) {
cleanupThread.cancel(false);
cleanupThread = null;
}
} }
public void handle(TaskContext context) { public void handle(TaskContext context) {
Task task = tasks.get(context.getId()); String taskId = context.getId();
Task task = tasks.get(taskId);
if (task == null) {
task = persistableTasks.get(context.getId());
}
if (task == null && (context instanceof ModalTaskContext || context instanceof ButtonTaskContext)) { if (task == null && (context instanceof ModalTaskContext || context instanceof ButtonTaskContext)) {
context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired"); context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired");
@@ -182,6 +154,27 @@ public class TaskManager {
} }
} }
private void loadBuiltInTasks() {
logger.info("Loading built-in tasks...");
Set<Class<? extends BuiltInTask>> classes = new Reflections("me.bobert.netneutrality.tasks").getSubTypesOf(BuiltInTask.class);
for (Class<? extends BuiltInTask> s : classes) {
try {
if (Modifier.isAbstract(s.getModifiers())) {
continue;
}
BuiltInTask task = s.getConstructor().newInstance();
logger.info("Loaded task: {}", task);
tasks.put(task.getId(), task);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
logger.error("Failed to instantiate built-in task", e);
}
}
}
private void deleteInteraction(Task task) { private void deleteInteraction(Task task) {
if (task instanceof WithParent<?> taskWithParent) { if (task instanceof WithParent<?> taskWithParent) {
Task parent = (Task) taskWithParent.getParentTask(); Task parent = (Task) taskWithParent.getParentTask();
@@ -220,6 +213,10 @@ public class TaskManager {
} }
private void removeTaskAndChildTasks(Task task) { private void removeTaskAndChildTasks(Task task) {
if (task instanceof BuiltInTask) {
return;
}
tasks.remove(task.getId()); tasks.remove(task.getId());
if (task instanceof WithParent<?> taskWithParent && !taskWithParent.shouldKeepChilds()) { if (task instanceof WithParent<?> taskWithParent && !taskWithParent.shouldKeepChilds()) {
@@ -233,6 +230,10 @@ public class TaskManager {
} }
private void reinsertTaskAndChildTasks(Task task) { private void reinsertTaskAndChildTasks(Task task) {
if (task instanceof BuiltInTask) {
return;
}
tasks.put(task.getId(), task); tasks.put(task.getId(), task);
if (task instanceof WithParent<?> taskWithParent && taskWithParent.shouldKeepChilds()) { if (task instanceof WithParent<?> taskWithParent && taskWithParent.shouldKeepChilds()) {
@@ -11,7 +11,6 @@ import me.bobert.netneutrality.core.tasks.TaskManager;
import java.util.List; import java.util.List;
public interface WithChildren { public interface WithChildren {
List<Task> getChildTasks(); List<Task> getChildTasks();
default void removeAllChildTasks() { default void removeAllChildTasks() {
@@ -10,7 +10,6 @@ public enum GSettings {
MUSIC_ROLE("MUSIC_ROLE", ""), MUSIC_ROLE("MUSIC_ROLE", ""),
MUSIC_CHANNEL("MUSIC_CHANNEL", ""), MUSIC_CHANNEL("MUSIC_CHANNEL", ""),
MUSIC_VOLUME("MUSIC_VOLUME", "30"), MUSIC_VOLUME("MUSIC_VOLUME", "30"),
MUSIC_SEARCHRESULTS_LIMIT("MUSIC_SEARCHRESULTS_LIMIT", "5"),
MUSIC_MAXPLAYLISTSIZE("MUSIC_MAXPLAYLISTSIZE", "20"); MUSIC_MAXPLAYLISTSIZE("MUSIC_MAXPLAYLISTSIZE", "20");
private final String name; private final String name;
@@ -43,8 +43,13 @@ public class TaskService {
JSONObject json = new JSONObject(payload); JSONObject json = new JSONObject(payload);
PersistableTask task = creator.fromPayload(json); PersistableTask task = creator.fromPayload(json);
if (TaskManager.getInstance().exists(task.getId())) {
logger.error("Task with id {} already exists", task.getId());
continue;
}
logger.info("Loaded task: {}", task); logger.info("Loaded task: {}", task);
TaskManager.getInstance().getPersistableTaskMap().put(task.getId(), task); TaskManager.getInstance().getTaskMap().put(task.getId(), task);
} catch (Exception e) { } catch (Exception e) {
logger.error("Error loading task: {}", payload, e); logger.error("Error loading task: {}", payload, e);
} }
@@ -76,6 +81,6 @@ public class TaskService {
TaskRepository taskRepository = new TaskRepository(); TaskRepository taskRepository = new TaskRepository();
taskRepository.deleteBy(task.getId()); taskRepository.deleteBy(task.getId());
taskManager.getPersistableTaskMap().remove(task.getId()); taskManager.getTaskMap().remove(task.getId());
} }
} }
@@ -57,7 +57,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
public void playlistLoaded(AudioPlaylist playlist) { public void playlistLoaded(AudioPlaylist playlist) {
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier); logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
TaskManager.getInstance().insert(context, new LoadPlaylistTask(playlist)); TaskManager.getInstance().insert(context, new LoadPlaylistTask(playlist, type));
} }
@Override @Override
@@ -13,7 +13,7 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import me.bobert.netneutrality.context.MusicChannelContext; import me.bobert.netneutrality.context.MusicChannelContext;
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.Context; import me.bobert.netneutrality.core.context.Context;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.exceptions.ContextException;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -115,13 +115,8 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) { public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
logger.error("Guild {}: exception while playing track: {}", guild.getId(), track.getIdentifier()); logger.error("Guild {}: exception while playing track: {}", guild.getId(), track.getIdentifier());
LogHandler.log(exception);
context.reply( LogHandler.log(new ContextException(String.format("Error while playing track: %s", track.getInfo().title), getContext(), exception));
MessageFunction.buildSentryErrorMessage(
new RuntimeException(String.format("Error while playing track: %s", track.getInfo().title), exception)
)
);
} }
@Override @Override
@@ -132,7 +127,7 @@ public class TrackScheduler extends AudioEventAdapter {
lastStuckTrack = track; lastStuckTrack = track;
logger.debug("Trying to load stuck track again: {}", track.getIdentifier()); logger.debug("Trying to load stuck track again: {}", track.getIdentifier());
context.async("Current track is stuck, trying to load it again", asyncMessage -> { context.async("Current track is stuck, trying to load it again", asyncContext -> {
try { try {
TrackInfo info = queueManager.peekHeadOfQueue(); TrackInfo info = queueManager.peekHeadOfQueue();
if (info != null && info.getTrack().getIdentifier().equals(track.getIdentifier())) { if (info != null && info.getTrack().getIdentifier().equals(track.getIdentifier())) {
@@ -152,15 +147,15 @@ public class TrackScheduler extends AudioEventAdapter {
playing.setPosition(lastPosition); playing.setPosition(lastPosition);
lastStuckTrack = null; lastStuckTrack = null;
} else { } else {
asyncMessage.edit("Failed to seek track to last timestamp, the track will play from the beginning"); asyncContext.edit("Failed to seek track to last timestamp, the track will play from the beginning");
} }
} else { } else {
asyncMessage.edit("Failed to load last playing track, skipping to next.."); asyncContext.edit("Failed to load last playing track, skipping to next..");
queueManager.startNextTrack(); queueManager.startNextTrack();
} }
} catch (Exception e) { } catch (Exception e) {
LogHandler.log(e); LogHandler.log(e);
asyncMessage.edit("Failed to load last playing track, skipping to next.."); asyncContext.edit("Failed to load last playing track, skipping to next..");
queueManager.startNextTrack(); queueManager.startNextTrack();
} }
}); });
@@ -5,8 +5,6 @@ package me.bobert.netneutrality.tasks;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractTask; import me.bobert.netneutrality.core.tasks.AbstractTask;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
@@ -20,17 +18,6 @@ public abstract class ButtonTask extends AbstractTask {
return id.toString(); return id.toString();
} }
@Override
public void execute(TaskContext context) {
if (context instanceof ButtonTaskContext) {
execute((ButtonTaskContext) context);
} else {
throw new IllegalArgumentException("Invalid context for this task");
}
}
public abstract void execute(ButtonTaskContext context);
@Override @Override
public boolean oneshot() { public boolean oneshot() {
return true; return true;
@@ -5,8 +5,6 @@ package me.bobert.netneutrality.tasks;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractPersistableTask; import me.bobert.netneutrality.core.tasks.AbstractPersistableTask;
public abstract class EmojiTask extends AbstractPersistableTask { public abstract class EmojiTask extends AbstractPersistableTask {
@@ -30,15 +28,4 @@ public abstract class EmojiTask extends AbstractPersistableTask {
public String getId() { public String getId() {
return String.format("%s:%s", getGuildId(), getMessageId()); return String.format("%s:%s", getGuildId(), getMessageId());
} }
@Override
public void execute(TaskContext context) {
if (context instanceof EmojiTaskContext) {
execute((EmojiTaskContext) context);
} else {
throw new IllegalArgumentException("Invalid context for this task");
}
}
public abstract void execute(EmojiTaskContext context);
} }
@@ -5,11 +5,12 @@ package me.bobert.netneutrality.tasks;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractTask; import me.bobert.netneutrality.core.tasks.AbstractTask;
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public abstract class ModalTask extends AbstractTask { public abstract class ModalTask extends AbstractTask {
@@ -20,17 +21,6 @@ public abstract class ModalTask extends AbstractTask {
return id.toString(); return id.toString();
} }
@Override
public void execute(TaskContext context) {
if (context instanceof ModalTaskContext) {
execute((ModalTaskContext) context);
} else {
throw new IllegalArgumentException("Invalid context for this task");
}
}
public abstract void execute(ModalTaskContext context);
@Override @Override
public boolean oneshot() { public boolean oneshot() {
return true; return true;
@@ -40,4 +30,13 @@ public abstract class ModalTask extends AbstractTask {
public boolean isExpired() { public boolean isExpired() {
return getCreationTime().plusMinutes(5).isBefore(OffsetDateTime.now()); return getCreationTime().plusMinutes(5).isBefore(OffsetDateTime.now());
} }
public ModalMapping getValue(String key, TaskContext context) {
List<?> mappings = (List<?>) context.getContextData("values");
return mappings.stream()
.filter(o -> o instanceof ModalMapping)
.map(o -> (ModalMapping) o)
.filter(mapping -> mapping.getId().equals(key))
.findFirst().orElse(null);
}
} }
@@ -5,7 +5,7 @@ package me.bobert.netneutrality.tasks.emoji;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.tasks.EmojiTaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.tasks.EmojiTask; import me.bobert.netneutrality.tasks.EmojiTask;
import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.Role;
@@ -43,9 +43,11 @@ public class EmojiReactionRoleTask extends EmojiTask {
} }
@Override @Override
public void execute(EmojiTaskContext context) { public void execute(TaskContext context) {
if (getEmojiMapping().containsKey(context.getEmoji())) { String emoji = (String) context.getContextData("emoji-code");
Role role = context.getGuild().getRoleById(getEmojiMapping().get(context.getEmoji()));
if (getEmojiMapping().containsKey(emoji)) {
Role role = context.getGuild().getRoleById(getEmojiMapping().get(emoji));
if (role != null) { if (role != null) {
context.getGuildManager().addRolesToMember(context.getGuildUser(), role); context.getGuildManager().addRolesToMember(context.getGuildUser(), role);
@@ -6,9 +6,9 @@ package me.bobert.netneutrality.tasks.git;
*/ */
import io.sentry.Sentry; import io.sentry.Sentry;
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.tasks.ModalTask; import me.bobert.netneutrality.tasks.ModalTask;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
@@ -53,7 +53,7 @@ public class ReportIssueTask extends ModalTask {
} }
@Override @Override
public void execute(ModalTaskContext context) { public void execute(TaskContext context) {
context.ephemeral(); context.ephemeral();
String gitlabHost = Settings.getInstance().get("GITLAB_HOST"); String gitlabHost = Settings.getInstance().get("GITLAB_HOST");
@@ -66,8 +66,8 @@ public class ReportIssueTask extends ModalTask {
return; return;
} }
ModalMapping modalTitle = context.getEvent().getValue("title"); ModalMapping modalTitle = getValue("title", context);
ModalMapping modalDescription = context.getEvent().getValue("description"); ModalMapping modalDescription = getValue("description", context);
if (modalTitle == null || modalDescription == null) { if (modalTitle == null || modalDescription == null) {
context.reply("Failed to create issue."); context.reply("Failed to create issue.");
@@ -0,0 +1,23 @@
package me.bobert.netneutrality.tasks.help;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
public class BotInfo extends AbstractBuiltInTask {
@Override
public String getId() {
return "help-info";
}
@Override
public void execute(TaskContext context) {
context.reply("For information about the bot and to follow the development, " +
"you can join the discord here: https://discord.gg/udVJwTgV");
}
}
@@ -0,0 +1,23 @@
package me.bobert.netneutrality.tasks.help;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
import me.bobert.netneutrality.utils.MessageFunction;
public class CommandHelp extends AbstractBuiltInTask {
@Override
public String getId() {
return "help-commands";
}
@Override
public void execute(TaskContext context) {
context.reply(MessageFunction.buildHelpMessage());
}
}
@@ -0,0 +1,23 @@
package me.bobert.netneutrality.tasks.help;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
public class ReportBug extends AbstractBuiltInTask {
@Override
public String getId() {
return "help-reportbug";
}
@Override
public void execute(TaskContext context) {
context.reply("To report a buuug with the bot, you can either use the /report-bug command " +
"or join the discord here: https://discord.gg/udVJwTgV");
}
}
@@ -6,12 +6,11 @@ package me.bobert.netneutrality.tasks.music;
*/ */
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.WithParent; import me.bobert.netneutrality.core.tasks.trait.WithParent;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.SourceFinder; import me.bobert.netneutrality.music.SourceFinder;
import me.bobert.netneutrality.music.TrackScheduler;
import me.bobert.netneutrality.tasks.ButtonTask; import me.bobert.netneutrality.tasks.ButtonTask;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
@@ -39,7 +38,7 @@ public class AddAllTracksTask extends ButtonTask implements WithParent<LoadPlayl
} }
@Override @Override
public void execute(ButtonTaskContext context) { public void execute(TaskContext context) {
MusicManager manager = MusicManager.get(context.getGuild()); MusicManager manager = MusicManager.get(context.getGuild());
Member member = context.getGuildUser(); Member member = context.getGuildUser();
@@ -5,8 +5,8 @@ package me.bobert.netneutrality.tasks.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable; import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
import me.bobert.netneutrality.core.tasks.trait.WithParent; import me.bobert.netneutrality.core.tasks.trait.WithParent;
import me.bobert.netneutrality.tasks.ButtonTask; import me.bobert.netneutrality.tasks.ButtonTask;
@@ -39,7 +39,7 @@ public class CancelLoadTask extends ButtonTask implements WithParent<LoadPlaylis
} }
@Override @Override
public void execute(ButtonTaskContext context) { public void execute(TaskContext context) {
// Interaction will get removed after execution // Interaction will get removed after execution
} }
} }
@@ -7,32 +7,32 @@ package me.bobert.netneutrality.tasks.music;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.Task; import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.trait.Deletable; import me.bobert.netneutrality.core.tasks.trait.Deletable;
import me.bobert.netneutrality.core.tasks.trait.WithChildren; import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.SourceFinder;
import me.bobert.netneutrality.tasks.ButtonTask; import me.bobert.netneutrality.tasks.ButtonTask;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.ItemComponent; import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static me.bobert.netneutrality.music.SourceFinder.AudioSourceType.Youtube;
public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deletable { public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deletable {
private final List<Task> childrens = new ArrayList<>(); private final List<Task> childrens = new ArrayList<>();
private final AudioPlaylist playlist; private final AudioPlaylist playlist;
private final SourceFinder.AudioSourceType sourceType;
public LoadPlaylistTask(AudioPlaylist playlist) { public LoadPlaylistTask(AudioPlaylist playlist, SourceFinder.AudioSourceType sourceType) {
this.playlist = playlist; this.playlist = playlist;
this.sourceType = sourceType;
} }
public AudioPlaylist getPlaylist() { public AudioPlaylist getPlaylist() {
@@ -56,9 +56,7 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
return; return;
} }
int searchResultsLimit = Integer.parseInt(context.getGuildManager() int searchResultsLimit = 5; // Max number of buttons per row
.getGuildConfigManager()
.getSetting(GSettings.MUSIC_SEARCHRESULTS_LIMIT));
int maxPlaylistSizeBeforeAsking = Integer.parseInt(context.getGuildManager() int maxPlaylistSizeBeforeAsking = Integer.parseInt(context.getGuildManager()
.getGuildConfigManager() .getGuildConfigManager()
.getSetting(GSettings.MUSIC_MAXPLAYLISTSIZE)); .getSetting(GSettings.MUSIC_MAXPLAYLISTSIZE));
@@ -72,8 +70,7 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
AudioTrack track = searchResults.get(i); AudioTrack track = searchResults.get(i);
Task buttonTask = new LoadTrackFromPlaylistTask(this, track); Task buttonTask = new LoadTrackFromPlaylistTask(this, track);
Button button = Button.primary(buttonTask.getId(), String.valueOf(i + 1)); buttonList.add(buttonTask.createButton().primary(String.valueOf(i + 1)));
buttonList.add(button);
childrens.add(buttonTask); childrens.add(buttonTask);
} }
@@ -98,25 +95,26 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
if (MusicUtils.connect(context)) { if (MusicUtils.connect(context)) {
manager.getQueueManager().queue(tracks, context.getGuildUser()); manager.getQueueManager().queue(tracks, context.getGuildUser());
context.edit(MessageFunction.buildAddingPlaylistMessage(tracks, playlist.getName(), context.getGuildUser(), Youtube)); context.edit(MessageFunction.buildAddingPlaylistMessage(tracks, playlist.getName(), context.getGuildUser(), sourceType));
} }
} }
@Override @Override
public void execute(ButtonTaskContext context) { public void execute(TaskContext context) {
// Only the buttons will get executed, this task is only used to insert them // Only the buttons will get executed, this task is only used to insert them
} }
private ActionRow commonButtons(List<AudioTrack> tracks, String addAllText, String cancelText) { private ActionRow commonButtons(List<AudioTrack> tracks, String addAllText, String cancelText) {
Task addAllTracksTask = new AddAllTracksTask(this, tracks); Task addAllTracksTask = new AddAllTracksTask(this, tracks);
Button addAllButton = Button.success(addAllTracksTask.getId(), addAllText).withEmoji(Emoji.fromFormatted("U+1F60E"));
childrens.add(addAllTracksTask); childrens.add(addAllTracksTask);
Task cancelTask = new CancelLoadTask(this); Task cancelTask = new CancelLoadTask(this);
Button cancelButton = Button.danger(cancelTask.getId(), cancelText).withEmoji(Emoji.fromFormatted("U+1F92E"));
childrens.add(cancelTask); childrens.add(cancelTask);
return ActionRow.of(addAllButton, cancelButton); return ActionRow.of(
addAllTracksTask.createButton().success(addAllText).withEmoji(Emoji.fromFormatted("U+1F60E")),
cancelTask.createButton().danger(cancelText).withEmoji(Emoji.fromFormatted("U+1F92E"))
);
} }
@Override @Override
@@ -6,8 +6,8 @@ package me.bobert.netneutrality.tasks.music;
*/ */
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.WithParent; import me.bobert.netneutrality.core.tasks.trait.WithParent;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.SourceFinder; import me.bobert.netneutrality.music.SourceFinder;
@@ -36,7 +36,7 @@ public class LoadTrackFromPlaylistTask extends ButtonTask implements WithParent<
} }
@Override @Override
public void execute(ButtonTaskContext context) { public void execute(TaskContext context) {
MusicManager manager = MusicManager.get(context.getGuild()); MusicManager manager = MusicManager.get(context.getGuild());
Member member = context.getGuildUser(); Member member = context.getGuildUser();
@@ -4,21 +4,35 @@ package me.bobert.netneutrality.thread;/*
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.tasks.BuiltInTask;
import me.bobert.netneutrality.core.tasks.PersistableTask;
import me.bobert.netneutrality.core.tasks.Task; import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.List;
public class CleanupTaskThread implements Runnable { public class CleanupTaskThread implements Runnable {
private static final Logger logger = LogManager.getLogger(CleanupTaskThread.class); private static final Logger logger = LogManager.getLogger(CleanupTaskThread.class);
@Override @Override
public void run() { public void run() {
List<Task> tasks = TaskManager.getInstance().getTasks()
.stream()
.filter(task -> !(task instanceof PersistableTask))
.filter(task -> !(task instanceof BuiltInTask))
.filter(Task::isExpired)
.toList();
if (!tasks.isEmpty()) {
logger.info("Cleaning up tasks.."); logger.info("Cleaning up tasks..");
TaskManager.getInstance().getTasks() for (Task task : tasks) {
.stream() TaskManager.getInstance().removeTask(task);
.filter(Task::isExpired) }
.forEach(task -> TaskManager.getInstance().removeTask(task)); }
} }
} }
@@ -200,39 +200,54 @@ public class MessageFunction {
return embed.build(); return embed.build();
} }
public static List<MessageEmbed> buildHelpMessage() { public static String buildHelpMessage() {
Map<CommandType, EmbedBuilder> help = new HashMap<>(); Map<CommandType, String> help = new HashMap<>();
for (Command command : CommandManager.commands.values()) { for (Command command : CommandManager.commands.values()) {
if (command.getType() == null) if (command.getType() == null) {
continue; continue;
}
String description = "No description yet";
if (command.isEnabled() && command.getType() != CommandType.UNLISTED) { if (command.isEnabled() && command.getType() != CommandType.UNLISTED) {
if (command.getDescription() != null) String description = "No description yet";
description = command.getDescription();
EmbedBuilder message; if (command.getDescription() != null) {
description = command.getDescription();
}
String message;
if (help.containsKey(command.getType())) { if (help.containsKey(command.getType())) {
message = help.get(command.getType()); message = help.get(command.getType());
} else { } else {
help.put(command.getType(), message = new EmbedBuilder()); help.put(command.getType(), message = "");
} }
if (message.isEmpty()) { if (message.isEmpty()) {
message.setTitle(command.getType().getDescription() + " commands for NetNeutrality"); message = "## " + command.getType() + " commands";
message.setColor(Color.BLUE);
} }
message.addField(String.format("%s", command.getCommandName()), description, false); message += "\n- /" + command.getCommandName() + " -> " + description;
help.put(command.getType(), message);
} }
} }
List<MessageEmbed> messages = new ArrayList<>(); String subtext = "\n\n" +
help.values().forEach(embedBuilder -> messages.add(embedBuilder.build())); "-# The bot supports both types of commands: slash (/) and message ($), " +
"however not all slash commands might have a corresponding message command." +
"-# To get help for a specific command, please use /help <command> ";
return messages; return String.join("\n", help.values()) + subtext;
}
public static String buildCommandHelpMessage(Command command) {
String message = "## Command info for /" + command.getCommandName();
if (command.getDescription() != null) {
message += "\n- Description: " + command.getDescription();
}
message += "\n\n-# *: required argument";
return message;
} }
public static MessageEmbed buildSentryErrorMessage(Exception ex) { public static MessageEmbed buildSentryErrorMessage(Exception ex) {
@@ -72,6 +72,10 @@ public class Settings {
return property; return property;
} }
public String getPrefix() {
return get("NETNEUTRALITY_BOT_PREFIX", "$");
}
public String getMavenVersion() { public String getMavenVersion() {
String version = null; String version = null;