Refactor music commands to return QueueActionResult

Replaced `CommandExecutionResult` with `QueueActionResult` in music-related commands, improving result clarity and alignment with queue-specific operations. Updated command logic, tests, and REST API endpoints to use the new result type, ensuring consistent handling and messaging.
This commit is contained in:
anthorob committed 2026-10-03 13:54:04 -04:00
1 parent 9cd1317659
commit 470f5a33aa
14 files changed
+190 -94

No files matched your search

@@ -63,7 +63,7 @@ public class ClearQueueCommand extends MusicCommand {
context.ack(); context.ack();
if (MusicUtils.checkIfConnected(context, manager)) { if (MusicUtils.checkIfConnected(context, manager)) {
return manager.getQueueManager().clearQueue(); return CommandExecutionResult.from(manager.getQueueManager().clearQueue());
} }
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -83,18 +83,18 @@ public class QueueCommand extends MusicCommand {
switch (sub) { switch (sub) {
case "clear-all" -> { case "clear-all" -> {
return manager.getQueueManager().clearQueue(); return CommandExecutionResult.from(manager.getQueueManager().clearQueue());
} }
case "delete-track" -> { case "delete-track" -> {
int position = context.getOption("position", 0, CommandOption::getAsInt); int position = context.getOption("position", 0, CommandOption::getAsInt);
return manager.getQueueManager().removeFromQueue(position); return CommandExecutionResult.from(manager.getQueueManager().removeFromQueue(position));
} }
case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager)); case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager));
case "shuffle" -> { case "shuffle" -> {
return manager.getQueueManager().shuffle(); return CommandExecutionResult.from(manager.getQueueManager().shuffle());
} }
case "from-user" -> { case "from-user" -> {
@@ -112,7 +112,7 @@ public class QueueCommand extends MusicCommand {
return CommandExecutionResult.warn("The user isn't from this guild"); return CommandExecutionResult.warn("The user isn't from this guild");
} }
return manager.getQueueManager().removeTracksFromUser(member); return CommandExecutionResult.from(manager.getQueueManager().removeTracksFromUser(member));
} }
} }
} }
@@ -60,7 +60,7 @@ public class ResumeCommand extends MusicCommand {
MusicManager manager = musicService.get(context.getGuild()); MusicManager manager = musicService.get(context.getGuild());
if (MusicUtils.checkIfConnected(context, manager)) { if (MusicUtils.checkIfConnected(context, manager)) {
return manager.resume(); return CommandExecutionResult.from(manager.resume());
} }
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -57,7 +57,7 @@ public class ShuffleCommand extends MusicCommand {
context.ack(); context.ack();
if (MusicUtils.checkIfConnected(context, manager)) { if (MusicUtils.checkIfConnected(context, manager)) {
return manager.getQueueManager().shuffle(); return CommandExecutionResult.from(manager.getQueueManager().shuffle());
} }
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -65,9 +65,9 @@ public class SkipCommand extends MusicCommand {
if (MusicUtils.checkIfConnected(context, manager)) { if (MusicUtils.checkIfConnected(context, manager)) {
int skips = context.getOption("skip", 1, CommandOption::getAsInt); int skips = context.getOption("skip", 1, CommandOption::getAsInt);
if (UserFunction.isAdmin(context) || MusicUtils.isUserDj(context, manager)) { if (UserFunction.isAdmin(context) || MusicUtils.isUserDj(context, manager)) {
return manager.getQueueManager().skip(skips); return CommandExecutionResult.from(manager.getQueueManager().skip(skips));
} else { } else {
return manager.voteSkip(context); return CommandExecutionResult.from(manager.voteSkip(context.getGuildUser()));
} }
} }
@@ -56,7 +56,7 @@ public class VoteSkipCommand extends MusicCommand {
MusicManager manager = musicService.get(context.getGuild()); MusicManager manager = musicService.get(context.getGuild());
if (MusicUtils.checkIfConnected(context, manager)) { if (MusicUtils.checkIfConnected(context, manager)) {
return manager.voteSkip(context); return CommandExecutionResult.from(manager.voteSkip(context.getGuildUser()));
} }
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.core.command;
*/ */
import me.bobert.netneutrality.core.tasks.Task; import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.music.QueueActionResult;
public class CommandExecutionResult { public class CommandExecutionResult {
@@ -80,4 +81,18 @@ public class CommandExecutionResult {
public static CommandExecutionResult task(Task task) { public static CommandExecutionResult task(Task task) {
return new CommandExecutionResult(Result.TASK, null, task, null); return new CommandExecutionResult(Result.TASK, null, task, null);
} }
/**
* Adapts a {@link QueueActionResult} returned by {@link me.bobert.netneutrality.music.QueueManager}
* into a {@link CommandExecutionResult} so it can be surfaced as a Discord reply or HTTP response.
*/
public static CommandExecutionResult from(QueueActionResult result) {
return switch (result.getStatus()) {
case SUCCESS -> success(result.getMessage());
case INFO -> info(result.getMessage());
case WARN -> warn(result.getMessage());
case ERROR -> error(result.getMessage());
case NOACTION -> none();
};
}
} }
@@ -17,7 +17,6 @@ import io.javalin.openapi.OpenApiContent;
import io.javalin.openapi.OpenApiParam; import io.javalin.openapi.OpenApiParam;
import io.javalin.openapi.OpenApiRequestBody; import io.javalin.openapi.OpenApiRequestBody;
import io.javalin.openapi.OpenApiResponse; import io.javalin.openapi.OpenApiResponse;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.http.server.dto.CommandResultResponse; import me.bobert.netneutrality.http.server.dto.CommandResultResponse;
import me.bobert.netneutrality.http.server.dto.ErrorResponse; import me.bobert.netneutrality.http.server.dto.ErrorResponse;
import me.bobert.netneutrality.http.server.dto.MusicSourceResponse; import me.bobert.netneutrality.http.server.dto.MusicSourceResponse;
@@ -32,6 +31,7 @@ import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.MusicService; import me.bobert.netneutrality.music.MusicService;
import me.bobert.netneutrality.music.PendingSearchResults; import me.bobert.netneutrality.music.PendingSearchResults;
import me.bobert.netneutrality.music.PlayOutcome; import me.bobert.netneutrality.music.PlayOutcome;
import me.bobert.netneutrality.music.QueueActionResult;
import me.bobert.netneutrality.music.SourceFinder; import me.bobert.netneutrality.music.SourceFinder;
import me.bobert.netneutrality.music.TrackInfo; import me.bobert.netneutrality.music.TrackInfo;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
@@ -150,7 +150,9 @@ public final class MusicRoutes {
) )
private static void skip(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) { private static void skip(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) {
MusicManager manager = manager(jdaSupplier, musicService, ctx); MusicManager manager = manager(jdaSupplier, musicService, ctx);
ctx.json(resultBody(manager.getQueueManager().skip())); QueueActionResult result = manager.getQueueManager().skip();
announce(manager, result.getMessage());
ctx.json(resultBody(result));
} }
@OpenApi( @OpenApi(
@@ -167,6 +169,7 @@ public final class MusicRoutes {
private static void stop(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) { private static void stop(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) {
MusicManager manager = manager(jdaSupplier, musicService, ctx); MusicManager manager = manager(jdaSupplier, musicService, ctx);
manager.stopTrack(); manager.stopTrack();
announce(manager, "Stopping current track");
ctx.json(Map.of("status", "ok", "message", "Stopping current track")); ctx.json(Map.of("status", "ok", "message", "Stopping current track"));
} }
@@ -184,6 +187,7 @@ public final class MusicRoutes {
private static void pause(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) { private static void pause(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) {
MusicManager manager = manager(jdaSupplier, musicService, ctx); MusicManager manager = manager(jdaSupplier, musicService, ctx);
manager.pauseTrack(); manager.pauseTrack();
announce(manager, "Pausing current track");
ctx.json(Map.of("status", "ok", "message", "Pausing current track")); ctx.json(Map.of("status", "ok", "message", "Pausing current track"));
} }
@@ -201,6 +205,7 @@ public final class MusicRoutes {
private static void resumePlayback(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) { private static void resumePlayback(Supplier<JDA> jdaSupplier, MusicService musicService, Context ctx) {
MusicManager manager = manager(jdaSupplier, musicService, ctx); MusicManager manager = manager(jdaSupplier, musicService, ctx);
manager.unpauseTrack(); manager.unpauseTrack();
announce(manager, "Resuming current track");
ctx.json(Map.of("status", "ok", "message", "Resuming current track")); ctx.json(Map.of("status", "ok", "message", "Resuming current track"));
} }
@@ -222,6 +227,7 @@ public final class MusicRoutes {
int newVolume = volume(ctx); int newVolume = volume(ctx);
manager.setVolume(newVolume); manager.setVolume(newVolume);
announce(manager, "Setting volume to " + newVolume);
ctx.json(Map.of("status", "ok", "volume", newVolume)); ctx.json(Map.of("status", "ok", "volume", newVolume));
} }
@@ -268,7 +274,11 @@ public final class MusicRoutes {
ctx, ctx,
() -> guild.getMemberById(userId), () -> guild.getMemberById(userId),
() -> guild.retrieveMemberById(userId), () -> guild.retrieveMemberById(userId),
member -> resultBody(manager.voteSkip(member)) member -> {
QueueActionResult result = manager.voteSkip(member);
announce(manager, result.getMessage());
return resultBody(result);
}
); );
} }
@@ -367,6 +377,8 @@ public final class MusicRoutes {
ctx.future(() -> resolveMember(guild, userId) ctx.future(() -> resolveMember(guild, userId)
.thenApply(member -> { .thenApply(member -> {
candidates.forEach(candidate -> manager.getQueueManager().queue(candidate, member)); candidates.forEach(candidate -> manager.getQueueManager().queue(candidate, member));
announce(manager, String.format("Queued %d track%s requested by %s",
candidates.size(), candidates.size() > 1 ? "s" : "", member.getEffectiveName()));
ctx.json(queuedAllBody(candidates, manager.getQueueSize())); ctx.json(queuedAllBody(candidates, manager.getQueueSize()));
return null; return null;
})); }));
@@ -378,6 +390,8 @@ public final class MusicRoutes {
ctx.future(() -> resolveMember(guild, userId) ctx.future(() -> resolveMember(guild, userId)
.thenApply(member -> { .thenApply(member -> {
manager.getQueueManager().queue(track, member); manager.getQueueManager().queue(track, member);
announce(manager, String.format("Queued %s requested by %s",
track.getInfo().getTitle(), member.getEffectiveName()));
ctx.json(queuedBody(track, manager.getQueueSize())); ctx.json(queuedBody(track, manager.getQueueSize()));
return null; return null;
})); }));
@@ -475,11 +489,24 @@ public final class MusicRoutes {
} }
/** /**
* Builds a JSON-friendly summary for a {@link CommandExecutionResult}. * Announces the result of an API-driven music action in the guild's music channel, mirroring
* the feedback slash commands post. No-op when no {@link me.bobert.netneutrality.core.context.Context}
* is currently attached to the guild's {@link me.bobert.netneutrality.music.TrackScheduler}
* (e.g. the bot was never joined to a voice channel through a Discord command).
*/ */
private static Map<String, Object> resultBody(CommandExecutionResult result) { private static void announce(MusicManager manager, String message) {
me.bobert.netneutrality.core.context.Context context = manager.getScheduler().getContext();
if (context != null && message != null) {
context.reply(message);
}
}
/**
* Builds a JSON-friendly summary for a {@link QueueActionResult}.
*/
private static Map<String, Object> resultBody(QueueActionResult result) {
Map<String, Object> body = new LinkedHashMap<>(); Map<String, Object> body = new LinkedHashMap<>();
body.put("status", result.getResult().name()); body.put("status", result.getStatus().name());
body.put("message", result.getMessage()); body.put("message", result.getMessage());
return body; return body;
} }
@@ -12,7 +12,6 @@ import dev.arbjerg.lavalink.client.player.LavalinkPlayer;
import dev.arbjerg.lavalink.client.player.PlayerUpdateBuilder; import dev.arbjerg.lavalink.client.player.PlayerUpdateBuilder;
import dev.arbjerg.lavalink.client.player.Track; import dev.arbjerg.lavalink.client.player.Track;
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.db.GSettings;
@@ -103,7 +102,10 @@ public class MusicManager implements MusicManagerCallback {
try { try {
return client.getOrCreateLink(guild.getIdLong()); return client.getOrCreateLink(guild.getIdLong());
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.error("Guild {}: no Lavalink node available", guild.getId(), e); // No Lavalink node is currently available. This is an expected, recoverable
// condition (e.g. the node is restarting), so it's logged without a stacktrace to
// avoid flooding the logs; callers already handle a null link gracefully.
logger.warn("Guild {}: no Lavalink node available: {}", guild.getId(), e.getMessage());
return null; return null;
} }
} }
@@ -311,7 +313,7 @@ public class MusicManager implements MusicManagerCallback {
updatePlayer("stop track", PlayerUpdateBuilder::stopTrack); updatePlayer("stop track", PlayerUpdateBuilder::stopTrack);
} }
public synchronized CommandExecutionResult resume() { public synchronized QueueActionResult resume() {
return getQueueManager().resume(); return getQueueManager().resume();
} }
@@ -341,15 +343,14 @@ public class MusicManager implements MusicManagerCallback {
return player != null && player.getPaused(); return player != null && player.getPaused();
} }
public synchronized CommandExecutionResult voteSkip(CommandContext context) {
return voteSkip(context.getGuildUser());
}
/** /**
* Same as {@link #voteSkip(CommandContext)} but driven directly by a {@link Member}, so it * Casts a vote to skip the currently playing track on behalf of {@code member}.
* can be invoked from non-command callers such as the REST API. *
* <p>Driven directly by a {@link Member} so it can be invoked both from the command layer
* (via {@link CommandContext#getGuildUser()}) and from non-command callers such as the REST
* API.
*/ */
public synchronized CommandExecutionResult voteSkip(Member member) { public synchronized QueueActionResult voteSkip(Member member) {
int threshold = parseVoteSkipThreshold(guildService); int threshold = parseVoteSkipThreshold(guildService);
return voteSkipService.voteSkip(member, guild.getId(), getQueueManager(), getConnectedChannel(), isCurrentlyPlaying(), threshold); return voteSkipService.voteSkip(member, guild.getId(), getQueueManager(), getConnectedChannel(), isCurrentlyPlaying(), threshold);
} }
@@ -0,0 +1,57 @@
package me.bobert.netneutrality.music;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
/**
* Outcome of a queue-mutating operation on {@link QueueManager}, independent of how it is later
* presented to the user (e.g. as a Discord reply or an HTTP response).
*/
public class QueueActionResult {
public enum Status {
SUCCESS, INFO, WARN, ERROR, NOACTION
}
private final Status status;
private final String message;
private QueueActionResult(Status status, String message) {
this.status = status;
this.message = message;
}
public Status getStatus() {
return status;
}
public String getMessage() {
return message;
}
public static QueueActionResult success(String message) {
return new QueueActionResult(Status.SUCCESS, message);
}
public static QueueActionResult success(String format, Object... args) {
return success(String.format(format, args));
}
public static QueueActionResult info(String message) {
return new QueueActionResult(Status.INFO, message);
}
public static QueueActionResult warn(String message) {
return new QueueActionResult(Status.WARN, message);
}
public static QueueActionResult error(String message) {
return new QueueActionResult(Status.ERROR, message);
}
public static QueueActionResult none() {
return new QueueActionResult(Status.NOACTION, null);
}
}
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.music;
*/ */
import dev.arbjerg.lavalink.client.player.Track; import dev.arbjerg.lavalink.client.player.Track;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
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;
@@ -72,24 +71,24 @@ public class QueueManager {
} }
} }
public CommandExecutionResult clearQueue() { public QueueActionResult clearQueue() {
synchronized (queue) { synchronized (queue) {
int queueSize = queue.size(); int queueSize = queue.size();
if (queueSize == 0) { if (queueSize == 0) {
return CommandExecutionResult.warn("Queue is empty!"); return QueueActionResult.warn("Queue is empty!");
} }
queue.clear(); queue.clear();
scheduler.getCallback().onQueueEmpty(); scheduler.getCallback().onQueueEmpty();
return CommandExecutionResult.success(String.format("Removed %d track%s from the queue", queueSize, (queueSize > 1 ? "s" : ""))); return QueueActionResult.success(String.format("Removed %d track%s from the queue", queueSize, (queueSize > 1 ? "s" : "")));
} }
} }
public CommandExecutionResult removeTracksFromUser(Member member) { public QueueActionResult removeTracksFromUser(Member member) {
synchronized (queue) { synchronized (queue) {
int size = (int) queue.stream().filter(trackInfo -> member.equals(trackInfo.getRequester())).count(); int size = (int) queue.stream().filter(trackInfo -> member.equals(trackInfo.getRequester())).count();
if (size == 0) { if (size == 0) {
return CommandExecutionResult.info(String.format("No tracks added by user %s", member.getEffectiveName())); return QueueActionResult.info(String.format("No tracks added by user %s", member.getEffectiveName()));
} }
if (size == queue.size()) { if (size == queue.size()) {
@@ -103,7 +102,7 @@ public class QueueManager {
skip(1); skip(1);
} }
return CommandExecutionResult.success(String.format("Removed all tracks from user %s", member.getEffectiveName())); return QueueActionResult.success(String.format("Removed all tracks from user %s", member.getEffectiveName()));
} }
} }
@@ -166,17 +165,17 @@ public class QueueManager {
scheduler.getMusicManager().stopTrack(); scheduler.getMusicManager().stopTrack();
} }
public CommandExecutionResult resume() { public QueueActionResult resume() {
synchronized (queue) { synchronized (queue) {
TrackInfo next = queue.peek(); TrackInfo next = queue.peek();
if (next != null) { if (next != null) {
startTrack(next.getTrack().makeClone(), false); startTrack(next.getTrack().makeClone(), false);
return CommandExecutionResult.success("Starting playback of track " + next.getTrack().getInfo().getTitle()); return QueueActionResult.success("Starting playback of track " + next.getTrack().getInfo().getTitle());
} }
stopTrack(); stopTrack();
return CommandExecutionResult.info("Queue is empty, leaving voice channel"); return QueueActionResult.info("Queue is empty, leaving voice channel");
} }
} }
@@ -193,11 +192,11 @@ public class QueueManager {
} }
} }
public CommandExecutionResult skip() { public QueueActionResult skip() {
return skip(1); return skip(1);
} }
public CommandExecutionResult skip(int nbTrack) { public QueueActionResult skip(int nbTrack) {
if (nbTrack < 1) { if (nbTrack < 1) {
nbTrack = 1; nbTrack = 1;
} }
@@ -205,7 +204,7 @@ public class QueueManager {
synchronized (queue) { synchronized (queue) {
if (queue.isEmpty()) { if (queue.isEmpty()) {
scheduler.getMusicManager().disconnect(); scheduler.getMusicManager().disconnect();
return CommandExecutionResult.info("Queue is empty, leaving voice channel"); return QueueActionResult.info("Queue is empty, leaving voice channel");
} }
if (nbTrack > queue.size()) { if (nbTrack > queue.size()) {
@@ -219,19 +218,19 @@ public class QueueManager {
if (queue.isEmpty()) { if (queue.isEmpty()) {
scheduler.getMusicManager().disconnect(); scheduler.getMusicManager().disconnect();
return CommandExecutionResult.info("Queue is empty, leaving voice channel"); return QueueActionResult.info("Queue is empty, leaving voice channel");
} }
startNextTrack(); startNextTrack();
return CommandExecutionResult.success(String.format("Skipping %s track%s..", nbTrack, (nbTrack > 1 ? "s" : ""))); return QueueActionResult.success(String.format("Skipping %s track%s..", nbTrack, (nbTrack > 1 ? "s" : "")));
} }
} }
public CommandExecutionResult shuffle() { public QueueActionResult shuffle() {
synchronized (queue) { synchronized (queue) {
if (queue.size() <= 3) { if (queue.size() <= 3) {
return CommandExecutionResult.warn("The queue is too small to be shuffled"); return QueueActionResult.warn("The queue is too small to be shuffled");
} }
logger.debug("Guild {}: shuffling queue", guild.getId()); logger.debug("Guild {}: shuffling queue", guild.getId());
@@ -245,18 +244,18 @@ public class QueueManager {
queue.add(current); queue.add(current);
queue.addAll(newQueue); queue.addAll(newQueue);
return CommandExecutionResult.success("The queue is now shuffled."); return QueueActionResult.success("The queue is now shuffled.");
} }
} }
public CommandExecutionResult removeFromQueue(int index) { public QueueActionResult removeFromQueue(int index) {
synchronized (queue) { synchronized (queue) {
if (queue.isEmpty() || index < 1) { if (queue.isEmpty() || index < 1) {
return CommandExecutionResult.none(); return QueueActionResult.none();
} }
if (index > queue.size()) { if (index > queue.size()) {
return CommandExecutionResult.error("Position is out of bounds"); return QueueActionResult.error("Position is out of bounds");
} }
logger.debug("Guild {}: removing index {} from queue", guild.getId(), index - 1); logger.debug("Guild {}: removing index {} from queue", guild.getId(), index - 1);
@@ -268,11 +267,11 @@ public class QueueManager {
queue.clear(); queue.clear();
queue.addAll(newQueue); queue.addAll(newQueue);
return CommandExecutionResult.success(String.format("Removed track #%d from the queue", index)); return QueueActionResult.success(String.format("Removed track #%d from the queue", index));
} }
skip(); skip();
return CommandExecutionResult.none(); return QueueActionResult.none();
} }
} }
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel;
@@ -29,7 +28,7 @@ public class VoteSkipService {
* from the same user are idempotent and the worst-case outcome is a single extra "vote * from the same user are idempotent and the worst-case outcome is a single extra "vote
* recorded" message before the skip fires on the next invocation.</p> * recorded" message before the skip fires on the next invocation.</p>
*/ */
public CommandExecutionResult voteSkip(CommandContext context, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying) { public QueueActionResult voteSkip(CommandContext context, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying) {
return voteSkip(context, queueManager, audioChannel, isCurrentlyPlaying, DEFAULT_VOTE_THRESHOLD_PERCENT); return voteSkip(context, queueManager, audioChannel, isCurrentlyPlaying, DEFAULT_VOTE_THRESHOLD_PERCENT);
} }
@@ -37,7 +36,7 @@ public class VoteSkipService {
* Same as {@link #voteSkip(CommandContext, QueueManager, AudioChannel, boolean)} but with a * Same as {@link #voteSkip(CommandContext, QueueManager, AudioChannel, boolean)} but with a
* configurable vote threshold percentage of connected listeners. * configurable vote threshold percentage of connected listeners.
*/ */
public CommandExecutionResult voteSkip(CommandContext context, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying, int thresholdPercent) { public QueueActionResult voteSkip(CommandContext context, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying, int thresholdPercent) {
return voteSkip(context.getGuildUser(), context.getGuild().getId(), queueManager, audioChannel, isCurrentlyPlaying, thresholdPercent); return voteSkip(context.getGuildUser(), context.getGuild().getId(), queueManager, audioChannel, isCurrentlyPlaying, thresholdPercent);
} }
@@ -46,18 +45,18 @@ public class VoteSkipService {
* driven directly by a {@link Member}, so it can be invoked from non-command callers such as * driven directly by a {@link Member}, so it can be invoked from non-command callers such as
* the REST API, which has no {@link CommandContext} to work with. * the REST API, which has no {@link CommandContext} to work with.
*/ */
public CommandExecutionResult voteSkip(Member voter, String guildId, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying, int thresholdPercent) { public QueueActionResult voteSkip(Member voter, String guildId, QueueManager queueManager, AudioChannel audioChannel, boolean isCurrentlyPlaying, int thresholdPercent) {
if (audioChannel == null) { if (audioChannel == null) {
return CommandExecutionResult.warn("Bot is not connected to a voice channel"); return QueueActionResult.warn("Bot is not connected to a voice channel");
} }
if (!isCurrentlyPlaying) { if (!isCurrentlyPlaying) {
return CommandExecutionResult.warn("No tracks are currently playing"); return QueueActionResult.warn("No tracks are currently playing");
} }
TrackInfo info = queueManager.peekHeadOfQueue(); TrackInfo info = queueManager.peekHeadOfQueue();
if (info == null) { if (info == null) {
return CommandExecutionResult.warn("Queue is empty"); return QueueActionResult.warn("Queue is empty");
} }
// Minus the bot itself // Minus the bot itself
@@ -65,7 +64,7 @@ public class VoteSkipService {
if (connected < MIN_MEMBERS_FOR_VOTE) { if (connected < MIN_MEMBERS_FOR_VOTE) {
queueManager.skip(); queueManager.skip();
return CommandExecutionResult.info("Skipping current track since not enough people are connected to the voice channel."); return QueueActionResult.info("Skipping current track since not enough people are connected to the voice channel.");
} }
double needed = connected * (thresholdPercent / 100.0); double needed = connected * (thresholdPercent / 100.0);
@@ -77,7 +76,7 @@ public class VoteSkipService {
return queueManager.skip(); return queueManager.skip();
} }
return CommandExecutionResult.success(String.format("Adding your vote to skip the current track. Current votes: %d/%d", return QueueActionResult.success(String.format("Adding your vote to skip the current track. Current votes: %d/%d",
info.getSkipCount(), info.getSkipCount(),
(int) Math.ceil(needed)) (int) Math.ceil(needed))
); );
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.music;
*/ */
import dev.arbjerg.lavalink.client.player.Track; import dev.arbjerg.lavalink.client.player.Track;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
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.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -126,8 +125,8 @@ public class QueueManagerTest {
@Test @Test
public void clearQueueOnEmptyReturnsWarn() { public void clearQueueOnEmptyReturnsWarn() {
CommandExecutionResult result = queueManager.clearQueue(); QueueActionResult result = queueManager.clearQueue();
assertEquals(CommandExecutionResult.Result.WARN, result.getResult()); assertEquals(QueueActionResult.Status.WARN, result.getStatus());
verify(callback, never()).onQueueEmpty(); verify(callback, never()).onQueueEmpty();
} }
@@ -137,9 +136,9 @@ public class QueueManagerTest {
queueManager.queue(mockTrack("t1"), mockMember("u1")); queueManager.queue(mockTrack("t1"), mockMember("u1"));
queueManager.queue(mockTrack("t2"), mockMember("u1")); queueManager.queue(mockTrack("t2"), mockMember("u1"));
CommandExecutionResult result = queueManager.clearQueue(); QueueActionResult result = queueManager.clearQueue();
assertEquals(CommandExecutionResult.Result.SUCCESS, result.getResult()); assertEquals(QueueActionResult.Status.SUCCESS, result.getStatus());
assertTrue(queueManager.isEmpty()); assertTrue(queueManager.isEmpty());
verify(callback).onQueueEmpty(); verify(callback).onQueueEmpty();
} }
@@ -148,8 +147,8 @@ public class QueueManagerTest {
@Test @Test
public void skipOnEmptyQueueDisconnects() { public void skipOnEmptyQueueDisconnects() {
CommandExecutionResult result = queueManager.skip(); QueueActionResult result = queueManager.skip();
assertEquals(CommandExecutionResult.Result.INFO, result.getResult()); assertEquals(QueueActionResult.Status.INFO, result.getStatus());
verify(musicManager).disconnect(); verify(musicManager).disconnect();
} }
@@ -163,8 +162,8 @@ public class QueueManagerTest {
queueManager.queue(t1, member); queueManager.queue(t1, member);
queueManager.queue(t2, member); queueManager.queue(t2, member);
CommandExecutionResult result = queueManager.skip(1); QueueActionResult result = queueManager.skip(1);
assertEquals(CommandExecutionResult.Result.SUCCESS, result.getResult()); assertEquals(QueueActionResult.Status.SUCCESS, result.getStatus());
// After skip(1): head (t1) removed via startNextTrack, t2 is now head // After skip(1): head (t1) removed via startNextTrack, t2 is now head
assertEquals(1, queueManager.getQueueSize()); assertEquals(1, queueManager.getQueueSize());
} }
@@ -192,8 +191,8 @@ public class QueueManagerTest {
when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(t1); when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(t1);
queueManager.queue(t1, mockMember("u1")); queueManager.queue(t1, mockMember("u1"));
CommandExecutionResult result = queueManager.shuffle(); QueueActionResult result = queueManager.shuffle();
assertEquals(CommandExecutionResult.Result.WARN, result.getResult()); assertEquals(QueueActionResult.Status.WARN, result.getStatus());
} }
@Test @Test
@@ -207,8 +206,8 @@ public class QueueManagerTest {
queueManager.queue(mockTrack("t3"), member); queueManager.queue(mockTrack("t3"), member);
queueManager.queue(mockTrack("t4"), member); queueManager.queue(mockTrack("t4"), member);
CommandExecutionResult result = queueManager.shuffle(); QueueActionResult result = queueManager.shuffle();
assertEquals(CommandExecutionResult.Result.SUCCESS, result.getResult()); assertEquals(QueueActionResult.Status.SUCCESS, result.getStatus());
// First track should still be t1 // First track should still be t1
List<TrackInfo> q = queueManager.getQueue(); List<TrackInfo> q = queueManager.getQueue();
@@ -220,8 +219,8 @@ public class QueueManagerTest {
@Test @Test
public void removeFromQueueEmptyReturnsNone() { public void removeFromQueueEmptyReturnsNone() {
CommandExecutionResult result = queueManager.removeFromQueue(1); QueueActionResult result = queueManager.removeFromQueue(1);
assertEquals(CommandExecutionResult.Result.NOACTION, result.getResult()); assertEquals(QueueActionResult.Status.NOACTION, result.getStatus());
} }
@Test @Test
@@ -229,8 +228,8 @@ public class QueueManagerTest {
when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(mockTrack("t1")); when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(mockTrack("t1"));
queueManager.queue(mockTrack("t1"), mockMember("u1")); queueManager.queue(mockTrack("t1"), mockMember("u1"));
CommandExecutionResult result = queueManager.removeFromQueue(5); QueueActionResult result = queueManager.removeFromQueue(5);
assertEquals(CommandExecutionResult.Result.ERROR, result.getResult()); assertEquals(QueueActionResult.Status.ERROR, result.getStatus());
} }
@Test @Test
@@ -238,8 +237,8 @@ public class QueueManagerTest {
when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(mockTrack("t1")); when(scheduler.getCurrentTrack()).thenReturn(null).thenReturn(mockTrack("t1"));
queueManager.queue(mockTrack("t1"), mockMember("u1")); queueManager.queue(mockTrack("t1"), mockMember("u1"));
CommandExecutionResult result = queueManager.removeFromQueue(0); QueueActionResult result = queueManager.removeFromQueue(0);
assertEquals(CommandExecutionResult.Result.NOACTION, result.getResult()); assertEquals(QueueActionResult.Status.NOACTION, result.getStatus());
} }
// --- RemoveTracksFromUser --- // --- RemoveTracksFromUser ---
@@ -247,8 +246,8 @@ public class QueueManagerTest {
@Test @Test
public void removeTracksFromUserNoMatchReturnsInfo() { public void removeTracksFromUserNoMatchReturnsInfo() {
Member member = mockMember("u1"); Member member = mockMember("u1");
CommandExecutionResult result = queueManager.removeTracksFromUser(member); QueueActionResult result = queueManager.removeTracksFromUser(member);
assertEquals(CommandExecutionResult.Result.INFO, result.getResult()); assertEquals(QueueActionResult.Status.INFO, result.getStatus());
} }
// --- isEmpty / getQueueSize --- // --- isEmpty / getQueueSize ---
@@ -5,7 +5,6 @@ package me.bobert.netneutrality.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
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;
@@ -53,21 +52,21 @@ public class VoteSkipServiceTest {
@Test @Test
public void returnsWarnWhenNotConnected() { public void returnsWarnWhenNotConnected() {
CommandExecutionResult result = service.voteSkip(context, queueManager, null, true); QueueActionResult result = service.voteSkip(context, queueManager, null, true);
assertEquals(CommandExecutionResult.Result.WARN, result.getResult()); assertEquals(QueueActionResult.Status.WARN, result.getStatus());
} }
@Test @Test
public void returnsWarnWhenNotPlaying() { public void returnsWarnWhenNotPlaying() {
CommandExecutionResult result = service.voteSkip(context, queueManager, audioChannel, false); QueueActionResult result = service.voteSkip(context, queueManager, audioChannel, false);
assertEquals(CommandExecutionResult.Result.WARN, result.getResult()); assertEquals(QueueActionResult.Status.WARN, result.getStatus());
} }
@Test @Test
public void returnsWarnWhenQueueEmpty() { public void returnsWarnWhenQueueEmpty() {
when(queueManager.peekHeadOfQueue()).thenReturn(null); when(queueManager.peekHeadOfQueue()).thenReturn(null);
CommandExecutionResult result = service.voteSkip(context, queueManager, audioChannel, true); QueueActionResult result = service.voteSkip(context, queueManager, audioChannel, true);
assertEquals(CommandExecutionResult.Result.WARN, result.getResult()); assertEquals(QueueActionResult.Status.WARN, result.getStatus());
} }
@Test @Test
@@ -78,10 +77,10 @@ public class VoteSkipServiceTest {
Member u2 = mockMember("u2"); Member u2 = mockMember("u2");
when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2)); when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2));
when(context.getGuildUser()).thenReturn(u1); when(context.getGuildUser()).thenReturn(u1);
when(queueManager.skip()).thenReturn(CommandExecutionResult.success("Skipping")); when(queueManager.skip()).thenReturn(QueueActionResult.success("Skipping"));
CommandExecutionResult result = service.voteSkip(context, queueManager, audioChannel, true); QueueActionResult result = service.voteSkip(context, queueManager, audioChannel, true);
assertEquals(CommandExecutionResult.Result.INFO, result.getResult()); assertEquals(QueueActionResult.Status.INFO, result.getStatus());
verify(queueManager).skip(); verify(queueManager).skip();
} }
@@ -96,8 +95,8 @@ public class VoteSkipServiceTest {
when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2, u3)); when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2, u3));
when(context.getGuildUser()).thenReturn(voter); when(context.getGuildUser()).thenReturn(voter);
CommandExecutionResult result = service.voteSkip(context, queueManager, audioChannel, true); QueueActionResult result = service.voteSkip(context, queueManager, audioChannel, true);
assertEquals(CommandExecutionResult.Result.SUCCESS, result.getResult()); assertEquals(QueueActionResult.Status.SUCCESS, result.getStatus());
assertTrue(result.getMessage().contains("Adding your vote")); assertTrue(result.getMessage().contains("Adding your vote"));
assertEquals(1, trackInfo.getSkipCount()); assertEquals(1, trackInfo.getSkipCount());
} }
@@ -112,7 +111,7 @@ public class VoteSkipServiceTest {
Member voter1 = mockMember("voter1"); Member voter1 = mockMember("voter1");
Member voter2 = mockMember("voter2"); Member voter2 = mockMember("voter2");
when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2, u3)); when(audioChannel.getMembers()).thenReturn(List.of(bot, u1, u2, u3));
when(queueManager.skip()).thenReturn(CommandExecutionResult.success("Skipping")); when(queueManager.skip()).thenReturn(QueueActionResult.success("Skipping"));
// First vote // First vote
when(context.getGuildUser()).thenReturn(voter1); when(context.getGuildUser()).thenReturn(voter1);
@@ -120,8 +119,8 @@ public class VoteSkipServiceTest {
// Second vote triggers skip // Second vote triggers skip
when(context.getGuildUser()).thenReturn(voter2); when(context.getGuildUser()).thenReturn(voter2);
CommandExecutionResult result = service.voteSkip(context, queueManager, audioChannel, true); QueueActionResult result = service.voteSkip(context, queueManager, audioChannel, true);
assertEquals(CommandExecutionResult.Result.SUCCESS, result.getResult()); assertEquals(QueueActionResult.Status.SUCCESS, result.getStatus());
verify(queueManager).skip(); verify(queueManager).skip();
} }