diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/KissShotAcerola.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/KissShotAcerola.java index 3fcafd7..93c6baa 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/KissShotAcerola.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/KissShotAcerola.java @@ -1,6 +1,6 @@ package org.camelia.studio.kiss.shot.acerola; -import org.camelia.studio.kiss.shot.acerola.listeners.ReadyListener; +import org.camelia.studio.kiss.shot.acerola.listeners.bot.ReadyListener; import org.camelia.studio.kiss.shot.acerola.managers.ListenerManager; import org.camelia.studio.kiss.shot.acerola.utils.Configuration; import net.dv8tion.jda.api.JDA; diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/audio/TrackScheduler.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/audio/TrackScheduler.java index c358e68..6c80bed 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/audio/TrackScheduler.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/audio/TrackScheduler.java @@ -34,6 +34,16 @@ public class TrackScheduler extends AudioEventAdapter { player.startTrack(queue.poll(), false); } + public void nextTrack(int nextTrack) { + if (nextTrack < 1) { + return; + } + for (int i = 0; i < nextTrack - 1; i++) { + queue.poll(); + } + player.startTrack(queue.poll(), false); + } + public Queue getQueue() { return queue; } diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/PlayAudioCommand.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/PlayCommand.java similarity index 95% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/PlayAudioCommand.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/PlayCommand.java index 089ff1a..ea583e4 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/PlayAudioCommand.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/PlayCommand.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.commands.utils; +package org.camelia.studio.kiss.shot.acerola.commands.audio; import net.dv8tion.jda.api.audio.hooks.ConnectionListener; import net.dv8tion.jda.api.audio.hooks.ConnectionStatus; @@ -15,10 +15,10 @@ import java.util.List; import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager; import org.camelia.studio.kiss.shot.acerola.interfaces.ISlashCommand; -public class PlayAudioCommand implements ISlashCommand { +public class PlayCommand implements ISlashCommand { @Override public String getName() { - return "playaudio"; + return "play"; } @Override diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/QueueCommand.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/QueueCommand.java new file mode 100644 index 0000000..671bce7 --- /dev/null +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/QueueCommand.java @@ -0,0 +1,127 @@ +package org.camelia.studio.kiss.shot.acerola.commands.audio; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +import org.camelia.studio.kiss.shot.acerola.audio.GuildMusicManager; +import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager; +import org.camelia.studio.kiss.shot.acerola.interfaces.ISlashCommand; + +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.managers.AudioManager; + +import java.awt.Color; + +public class QueueCommand implements ISlashCommand { + final int TRACKS_PER_PAGE = 10; + + @Override + public String getName() { + return "queue"; + } + + @Override + public String getDescription() { + return "Permet de passer à la musique suivante"; + } + + @Override + public List getOptions() { + return List.of( + new OptionData(OptionType.INTEGER, "page", "Numéro de la page à visionner").setRequired(false)); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + event.deferReply().queue(); + OptionMapping option = event.getOption("page"); + int page = 1; + + if (option != null) { + page = (int) option.getAsInt(); + } + + if (page < 1) { + event.getHook().editOriginal("La page doit être supérieure à 0.").queue(); + return; + } + + AudioManager audioManager = event.getGuild().getAudioManager(); + if (!audioManager.isConnected()) { + event.getHook().editOriginal("Je ne suis pas connecté à un canal vocal !").queue(); + return; + } + + // On passe aux musiques suivantes + GuildMusicManager musicManager = PlayerManager.getInstance().getMusicManager(event.getGuild()); + Queue queue = musicManager.scheduler.getQueue(); + AudioTrack currentTrack = musicManager.audioPlayer.getPlayingTrack(); + + if (queue.isEmpty()) { + event.getHook().editOriginal("La file d'attente est vide.").queue(); + return; + } + + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("🎵 File d'attente"); + embed.setColor(Color.BLUE); + + // Calculer le nombre total de pages + int totalPages = (int) Math.ceil((double) queue.size() / TRACKS_PER_PAGE); + if (totalPages == 0) + totalPages = 1; + + // Vérifier que la page demandée est valide + if (page < 1 || page > totalPages) { + event.getHook().editOriginal("❌ Page invalide ! (1-" + totalPages + ")").queue(); + return; + } + + // Afficher la file d'attente pour la page demandée + if (queue.isEmpty()) { + embed.setDescription("Aucune musique dans la file d'attente"); + } else { + List trackList = new ArrayList<>(queue); + int startIndex = (page - 1) * TRACKS_PER_PAGE; + int endIndex = Math.min(startIndex + TRACKS_PER_PAGE, trackList.size()); + + StringBuilder queueList = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + AudioTrack track = trackList.get(i); + queueList.append(i + 1) + .append(". `") + .append(track.getInfo().title) + .append("` [") + .append(formatTime(track.getDuration())) + .append("]\n"); + } + + embed.setDescription(queueList.toString()); + } + + long totalDuration = queue.stream().mapToLong(AudioTrack::getDuration).sum(); + embed.setFooter(String.format("Page %d/%d • %d musiques • Durée totale: %s", + page, totalPages, queue.size(), formatTime(totalDuration))); + + event.getHook().editOriginalEmbeds(embed.build()).queue(); + } + + private String formatTime(long timeInMillis) { + long hours = timeInMillis / 3600000; + long minutes = (timeInMillis % 3600000) / 60000; + long seconds = (timeInMillis % 60000) / 1000; + + if (hours > 0) { + return String.format("%d:%02d:%02d", hours, minutes, seconds); + } else { + return String.format("%d:%02d", minutes, seconds); + } + } +} diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/SkipCommand.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/SkipCommand.java new file mode 100644 index 0000000..93c5d91 --- /dev/null +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/SkipCommand.java @@ -0,0 +1,70 @@ +package org.camelia.studio.kiss.shot.acerola.commands.audio; + +import java.util.List; + +import org.camelia.studio.kiss.shot.acerola.audio.GuildMusicManager; +import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager; +import org.camelia.studio.kiss.shot.acerola.interfaces.ISlashCommand; + +import net.dv8tion.jda.api.entities.GuildVoiceState; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.managers.AudioManager; + +public class SkipCommand implements ISlashCommand { + + @Override + public String getName() { + return "skip"; + } + + @Override + public String getDescription() { + return "Permet de passer à la musique suivante"; + } + + @Override + public List getOptions() { + return List.of( + new OptionData(OptionType.INTEGER, "tracknumber", "Nombre de musique à passer").setRequired(false)); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + event.deferReply().queue(); + OptionMapping option = event.getOption("tracknumber"); + int skipAmount = 1; + + if (option != null) { + skipAmount = (int) option.getAsInt(); + } + + // Vérifier si l'utilisateur est dans un canal vocal + GuildVoiceState voiceState = event.getMember().getVoiceState(); + if (!voiceState.inAudioChannel()) { + event.getHook().editOriginal("Vous devez être dans un canal vocal pour utiliser cette commande !").queue(); + return; + } + + // Vérifier si le bot est dans le même canal vocal + AudioManager audioManager = event.getGuild().getAudioManager(); + if (!audioManager.isConnected()) { + event.getHook().editOriginal("Je ne suis pas connecté à un canal vocal !").queue(); + return; + } + + if (voiceState.getChannel() != audioManager.getConnectedChannel()) { + event.getHook().editOriginal("Vous devez être dans le même canal vocal que moi !").queue(); + return; + } + + // On passe aux musiques suivantes + GuildMusicManager musicManager = PlayerManager.getInstance().getMusicManager(event.getGuild()); + musicManager.scheduler.nextTrack(skipAmount); + + event.getHook().editOriginal("Passage de %d musiques.".formatted(skipAmount)).queue(); + } + +} diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/StopAudioCommand.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/StopCommand.java similarity index 91% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/StopAudioCommand.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/StopCommand.java index a970d5c..ced52f1 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/StopAudioCommand.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/audio/StopCommand.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.commands.utils; +package org.camelia.studio.kiss.shot.acerola.commands.audio; import org.camelia.studio.kiss.shot.acerola.audio.GuildMusicManager; import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager; @@ -8,11 +8,11 @@ import net.dv8tion.jda.api.entities.GuildVoiceState; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.managers.AudioManager; -public class StopAudioCommand implements ISlashCommand{ +public class StopCommand implements ISlashCommand { @Override public String getName() { - return "stopaudio"; + return "stop"; } @Override @@ -35,7 +35,7 @@ public class StopAudioCommand implements ISlashCommand{ event.reply("Je ne suis pas connecté à un canal vocal !").queue(); return; } - + if (voiceState.getChannel() != audioManager.getConnectedChannel()) { event.reply("Vous devez être dans le même canal vocal que moi !").queue(); return; @@ -44,10 +44,10 @@ public class StopAudioCommand implements ISlashCommand{ // Arrêter la musique GuildMusicManager musicManager = PlayerManager.getInstance().getMusicManager(event.getGuild()); musicManager.audioPlayer.stopTrack(); - + // Déconnecter le bot audioManager.closeAudioConnection(); - + event.reply("Musique arrêtée et déconnexion du canal vocal.").queue(); } diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/MsgEditCommand.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/MsgEditCommand.java index 663194c..36528d6 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/MsgEditCommand.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/commands/utils/MsgEditCommand.java @@ -5,7 +5,6 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.ChannelType; -import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/ReadyListener.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/bot/ReadyListener.java similarity index 96% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/ReadyListener.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/bot/ReadyListener.java index 4096075..265f1cf 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/ReadyListener.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/bot/ReadyListener.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.listeners; +package org.camelia.studio.kiss.shot.acerola.listeners.bot; import org.camelia.studio.kiss.shot.acerola.utils.Configuration; import net.dv8tion.jda.api.JDA; diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/GuildMemberJoinListener.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/GuildMemberJoinListener.java similarity index 89% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/GuildMemberJoinListener.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/GuildMemberJoinListener.java index 3c421b1..81c4fb0 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/GuildMemberJoinListener.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/GuildMemberJoinListener.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.listeners; +package org.camelia.studio.kiss.shot.acerola.listeners.global; import org.camelia.studio.kiss.shot.acerola.utils.Configuration; import net.dv8tion.jda.api.entities.Member; @@ -6,9 +6,6 @@ import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import java.awt.*; - - public class GuildMemberJoinListener extends ListenerAdapter { @Override public void onGuildMemberJoin(GuildMemberJoinEvent event) { diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/SlashCommandListener.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/SlashCommandListener.java similarity index 91% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/SlashCommandListener.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/SlashCommandListener.java index 3618e70..2143ee4 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/SlashCommandListener.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/SlashCommandListener.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.listeners; +package org.camelia.studio.kiss.shot.acerola.listeners.global; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/VoiceLeaveListener.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/VoiceLeaveListener.java similarity index 95% rename from src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/VoiceLeaveListener.java rename to src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/VoiceLeaveListener.java index 157b555..8d9e51f 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/VoiceLeaveListener.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/listeners/global/VoiceLeaveListener.java @@ -1,4 +1,4 @@ -package org.camelia.studio.kiss.shot.acerola.listeners; +package org.camelia.studio.kiss.shot.acerola.listeners.global; import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager; diff --git a/src/main/java/org/camelia/studio/kiss/shot/acerola/managers/ListenerManager.java b/src/main/java/org/camelia/studio/kiss/shot/acerola/managers/ListenerManager.java index 019c8bd..90fdbaa 100644 --- a/src/main/java/org/camelia/studio/kiss/shot/acerola/managers/ListenerManager.java +++ b/src/main/java/org/camelia/studio/kiss/shot/acerola/managers/ListenerManager.java @@ -2,13 +2,11 @@ package org.camelia.studio.kiss.shot.acerola.managers; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.camelia.studio.kiss.shot.acerola.listeners.GuildMemberJoinListener; -import org.camelia.studio.kiss.shot.acerola.listeners.SlashCommandListener; -import org.camelia.studio.kiss.shot.acerola.listeners.VoiceLeaveListener; + +import org.camelia.studio.kiss.shot.acerola.utils.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.List; public class ListenerManager { @@ -16,11 +14,9 @@ public class ListenerManager { private final Logger logger = LoggerFactory.getLogger(ListenerManager.class.getName()); public ListenerManager() { - listener = new ArrayList<>(); - - addListener(new SlashCommandListener()); - addListener(new GuildMemberJoinListener()); - addListener(new VoiceLeaveListener()); + listener = ReflectionUtils.loadClasses( + "org.camelia.studio.kiss.shot.acerola.listeners.global", + ListenerAdapter.class); } public void registerListeners(JDA jda) { @@ -30,8 +26,4 @@ public class ListenerManager { logger.info("Listener {} enregistré !", listenerAdapter.getClass().getSimpleName()); } } - - private void addListener(ListenerAdapter listenerAdapter) { - this.listener.add(listenerAdapter); - } }