diff --git a/src/main/java/me/bobert/netneutrality/NetNeutrality.java b/src/main/java/me/bobert/netneutrality/NetNeutrality.java index 3270160..ce0abab 100644 --- a/src/main/java/me/bobert/netneutrality/NetNeutrality.java +++ b/src/main/java/me/bobert/netneutrality/NetNeutrality.java @@ -11,7 +11,6 @@ import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.command.CommandManager; import me.bobert.netneutrality.core.db.SessionManager; import me.bobert.netneutrality.core.tasks.TaskManager; -import me.bobert.netneutrality.core.thread.BaseThreadFactory; import me.bobert.netneutrality.core.thread.ThreadManager; import me.bobert.netneutrality.hooks.guilds.ServerJoinHook; import me.bobert.netneutrality.hooks.interaction.ButtonInteractionHook; @@ -35,10 +34,10 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; public class NetNeutrality { - - private static JDA jda; - private final static Logger logger = LogManager.getLogger(NetNeutrality.class); + private static final Logger logger = LogManager.getLogger(NetNeutrality.class); public static final String VERSION = Settings.getInstance().getMavenVersion(); + private static NetNeutrality instance; + private static JDA jda; public static void main(String[] args) { String env = Settings.getInstance().get("NETNEUTRALITY_BOT_ENV", "dev"); @@ -60,6 +59,14 @@ public class NetNeutrality { Configurator.setLevel("net.dv8tion.jda", Level.DEBUG); } + instance = new NetNeutrality(); + } + + public NetNeutrality() { + startup(); + } + + private void startup() { // DatabaseMigration.migrate(); SessionManager.getSessionFactory(); initJDA(); @@ -72,11 +79,14 @@ public class NetNeutrality { new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000"))); jda.getPresence().setActivity(Activity.watching("you")); - Runtime.getRuntime().addShutdownHook(new BaseThreadFactory("shutdown").newThread(new ShutdownThread())); - + Runtime.getRuntime().addShutdownHook(new ShutdownThread(this)); } - public static void initJDA() { + public static NetNeutrality getInstance() { + return instance; + } + + public void initJDA() { String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN"); if (token == null) { SessionManager.shutdown(); @@ -96,7 +106,7 @@ public class NetNeutrality { } } - public static void shutdown() { + public void shutdown() { System.out.println("[Net Neutrality] Shutting down..."); MusicManager.disconnectPlayers(); ThreadManager.shutdownExecutor(); @@ -105,16 +115,16 @@ public class NetNeutrality { System.out.println("[Net Neutrality] Shutdown complete"); } - public static JDA getJDA() { + public JDA getJDA() { return jda; } - public static User getSelf() { + public User getSelf() { return jda.getSelfUser(); } - private static void disconnect() { + private void disconnect() { if (jda != null) { jda.shutdown(); } diff --git a/src/main/java/me/bobert/netneutrality/command/music/QueueCommand.java b/src/main/java/me/bobert/netneutrality/command/music/QueueCommand.java index 370d44a..b32e96e 100644 --- a/src/main/java/me/bobert/netneutrality/command/music/QueueCommand.java +++ b/src/main/java/me/bobert/netneutrality/command/music/QueueCommand.java @@ -9,7 +9,6 @@ import me.bobert.netneutrality.command.AbstractMusicCommand; import me.bobert.netneutrality.context.SlashCommandContext; import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.music.MusicManager; -import me.bobert.netneutrality.music.QueueManager; import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MusicUtils; import net.dv8tion.jda.api.entities.Member; @@ -62,32 +61,25 @@ public class QueueCommand extends AbstractMusicCommand { @Override public CommandExecutionResult execute(SlashCommandContext context) { MusicManager manager = MusicManager.get(context.getGuild()); - QueueManager queueManager = manager.getQueueManager(); - context.ack(); String sub = context.getSubCommand(); + context.ack(); if (MusicUtils.checkIfConnected(context)) { switch (sub) { case "clear-all" -> { - return queueManager.clearQueue(); + return manager.getQueueManager().clearQueue(); } case "delete-track" -> { int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt); - return queueManager.removeFromQueue(position); + return manager.getQueueManager().removeFromQueue(position); } - case "show-queue" -> { - if (queueManager.getQueueSize() == 0) { - return CommandExecutionResult.info("Queue is empty!"); - } - - context.reply(MessageFunction.buildViewQueueMessage(manager)); - } + case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager)); case "shuffle" -> { - return queueManager.shuffle(); + return manager.getQueueManager().shuffle(); } case "from-user" -> { @@ -105,13 +97,7 @@ public class QueueCommand extends AbstractMusicCommand { return CommandExecutionResult.warn("The user isn't from this guild"); } - int size = queueManager.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester())).size(); - if (size == 0) { - return CommandExecutionResult.info(String.format("No tracks added by user %s", member.getEffectiveName())); - } - - queueManager.removeTracksFromUser(member); - context.reply("Removed all tracks from user %s", member.getEffectiveName()); + return manager.getQueueManager().removeTracksFromUser(member); } } } diff --git a/src/main/java/me/bobert/netneutrality/command/music/ResumeCommand.java b/src/main/java/me/bobert/netneutrality/command/music/ResumeCommand.java index 971f8ce..fcb1255 100644 --- a/src/main/java/me/bobert/netneutrality/command/music/ResumeCommand.java +++ b/src/main/java/me/bobert/netneutrality/command/music/ResumeCommand.java @@ -37,7 +37,7 @@ public class ResumeCommand extends AbstractMusicCommand { MusicManager manager = MusicManager.get(context.getGuild()); if (MusicUtils.checkIfConnected(context)) { - return manager.getQueueManager().skip(); + return manager.resume(); } return CommandExecutionResult.none(); diff --git a/src/main/java/me/bobert/netneutrality/core/AsyncMessage.java b/src/main/java/me/bobert/netneutrality/context/AsyncContext.java similarity index 80% rename from src/main/java/me/bobert/netneutrality/core/AsyncMessage.java rename to src/main/java/me/bobert/netneutrality/context/AsyncContext.java index eddcf81..25dfdea 100644 --- a/src/main/java/me/bobert/netneutrality/core/AsyncMessage.java +++ b/src/main/java/me/bobert/netneutrality/context/AsyncContext.java @@ -1,4 +1,4 @@ -package me.bobert.netneutrality.core; +package me.bobert.netneutrality.context; /* * Copyright (c) anthorob * @@ -14,19 +14,20 @@ import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageEditData; +import java.util.List; import java.util.function.Consumer; -public class AsyncMessage extends AbstractContext { +public class AsyncContext extends AbstractContext { private final InteractionHook hook; private final Message message; - public AsyncMessage(InteractionHook hook) { + public AsyncContext(InteractionHook hook) { super(hook.getJDA(), hook.getInteraction().getGuild(), hook.getInteraction().getMessageChannel(), hook.getInteraction().getMember(), hook.getInteraction().getUser()); this.hook = hook; this.message = null; } - public AsyncMessage(Message message) { + public AsyncContext(Message message) { super(message.getJDA(), message.getGuild(), message.getChannel(), message.getMember(), message.getAuthor()); this.hook = null; this.message = message; @@ -49,7 +50,7 @@ public class AsyncMessage extends AbstractContext { @Override public boolean isFromGuild() { - return false; + return message != null ? message.isFromGuild() : hook.getInteraction().isFromGuild(); } @Override @@ -69,7 +70,7 @@ public class AsyncMessage extends AbstractContext { } @Override - public void reply(MessageCreateData messageCreateData, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + public void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { if (hook != null) { hook.sendMessage(messageCreateData).queue(); } @@ -80,7 +81,7 @@ public class AsyncMessage extends AbstractContext { } @Override - public void defer(Consumer consumer) { + public void defer(boolean ephemeral, Consumer consumer) { } @@ -90,7 +91,7 @@ public class AsyncMessage extends AbstractContext { } @Override - public void edit(MessageEditData messageEditData, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + public void edit(MessageEditData messageEditData, boolean ephemeral, List components, Consumer consumer) { if (hook != null) { hook.editOriginal(messageEditData).queue(); } diff --git a/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java b/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java index a2a7a7f..bf8d75a 100644 --- a/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java +++ b/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java @@ -5,7 +5,6 @@ package me.bobert.netneutrality.context; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.utils.Settings; import net.dv8tion.jda.api.entities.Message; @@ -15,6 +14,7 @@ import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -77,14 +77,14 @@ public class MessageCommandContext extends CommandContext { } @Override - public void defer(Consumer consumer) { + public void defer(boolean ephemeral, Consumer consumer) { reply("Bot is thinking...", consumer); } @Override - public void reply(MessageCreateData messageCreateData, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + public void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { if (getChannel() != null && getChannel().canTalk()) { - message.reply(messageCreateData).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1))); + message.reply(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); } } @@ -94,8 +94,8 @@ public class MessageCommandContext extends CommandContext { } @Override - public void replyEphemeral(MessageCreateData message) { - sendMessageInPrivate(message); + public void replyEphemeral(MessageCreateData messageCreateData) { + sendMessageInPrivate(messageCreateData); } @Override diff --git a/src/main/java/me/bobert/netneutrality/context/MusicChannelContext.java b/src/main/java/me/bobert/netneutrality/context/MusicChannelContext.java index aa8094e..9745d12 100644 --- a/src/main/java/me/bobert/netneutrality/context/MusicChannelContext.java +++ b/src/main/java/me/bobert/netneutrality/context/MusicChannelContext.java @@ -5,7 +5,6 @@ package me.bobert.netneutrality.context; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; import me.bobert.netneutrality.core.context.AbstractContext; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; @@ -15,6 +14,7 @@ import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import java.util.List; import java.util.function.Consumer; public class MusicChannelContext extends AbstractContext { @@ -54,14 +54,14 @@ public class MusicChannelContext extends AbstractContext { } @Override - public void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + public void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { if (getChannel().canTalk()) { - getChannel().sendMessage(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1))); + getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); } } @Override - public void defer(Consumer consumer) { + public void defer(boolean ephemeral, Consumer consumer) { reply("Bot is thinking", consumer); } diff --git a/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java b/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java index 3206379..16e8baa 100644 --- a/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java +++ b/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java @@ -5,17 +5,13 @@ package me.bobert.netneutrality.context; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; import me.bobert.netneutrality.core.context.CommandContext; +import me.bobert.netneutrality.core.context.InteractionContext; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; -import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import java.util.function.Consumer; - -public class SlashCommandContext extends CommandContext { +public class SlashCommandContext extends CommandContext implements InteractionContext { private final SlashCommandInteractionEvent event; public SlashCommandContext(SlashCommandInteractionEvent event) { @@ -28,16 +24,6 @@ public class SlashCommandContext extends CommandContext { return event; } - @Override - public boolean isAcknowledged() { - return event.isAcknowledged(); - } - - @Override - public boolean isFromGuild() { - return event.isFromGuild(); - } - @Override public InteractionHook getInteractionHook() { if (!event.isAcknowledged()) @@ -51,36 +37,11 @@ public class SlashCommandContext extends CommandContext { // not implemented } - @Override - public void defer(Consumer consumer) { - event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook))); - } - - @Override - public void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { - if (isAcknowledged()) { - getInteractionHook().sendMessage(message).setEphemeral(ephemeral).setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - return; - } - - event.reply(message).setEphemeral(ephemeral).setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - } - @Override public void reply(Modal modal) { event.replyModal(modal).queue(); } - @Override - public void ack(boolean ephemeral) { - if (event.isAcknowledged()) - throw new RuntimeException("Interaction is already acknowledged, please use getInteractionHook() instead."); - - event.deferReply(ephemeral).queue(); - } - @Override public String parseCommandName() { return event.getName(); diff --git a/src/main/java/me/bobert/netneutrality/context/tasks/ButtonTaskContext.java b/src/main/java/me/bobert/netneutrality/context/tasks/ButtonTaskContext.java index b5b8ea5..1b194e9 100644 --- a/src/main/java/me/bobert/netneutrality/context/tasks/ButtonTaskContext.java +++ b/src/main/java/me/bobert/netneutrality/context/tasks/ButtonTaskContext.java @@ -5,16 +5,12 @@ package me.bobert.netneutrality.context.tasks; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; +import me.bobert.netneutrality.core.context.InteractionContext; import me.bobert.netneutrality.core.context.TaskContext; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; -import net.dv8tion.jda.api.interactions.components.LayoutComponent; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import java.util.function.Consumer; - -public class ButtonTaskContext extends TaskContext { +public class ButtonTaskContext extends TaskContext implements InteractionContext { private final ButtonInteractionEvent event; public ButtonTaskContext(ButtonInteractionEvent event) { @@ -36,40 +32,4 @@ public class ButtonTaskContext extends TaskContext { public InteractionHook getInteractionHook() { return event.getHook(); } - - @Override - public void defer(Consumer consumer) { - event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook))); - } - - @Override - public boolean isAcknowledged() { - return event.isAcknowledged(); - } - - @Override - public boolean isFromGuild() { - return event.isFromGuild(); - } - - @Override - public void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { - if (isAcknowledged()) { - getInteractionHook().sendMessage(message) - .setEphemeral(ephemeral) - .setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - return; - } - - event.reply(message) - .setEphemeral(ephemeral) - .setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - } - - @Override - public void ack(boolean ephemeral) { - event.deferReply(ephemeral).queue(); - } } diff --git a/src/main/java/me/bobert/netneutrality/context/tasks/EmojiTaskContext.java b/src/main/java/me/bobert/netneutrality/context/tasks/EmojiTaskContext.java index 54d195c..29eeb5c 100644 --- a/src/main/java/me/bobert/netneutrality/context/tasks/EmojiTaskContext.java +++ b/src/main/java/me/bobert/netneutrality/context/tasks/EmojiTaskContext.java @@ -5,7 +5,7 @@ package me.bobert.netneutrality.context.tasks; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; +import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.core.context.TaskContext; import net.dv8tion.jda.api.entities.MessageReaction; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; @@ -14,6 +14,7 @@ import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import java.util.List; import java.util.function.Consumer; public class EmojiTaskContext extends TaskContext { @@ -50,7 +51,7 @@ public class EmojiTaskContext extends TaskContext { } @Override - public void defer(Consumer consumer) { + public void defer(boolean ephemeral, Consumer consumer) { reply("Bot is thinking...", consumer); } @@ -65,15 +66,15 @@ public class EmojiTaskContext extends TaskContext { } @Override - public void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + public void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { if (getChannel() != null && getChannel().canTalk()) { - getChannel().sendMessage(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1))); + getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); } } @Override - public void replyEphemeral(MessageCreateData message) { - sendMessageInPrivate(message); + public void replyEphemeral(MessageCreateData messageCreateData) { + sendMessageInPrivate(messageCreateData); } @Override diff --git a/src/main/java/me/bobert/netneutrality/context/tasks/ModalTaskContext.java b/src/main/java/me/bobert/netneutrality/context/tasks/ModalTaskContext.java index 5d03799..e4621ad 100644 --- a/src/main/java/me/bobert/netneutrality/context/tasks/ModalTaskContext.java +++ b/src/main/java/me/bobert/netneutrality/context/tasks/ModalTaskContext.java @@ -5,16 +5,12 @@ package me.bobert.netneutrality.context.tasks; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; +import me.bobert.netneutrality.core.context.InteractionContext; import me.bobert.netneutrality.core.context.TaskContext; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; -import net.dv8tion.jda.api.interactions.components.LayoutComponent; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import java.util.function.Consumer; - -public class ModalTaskContext extends TaskContext { +public class ModalTaskContext extends TaskContext implements InteractionContext { private final ModalInteractionEvent event; public ModalTaskContext(ModalInteractionEvent event) { @@ -36,36 +32,4 @@ public class ModalTaskContext extends TaskContext { public InteractionHook getInteractionHook() { return event.getHook(); } - - @Override - public void defer(Consumer consumer) { - event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook))); - } - - @Override - public boolean isAcknowledged() { - return event.isAcknowledged(); - } - - @Override - public boolean isFromGuild() { - return event.isFromGuild(); - } - - @Override - public void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { - if (isAcknowledged()) { - getInteractionHook().sendMessage(message).setEphemeral(ephemeral).setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - return; - } - - event.reply(message).setEphemeral(ephemeral).setComponents(components) - .queue(message1 -> consumer.accept(new AsyncMessage(message1))); - } - - @Override - public void ack(boolean ephemeral) { - event.deferReply(ephemeral).queue(); - } } diff --git a/src/main/java/me/bobert/netneutrality/core/GuildManager.java b/src/main/java/me/bobert/netneutrality/core/GuildManager.java index 04f30f7..120289c 100644 --- a/src/main/java/me/bobert/netneutrality/core/GuildManager.java +++ b/src/main/java/me/bobert/netneutrality/core/GuildManager.java @@ -55,7 +55,7 @@ public class GuildManager { return managers.get(guildId); } - Guild temp = NetNeutrality.getJDA().getGuildById(guildId); + Guild temp = NetNeutrality.getInstance().getJDA().getGuildById(guildId); if (temp == null) { throw new IllegalArgumentException("Guild does not exist in current cache"); } diff --git a/src/main/java/me/bobert/netneutrality/core/LogHandler.java b/src/main/java/me/bobert/netneutrality/core/LogHandler.java index 02d86db..305b10f 100644 --- a/src/main/java/me/bobert/netneutrality/core/LogHandler.java +++ b/src/main/java/me/bobert/netneutrality/core/LogHandler.java @@ -60,7 +60,12 @@ public class LogHandler { private static void logInternally(ContextException exception) { Context context = exception.getContext(); - logger.error("Exception thrown from context in guild {}: {}", context.getGuild() == null ? "PrivateMessage": context.getGuild().getId(), exception.getMessage()); + String guildId = context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId(); + + logger.error("Exception thrown from context (guild: {}, user: {}): {}", guildId, + context.getDiscordUser().getId(), + exception.getMessage() + ); if (context instanceof CommandContext commandContext) { logger.error("Caused by command: {}", commandContext.getCommand().getCommandName(), exception); diff --git a/src/main/java/me/bobert/netneutrality/core/context/Context.java b/src/main/java/me/bobert/netneutrality/core/context/Context.java index ae98789..d6b7263 100644 --- a/src/main/java/me/bobert/netneutrality/core/context/Context.java +++ b/src/main/java/me/bobert/netneutrality/core/context/Context.java @@ -5,7 +5,7 @@ package me.bobert.netneutrality.core.context; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.core.AsyncMessage; +import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.core.GuildManager; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; @@ -47,9 +47,9 @@ public interface Context { void reply(Modal modal); - void reply(MessageCreateData message, boolean ephemeral, Consumer consumer, LayoutComponent... components); + void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer); - void defer(Consumer consumer); + void defer(boolean ephemeral, Consumer consumer); void ack(boolean ephemeral); @@ -65,8 +65,8 @@ public interface Context { ack(true); } - default void reply(MessageCreateData message) { - reply(message, false, asyncMessage -> {}); + default void reply(MessageCreateData messageCreateData) { + reply(messageCreateData, false, List.of(), asyncMessage -> {}); } default void reply(String message) { @@ -81,21 +81,21 @@ public interface Context { reply(String.format(format, arguments)); } - default void edit(MessageEditData message, boolean ephemeral, Consumer consumer, LayoutComponent... components) { + default void edit(MessageEditData messageEditData, boolean ephemeral, List components, Consumer consumer) { if (isAcknowledged() && getInteractionHook() != null) { - getInteractionHook().editOriginal(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1))); + getInteractionHook().editOriginal(messageEditData).setComponents(components).queue(message1 -> consumer.accept(new AsyncContext(message1))); return; } - reply(MessageCreateData.fromEditData(message), ephemeral, consumer, components); + reply(MessageCreateData.fromEditData(messageEditData), ephemeral, components, consumer); } - default void edit(MessageEditData message, LayoutComponent... components) { - edit(message, false, asyncMessage -> {}, components); + default void edit(MessageEditData messageEditData, List components) { + edit(messageEditData, false, components, asyncMessage -> {}); } - default void edit(MessageEditData message) { - edit(message, false, asyncMessage -> {}); + default void edit(MessageEditData messageEditData) { + edit(messageEditData, false, List.of(), asyncMessage -> {}); } default void edit(String message) { @@ -106,7 +106,7 @@ public interface Context { edit(MessageEditData.fromEmbeds(embed)); } - default void edit(MessageEmbed embeds, LayoutComponent... components) { + default void edit(MessageEmbed embeds, List components) { edit(MessageEditData.fromEmbeds(embeds), components); } @@ -114,20 +114,20 @@ public interface Context { edit(String.format(format, arguments)); } - default void async(MessageCreateData message, Consumer consumer) { - reply(message, false, consumer); + default void async(MessageCreateData messageCreateData, Consumer consumer) { + reply(messageCreateData, false, List.of(), consumer); } - default void async(String message, Consumer consumer) { + default void async(String message, Consumer consumer) { async(MessageCreateData.fromContent(message), consumer); } - default void async(Consumer consumer) { - defer(consumer); + default void async(Consumer consumer) { + defer(false, consumer); } - default void replyEphemeral(MessageCreateData message) { - reply(message, true, asyncMessage -> {}); + default void replyEphemeral(MessageCreateData messageCreateData) { + reply(messageCreateData, true, List.of(), asyncMessage -> {}); } default void replyEphemeral(String message) { @@ -142,10 +142,10 @@ public interface Context { replyEphemeral(MessageCreateData.fromEmbeds(embeds)); } - default void sendMessageInPrivate(MessageCreateData msg) { + default void sendMessageInPrivate(MessageCreateData messageCreateData) { User user = getDiscordUser(); if (user != null) { - user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue(); + user.openPrivateChannel().flatMap(channel -> channel.sendMessage(messageCreateData)).queue(); } } diff --git a/src/main/java/me/bobert/netneutrality/core/context/InteractionContext.java b/src/main/java/me/bobert/netneutrality/core/context/InteractionContext.java new file mode 100644 index 0000000..5f53bee --- /dev/null +++ b/src/main/java/me/bobert/netneutrality/core/context/InteractionContext.java @@ -0,0 +1,73 @@ +package me.bobert.netneutrality.core.context; +/* + * Copyright (c) anthorob + * + * https://discord.gg/qUa6zNFZsd + */ + +import me.bobert.netneutrality.context.AsyncContext; +import net.dv8tion.jda.api.events.Event; +import net.dv8tion.jda.api.interactions.Interaction; +import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; +import net.dv8tion.jda.api.interactions.components.LayoutComponent; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.List; +import java.util.function.Consumer; + +public interface InteractionContext extends Context { + + @Override + default void ack(boolean ephemeral) { + defer(ephemeral, asyncMessage -> {}); + } + + @Override + default void defer(boolean ephemeral, Consumer consumer) { + if (isAcknowledged()) { + throw new RuntimeException("Interaction is already acknowledged, please use getInteractionHook() instead."); + } + + getReplyCallback(getEvent()).deferReply(ephemeral).queue(hook -> consumer.accept(new AsyncContext(hook))); + } + + @Override + default boolean isFromGuild() { + Event event = getEvent(); + try { + Interaction interaction = (Interaction) event; + return interaction.isFromGuild(); + } catch (ClassCastException e) { + throw new IllegalArgumentException(event.getClass().getSimpleName() + " does not implement class 'Interaction'", e); + } + } + + @Override + default boolean isAcknowledged() { + Event event = getEvent(); + try { + Interaction interaction = (Interaction) event; + return interaction.isAcknowledged(); + } catch (ClassCastException e) { + throw new IllegalArgumentException(event.getClass().getSimpleName() + " does not implement class 'Interaction'", e); + } + } + + @Override + default void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { + if (isAcknowledged()) { + getInteractionHook().sendMessage(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); + return; + } + + getReplyCallback(getEvent()).reply(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); + } + + static IReplyCallback getReplyCallback(Event event) { + try { + return (IReplyCallback) event; + } catch (ClassCastException e) { + throw new IllegalArgumentException(event.getClass().getSimpleName() + " does not implement class 'IReplyCallback'", e); + } + } +} diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/Task.java b/src/main/java/me/bobert/netneutrality/core/tasks/Task.java index 88c2699..4f6e8b8 100644 --- a/src/main/java/me/bobert/netneutrality/core/tasks/Task.java +++ b/src/main/java/me/bobert/netneutrality/core/tasks/Task.java @@ -16,9 +16,9 @@ public interface Task { /** *

This method should generate a unique ID to differentiate it from other tasks.

*

- * For button and modal interaction, this is the ID of the component sent to discord. - * For other types of interaction, for example emoji reaction, - * each reaction should get a unique ID generated by the context. + * This determines what triggers a task to be executed. + * For example, a modal or button would use the component id. + * For an emoji reaction, the guild and message id would be combined. *

* @return a unique ID for this task */ diff --git a/src/main/java/me/bobert/netneutrality/http/server/ActionHandler.java b/src/main/java/me/bobert/netneutrality/http/server/ActionHandler.java index a2b8f65..e73bf19 100644 --- a/src/main/java/me/bobert/netneutrality/http/server/ActionHandler.java +++ b/src/main/java/me/bobert/netneutrality/http/server/ActionHandler.java @@ -26,7 +26,7 @@ public class ActionHandler extends AbstractHandler { if (path.equals("/actions/shutdown")) { LogManager.getLogger(NetNeutrality.class).info("Request to shutdown received"); - NetNeutrality.shutdown(); + NetNeutrality.getInstance().shutdown(); sendStringResponse(exchange, "Shutting down.."); System.exit(0); @@ -35,8 +35,8 @@ public class ActionHandler extends AbstractHandler { if (path.equals("/actions/status")) { TreeMap json = new TreeMap<>(); - json.put("health", String.valueOf(NetNeutrality.getJDA().getStatus())); - json.put("ping", String.valueOf(NetNeutrality.getJDA().getGatewayPing())); + json.put("health", String.valueOf(NetNeutrality.getInstance().getJDA().getStatus())); + json.put("ping", String.valueOf(NetNeutrality.getInstance().getJDA().getGatewayPing())); sendJsonResponse(exchange, WebModule.createJson(json)); return; @@ -47,7 +47,7 @@ public class ActionHandler extends AbstractHandler { String[] args = exchange.getRequestURI().toString().split("/"); String guildId = args[args.length - 1]; - Guild guild = NetNeutrality.getJDA().getGuildById(guildId); + Guild guild = NetNeutrality.getInstance().getJDA().getGuildById(guildId); if (guild != null) { GuildManager manager = GuildManager.getManager(guild); Map settings = manager.getGuildConfigManager().getSettings(); @@ -68,7 +68,7 @@ public class ActionHandler extends AbstractHandler { String[] args = exchange.getRequestURI().toString().split("/"); String guildId = args[args.length - 1]; - Guild guild = NetNeutrality.getJDA().getGuildById(guildId); + Guild guild = NetNeutrality.getInstance().getJDA().getGuildById(guildId); boolean ok = true; //TODO: add bool if (guild == null) GuildManager.getManager(guildId).getGuildConfigManager().reloadSettingsCache(); diff --git a/src/main/java/me/bobert/netneutrality/http/server/GuildHandler.java b/src/main/java/me/bobert/netneutrality/http/server/GuildHandler.java index 26016b8..c082b06 100644 --- a/src/main/java/me/bobert/netneutrality/http/server/GuildHandler.java +++ b/src/main/java/me/bobert/netneutrality/http/server/GuildHandler.java @@ -25,7 +25,7 @@ public class GuildHandler extends AbstractHandler { JSONArray array = new JSONArray(); - JDA jda = NetNeutrality.getJDA(); + JDA jda = NetNeutrality.getInstance().getJDA(); jda.getGuilds().forEach(guild -> { JSONObject obj = new JSONObject(); @@ -45,7 +45,7 @@ public class GuildHandler extends AbstractHandler { String guild = args[args.length - 1]; JSONArray array = new JSONArray(); - JDA jda = NetNeutrality.getJDA(); + JDA jda = NetNeutrality.getInstance().getJDA(); if (jda.getGuildById(guild) == null) { sendStringResponse(exchange, path); @@ -69,7 +69,7 @@ public class GuildHandler extends AbstractHandler { String[] args = exchange.getRequestURI().toString().split("/"); String guild = args[args.length - 1]; JSONArray array = new JSONArray(); - JDA jda = NetNeutrality.getJDA(); + JDA jda = NetNeutrality.getInstance().getJDA(); if (jda.getGuildById(guild) == null) { sendStringResponse(exchange, path); diff --git a/src/main/java/me/bobert/netneutrality/music/MusicManager.java b/src/main/java/me/bobert/netneutrality/music/MusicManager.java index ae7957c..4f0d165 100644 --- a/src/main/java/me/bobert/netneutrality/music/MusicManager.java +++ b/src/main/java/me/bobert/netneutrality/music/MusicManager.java @@ -19,6 +19,7 @@ import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.thread.ThreadManager; import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.music.source.UdioAudioSourceManager; +import me.bobert.netneutrality.thread.MusicManagerCleanupThread; import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread; import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.Settings; @@ -27,24 +28,20 @@ import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class MusicManager { private static final Logger logger = LogManager.getLogger(MusicManager.class); private static final Map managers = new ConcurrentHashMap<>(); - private static final ScheduledExecutorService executor = ThreadManager.getScheduledExecutor(); - private static final AudioPlayerManager playerManager = new DefaultAudioPlayerManager(); private final Guild guild; private final AudioPlayer player; private final TrackScheduler scheduler; private final int baseVolume; - private ScheduledFuture autoDisconnectThread; private MusicManager(Guild guild) { this.guild = guild; @@ -75,6 +72,9 @@ public class MusicManager { String po = Settings.getInstance().get("NETNEUTRALITY_PO_TOKEN"); String visitor = Settings.getInstance().get("NETNEUTRALITY_VISITOR_DATA"); dev.lavalink.youtube.clients.Web.setPoTokenAndVisitorData(po, visitor); + + ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicPlayerAutoDisconnectThread(), 15, 15, TimeUnit.MINUTES); + ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicManagerCleanupThread(), 1, 1, TimeUnit.HOURS); } public static void disconnectPlayers() { @@ -84,6 +84,10 @@ public class MusicManager { } } + public static Collection getManagers() { + return managers.values(); + } + public TrackScheduler getScheduler() { return scheduler; } @@ -105,14 +109,11 @@ public class MusicManager { } public synchronized void connect(AudioChannel audioChannel) { - if (audioChannel != null && isNotConnectedTo(audioChannel)) { + if (audioChannel != null) { logger.debug("Guild {}: connecting to voice channel {}", audioChannel.getGuild().getId(), audioChannel.getId()); guild.getAudioManager().openAudioConnection(audioChannel); guild.getAudioManager().setSelfDeafened(true); player.setVolume(baseVolume); - - if (autoDisconnectThread == null) - autoDisconnectThread = executor.scheduleWithFixedDelay(new MusicPlayerAutoDisconnectThread(this), 240, 240, TimeUnit.SECONDS); } else { throw new RuntimeException("Error connecting to voice channel"); } @@ -126,11 +127,6 @@ public class MusicManager { } scheduler.detachContext(); - - if (autoDisconnectThread != null) { - autoDisconnectThread.cancel(true); - autoDisconnectThread = null; - } } public boolean isConnected() { @@ -152,14 +148,14 @@ public class MusicManager { public synchronized void play(String query, CommandContext context, boolean silent) { logger.debug("Guild {}: request to play '{}' by user {}", guild.getId(), query, context.getGuildUser().getId()); - SourceFinder.AudioSourceType type = SourceFinder.getSource(query); - String identifier = SourceFinder.getIdentifier(query); - if (identifier == null) { - context.reply("No source found to play the given url"); - return; - } - if (MusicUtils.canConnect(context)) { + SourceFinder.AudioSourceType type = SourceFinder.getSource(query); + String identifier = SourceFinder.getIdentifier(query); + if (identifier == null) { + context.reply("No source found to play the given url"); + return; + } + TrackLoadingHandler loadingHandler = new TrackLoadingHandler(context, this, identifier, type, silent); playerManager.loadItemOrdered(this, identifier, loadingHandler); } @@ -169,6 +165,10 @@ public class MusicManager { player.stopTrack(); } + public synchronized CommandExecutionResult resume() { + return getQueueManager().resume(); + } + public synchronized CommandExecutionResult voteSkip(CommandContext context) { AudioChannel audioChannel = guild.getAudioManager().getConnectedChannel(); if (audioChannel == null) { diff --git a/src/main/java/me/bobert/netneutrality/music/QueueManager.java b/src/main/java/me/bobert/netneutrality/music/QueueManager.java index aa20163..acdd29c 100644 --- a/src/main/java/me/bobert/netneutrality/music/QueueManager.java +++ b/src/main/java/me/bobert/netneutrality/music/QueueManager.java @@ -90,23 +90,29 @@ public class QueueManager { } } - public void removeTracksFromUser(Member member) { + public CommandExecutionResult removeTracksFromUser(Member member) { + if (queue.stream().filter(trackInfo -> member.equals(trackInfo.getRequester())).toList().isEmpty()) { + return CommandExecutionResult.info(String.format("No tracks added by user %s", member.getEffectiveName())); + } + 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); - } + if (size == 0) { + return CommandExecutionResult.info(String.format("No tracks added by user %s", member.getEffectiveName())); } + + if (size == queue.size()) { + return clearQueue(); + } + + TrackInfo first = queue.peek(); + queue.removeIf(trackInfo -> member.equals(trackInfo.getRequester())); + + if (first != null && member.equals(first.getRequester())) { + skip(1); + } + + return CommandExecutionResult.success(String.format("Removed all tracks from user %s", member.getEffectiveName())); } } @@ -119,6 +125,10 @@ public class QueueManager { } } + public void queue(List tracks, Member member) { + tracks.forEach(track -> queue(track, member)); + } + public void queue(AudioTrack track, Member member) { TrackInfo info = new TrackInfo(track, member); if (queue.contains(info)) { @@ -145,6 +155,20 @@ public class QueueManager { } } + public CommandExecutionResult resume() { + synchronized (queue) { + TrackInfo next = queue.peek(); + + if (next != null) { + player.startTrack(next.getTrack().makeClone(), false); + return CommandExecutionResult.success("Starting playback of track " + next.getTrack().getInfo().title); + } + + player.stopTrack(); + return CommandExecutionResult.info("Queue is empty, leaving voice channel"); + } + } + public void startNextTrack() { synchronized (queue) { TrackInfo last = queue.poll(); diff --git a/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java b/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java index 390f44c..4fb3a89 100644 --- a/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java +++ b/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java @@ -67,6 +67,10 @@ public class TrackScheduler extends AudioEventAdapter { this.context = null; } + public Context getContext() { + return context; + } + @Override public void onPlayerPause(AudioPlayer player) { // Player was paused @@ -96,6 +100,7 @@ public class TrackScheduler extends AudioEventAdapter { } if (endReason == AudioTrackEndReason.STOPPED) { + // maybe skip removing head to make resuming easier? queueManager.removeHeadOfQueue(); } @@ -109,7 +114,7 @@ public class TrackScheduler extends AudioEventAdapter { @Override public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) { - logger.debug("Guild {}: exception while playing track [{}]: {}", guild.getId(), track.getIdentifier(), exception.getMessage()); + logger.error("Guild {}: exception while playing track: {}", guild.getId(), track.getIdentifier()); LogHandler.log(exception); context.reply( diff --git a/src/main/java/me/bobert/netneutrality/tasks/music/AddAllTracksTask.java b/src/main/java/me/bobert/netneutrality/tasks/music/AddAllTracksTask.java index 6793f98..b225e3f 100644 --- a/src/main/java/me/bobert/netneutrality/tasks/music/AddAllTracksTask.java +++ b/src/main/java/me/bobert/netneutrality/tasks/music/AddAllTracksTask.java @@ -44,9 +44,7 @@ public class AddAllTracksTask extends ButtonTask implements WithParent maxPlaylistSizeBeforeAsking) { ActionRow firstRow = commonButtons(playlist.getTracks(), "I'm ready", "Please don't"); - context.edit(MessageFunction.buildPlaylistLoadMessage(playlist, context.getGuildUser()), firstRow); + context.edit(MessageFunction.buildPlaylistLoadMessage(playlist, context.getGuildUser()), List.of(firstRow)); return; } // Load playlist normally MusicManager manager = MusicManager.get(context.getGuild()); if (MusicUtils.connect(context)) { - for (AudioTrack track : tracks) { - manager.getQueueManager().queue(track, context.getGuildUser()); - } + manager.getQueueManager().queue(tracks, context.getGuildUser()); context.edit(MessageFunction.buildAddingPlaylistMessage(tracks, playlist.getName(), context.getGuildUser(), Youtube)); } diff --git a/src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java b/src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java new file mode 100644 index 0000000..d52d064 --- /dev/null +++ b/src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java @@ -0,0 +1,39 @@ +package me.bobert.netneutrality.thread; +/* + * Copyright (c) anthorob + * + * https://discord.gg/qUa6zNFZsd + */ + +import me.bobert.netneutrality.music.MusicManager; +import me.bobert.netneutrality.music.TrackScheduler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class MusicManagerCleanupThread implements Runnable { + private static final Logger logger = LogManager.getLogger(MusicManagerCleanupThread.class); + + @Override + public void run() { + + try { + + for (MusicManager manager : MusicManager.getManagers()) { + processManager(manager); + } + + } catch (InterruptedException e) { + logger.error("Interrupted while running MusicManagerCleanupThread", e); + } + } + + private void processManager(MusicManager manager) throws InterruptedException { + TrackScheduler scheduler = manager.getScheduler(); + + // Context is still attached after bot is disconnected, the bot might have been kicked from the channel + if (scheduler.getContext() != null && !manager.isConnected()) { + manager.disconnect(); + manager.getQueueManager().clearQueue(); + } + } +} diff --git a/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java b/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java index 41ce844..596cc7b 100644 --- a/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java +++ b/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java @@ -5,9 +5,7 @@ package me.bobert.netneutrality.thread; * https://discord.gg/qUa6zNFZsd */ -import me.bobert.netneutrality.NetNeutrality; import me.bobert.netneutrality.music.MusicManager; -import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; import org.apache.logging.log4j.LogManager; @@ -16,40 +14,47 @@ import org.apache.logging.log4j.Logger; import java.util.List; public class MusicPlayerAutoDisconnectThread implements Runnable { - private static final Logger logger = LogManager.getLogger(MusicPlayerAutoDisconnectThread.class); - private final MusicManager manager; - - //TODO: move to scheduler - public MusicPlayerAutoDisconnectThread(MusicManager musicManager) { - manager = musicManager; - } @Override public void run() { - Guild guild = manager.getGuild(); - AudioChannel channel = guild.getAudioManager().getConnectedChannel(); - try { - if (channel != null) { - if (conditionsMetToDisconnect(channel)) { - // Sleep 30 seconds then check again (prevents disconnecting right after a track ends) - Thread.sleep(30000); - if (conditionsMetToDisconnect(channel)) { - logger.debug("Guild {}: auto-disconnect player from voice channel: {}", guild.getId(), channel.getId()); - manager.disconnect(); - } - } + for (MusicManager manager : MusicManager.getManagers()) { + processManager(manager); } + } catch (InterruptedException e) { - throw new RuntimeException(e); + logger.error("Interrupted while running MusicPlayerAutoDisconnectThread", e); } } - private boolean conditionsMetToDisconnect(AudioChannel channel) { - List members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList(); + private void processManager(MusicManager manager) throws InterruptedException { + logger.debug("Processing MusicPlayerAutoDisconnectThread for guild {}", manager.getGuild().getId()); + AudioChannel channel = manager.getGuild().getAudioManager().getConnectedChannel(); - return members.isEmpty() || manager.getQueueManager().isEmpty(); + if (channel != null) { + if (conditionsMetToDisconnect(manager)) { + logger.debug("Guild {}: Sleeping 30 seconds and checking again", manager.getGuild().getId()); + // Sleep 30 seconds then check again (prevents disconnecting right after a track ends) + Thread.sleep(30000); + + if (conditionsMetToDisconnect(manager)) { + logger.debug("Guild {}: auto-disconnect player from voice channel: {}", manager.getGuild().getId(), channel.getId()); + manager.disconnect(); + } + } + } + } + + private boolean conditionsMetToDisconnect(MusicManager manager) { + AudioChannel channel = manager.getGuild().getAudioManager().getConnectedChannel(); + if (channel != null) { + List members = channel.getMembers().stream().filter(member -> !member.getId().equals(channel.getJDA().getSelfUser().getId())).toList(); + + return members.isEmpty() || manager.getQueueManager().isEmpty(); + } + + return false; } } diff --git a/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java b/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java index c9900e0..cb19a71 100644 --- a/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java +++ b/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java @@ -11,7 +11,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configurator; -public class ShutdownThread implements Runnable { +public class ShutdownThread extends Thread { + private final NetNeutrality main; + + public ShutdownThread(NetNeutrality main) { + this.main = main; + } @Override public void run() { @@ -20,8 +25,8 @@ public class ShutdownThread implements Runnable { Configurator.shutdown((LoggerContext) LogManager.getContext()); } - if (NetNeutrality.getJDA() != null) { - NetNeutrality.shutdown(); + if (main.getJDA() != null) { + main.shutdown(); } } diff --git a/src/main/java/me/bobert/netneutrality/utils/MessageFunction.java b/src/main/java/me/bobert/netneutrality/utils/MessageFunction.java index 1c08a2f..798c456 100644 --- a/src/main/java/me/bobert/netneutrality/utils/MessageFunction.java +++ b/src/main/java/me/bobert/netneutrality/utils/MessageFunction.java @@ -31,10 +31,8 @@ import org.apache.logging.log4j.Logger; import java.awt.*; import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.*; public class MessageFunction { @@ -44,12 +42,12 @@ public class MessageFunction { AudioTrack current = manager.getCurrentlyPlaying(); EmbedBuilder message = new EmbedBuilder(); - message.setAuthor("Link to track", String.format("https://youtu.be/%s", current.getIdentifier())); + message.setAuthor("Link to track (click me)", String.format("https://youtu.be/%s", current.getIdentifier())); message.setThumbnail(String.format("https://img.youtube.com/vi/%s/default.jpg", current.getIdentifier())); - message.setTitle("Currently playing:"); + //message.setTitle("Currently playing:"); 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.addField("Currently playing: ", current.getInfo().title, true); + message.addField("Duration: ", DurationFormatUtils.formatDuration(current.getPosition(), "HH:mm:ss", true) + " / " + DurationFormatUtils.formatDuration(current.getDuration(), "HH:mm:ss", true), true); message.setTimestamp(OffsetDateTime.now()); return message.build(); @@ -110,8 +108,12 @@ public class MessageFunction { } public static String buildViewQueueMessage(MusicManager manager) { - int limit = 15; List tracks = manager.getQueueManager().getAllQueuedTracks(); + if (tracks.isEmpty()) { + return "Queue is empty!"; + } + + int limit = 10; AudioTrack current = manager.getCurrentlyPlaying(); int numberofTracksShown = Math.min(tracks.size(), limit); @@ -125,13 +127,14 @@ public class MessageFunction { int index = 0; for (AudioTrack track : tracks) { + index++; AudioTrackInfo info = track.getInfo(); message.append("\n"); if (current != null && current.getInfo().title.equals(info.title)) { - message.append("**").append(++index).append(". ").append(info.title).append("**"); + message.append("- ").append("**").append(info.title).append("**"); } else { - message.append(++index).append(". ").append(info.title); + message.append("- ").append(info.title); } if (index == limit) break; @@ -197,8 +200,8 @@ public class MessageFunction { return embed.build(); } - public static ArrayList buildHelpMessage() { - HashMap help = new HashMap<>(); + public static List buildHelpMessage() { + Map help = new HashMap<>(); for (Command command : CommandManager.commands.values()) { if (command.getType() == null) @@ -226,7 +229,7 @@ public class MessageFunction { } } - ArrayList messages = new ArrayList<>(); + List messages = new ArrayList<>(); help.values().forEach(embedBuilder -> messages.add(embedBuilder.build())); return messages; @@ -262,10 +265,11 @@ public class MessageFunction { if (exception.getCause() != null) { String cause; if (exception.getCause().getMessage() != null && !exception.getCause().getMessage().isEmpty()) { - cause = exception.getCause().getMessage(); + cause = exception.getCause().getMessage().substring(0, 64); } else { cause = exception.getCause().getClass().getName(); } + message.addField("Caused by:", cause, false); } diff --git a/src/main/java/me/bobert/netneutrality/utils/MusicUtils.java b/src/main/java/me/bobert/netneutrality/utils/MusicUtils.java index 8a9e3c4..453063d 100644 --- a/src/main/java/me/bobert/netneutrality/utils/MusicUtils.java +++ b/src/main/java/me/bobert/netneutrality/utils/MusicUtils.java @@ -17,10 +17,10 @@ import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; public class MusicUtils { public static boolean connect(Context context) { - return connect(context, false, false); + return connect(context, false); } - public static boolean connect(Context context, boolean skipChecks, boolean checkOnly) { + public static boolean connect(Context context, boolean checkOnly) { MusicManager manager = MusicManager.get(context.getGuild()); Member member = context.getGuildUser(); if (member == null) { @@ -34,7 +34,7 @@ public class MusicUtils { return false; } - if (!skipChecks && !isUserDj(member) && (manager.isConnected() && manager.isNotConnectedTo(audioChannel))) { + if (!isUserDj(member) && (manager.isConnected() && manager.isNotConnectedTo(audioChannel))) { context.reply("The bot isn't connected to your voice channel"); return false; } @@ -48,7 +48,7 @@ public class MusicUtils { } public static boolean canConnect(Context context) { - return connect(context, false, true); + return connect(context, true); } public static boolean checkIfConnected(Context context) {