✨ Ajout de la gestion audio avec les commandes play et stop, et implémentation des gestionnaires de musique
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
This commit is contained in:
parent
0fedd154ea
commit
f858684132
@ -1,21 +1,26 @@
|
|||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
application
|
||||||
id("java")
|
id("java")
|
||||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "fr.melaine.gerard.kiss.shot.acerola"
|
group = "fr.melaine.gerard.kiss.shot.acerola"
|
||||||
version = "1.0-SNAPSHOT"
|
version = "1.0-SNAPSHOT"
|
||||||
|
application {
|
||||||
|
mainClass = "org.camelia.studio.kiss.shot.acerola.KissShotAcerola"
|
||||||
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { setUrl("https://jitpack.io") }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("io.github.cdimascio:dotenv-kotlin:6.4.2")
|
implementation("io.github.cdimascio:dotenv-kotlin:6.4.2")
|
||||||
implementation("net.dv8tion:JDA:5.2.1")
|
implementation("net.dv8tion:JDA:5.2.1")
|
||||||
implementation("ch.qos.logback:logback-classic:1.5.12")
|
implementation("ch.qos.logback:logback-classic:1.5.12")
|
||||||
|
implementation ("dev.arbjerg:lavaplayer:2.2.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.audio;
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame;
|
||||||
|
import net.dv8tion.jda.api.audio.AudioSendHandler;
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class AudioPlayerSendHandler implements AudioSendHandler {
|
||||||
|
private final AudioPlayer audioPlayer;
|
||||||
|
private final ByteBuffer buffer;
|
||||||
|
private final MutableAudioFrame frame;
|
||||||
|
|
||||||
|
public AudioPlayerSendHandler(AudioPlayer audioPlayer) {
|
||||||
|
this.audioPlayer = audioPlayer;
|
||||||
|
this.buffer = ByteBuffer.allocate(1024);
|
||||||
|
this.frame = new MutableAudioFrame();
|
||||||
|
this.frame.setBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canProvide() {
|
||||||
|
return audioPlayer.provide(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer provide20MsAudio() {
|
||||||
|
((Buffer) buffer).flip();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpus() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.audio;
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||||
|
|
||||||
|
public class GuildMusicManager {
|
||||||
|
public final AudioPlayer audioPlayer;
|
||||||
|
public final TrackScheduler scheduler;
|
||||||
|
private final AudioPlayerSendHandler sendHandler;
|
||||||
|
|
||||||
|
public GuildMusicManager(AudioPlayerManager manager) {
|
||||||
|
this.audioPlayer = manager.createPlayer();
|
||||||
|
this.scheduler = new TrackScheduler(this.audioPlayer);
|
||||||
|
this.audioPlayer.addListener(this.scheduler);
|
||||||
|
this.sendHandler = new AudioPlayerSendHandler(this.audioPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AudioPlayerSendHandler getSendHandler() {
|
||||||
|
return sendHandler;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.audio;
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PlayerManager {
|
||||||
|
private static PlayerManager INSTANCE;
|
||||||
|
private final Map<Long, GuildMusicManager> musicManagers;
|
||||||
|
private final AudioPlayerManager audioPlayerManager;
|
||||||
|
|
||||||
|
public PlayerManager() {
|
||||||
|
this.musicManagers = new HashMap<>();
|
||||||
|
this.audioPlayerManager = new DefaultAudioPlayerManager();
|
||||||
|
AudioSourceManagers.registerRemoteSources(audioPlayerManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlayerManager getInstance() {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new PlayerManager();
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuildMusicManager getMusicManager(Guild guild) {
|
||||||
|
return musicManagers.computeIfAbsent(guild.getIdLong(), (guildId) -> {
|
||||||
|
final GuildMusicManager guildMusicManager = new GuildMusicManager(audioPlayerManager);
|
||||||
|
guild.getAudioManager().setSendingHandler(guildMusicManager.getSendHandler());
|
||||||
|
return guildMusicManager;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadAndPlay(TextChannel channel, String url) {
|
||||||
|
final GuildMusicManager musicManager = getMusicManager(channel.getGuild());
|
||||||
|
|
||||||
|
audioPlayerManager.loadItemOrdered(musicManager, url, new AudioLoadResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void trackLoaded(AudioTrack track) {
|
||||||
|
musicManager.scheduler.queue(track);
|
||||||
|
channel.sendMessage("Ajout à la file d'attente: `" + track.getInfo().title + "`").queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playlistLoaded(AudioPlaylist playlist) {
|
||||||
|
final AudioTrack track = playlist.getTracks().get(0);
|
||||||
|
musicManager.scheduler.queue(track);
|
||||||
|
channel.sendMessage("Ajout à la file d'attente: `" + track.getInfo().title + "`").queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noMatches() {
|
||||||
|
channel.sendMessage("Aucun résultat trouvé pour: " + url).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadFailed(FriendlyException e) {
|
||||||
|
channel.sendMessage("Erreur lors du chargement: " + e.getMessage()).queue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.audio;
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||||
|
|
||||||
|
public class TrackScheduler extends AudioEventAdapter {
|
||||||
|
private final AudioPlayer player;
|
||||||
|
|
||||||
|
public TrackScheduler(AudioPlayer player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queue(AudioTrack track) {
|
||||||
|
player.startTrack(track, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||||
|
if (endReason.mayStartNext) {
|
||||||
|
// Ici vous pouvez gérer la lecture de la prochaine piste si vous implémentez une file d'attente
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.commands.utils;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.GuildVoiceState;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
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.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 {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "playaudio";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Permet de lancer une musique en .mp3 dans un salon vocal";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OptionData> getOptions() {
|
||||||
|
return List.of(
|
||||||
|
new OptionData(OptionType.STRING, "url", "URL de la musique à jouer", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(SlashCommandInteractionEvent event) {
|
||||||
|
event.deferReply().queue();
|
||||||
|
String url = event.getOption("url").getAsString();
|
||||||
|
Member member = event.getMember();
|
||||||
|
GuildVoiceState voiceState = member.getVoiceState();
|
||||||
|
|
||||||
|
if (!member.getVoiceState().inAudioChannel()) {
|
||||||
|
event.getHook().editOriginal("Vous devez être connecté à un salon vocal pour utiliser cette commande !")
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VoiceChannel channel = voiceState.getChannel().asVoiceChannel();
|
||||||
|
|
||||||
|
AudioManager audioManager = event.getGuild().getAudioManager();
|
||||||
|
audioManager.openAudioConnection(channel);
|
||||||
|
PlayerManager.getInstance().getMusicManager(event.getGuild()).audioPlayer.setVolume(25);
|
||||||
|
PlayerManager.getInstance().loadAndPlay(event.getChannel().asTextChannel(), url);
|
||||||
|
event.getHook().editOriginal("Chargement du fichier audio en cours...").queue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.commands.utils;
|
||||||
|
|
||||||
|
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.managers.AudioManager;
|
||||||
|
|
||||||
|
public class StopCommand implements ISlashCommand{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "stopaudio"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Permet de stopper la musique en cours";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(SlashCommandInteractionEvent event) {
|
||||||
|
// Vérifier si l'utilisateur est dans un canal vocal
|
||||||
|
GuildVoiceState voiceState = event.getMember().getVoiceState();
|
||||||
|
if (!voiceState.inAudioChannel()) {
|
||||||
|
event.reply("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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.camelia.studio.kiss.shot.acerola.listeners;
|
||||||
|
|
||||||
|
import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||||
|
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
import net.dv8tion.jda.api.managers.AudioManager;
|
||||||
|
|
||||||
|
public class VoiceLeaveListener extends ListenerAdapter {
|
||||||
|
@Override
|
||||||
|
public void onGuildVoiceUpdate(GuildVoiceUpdateEvent event) {
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
AudioManager audioManager = guild.getAudioManager();
|
||||||
|
|
||||||
|
// Vérifie si le bot est connecté à un canal vocal
|
||||||
|
if (!audioManager.isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VoiceChannel botChannel = audioManager.getConnectedChannel().asVoiceChannel();
|
||||||
|
|
||||||
|
// Compte le nombre de membres dans le canal (excluant les bots)
|
||||||
|
long realMembersCount = botChannel.getMembers().stream()
|
||||||
|
.filter(member -> !member.getUser().isBot())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// Si plus personne dans le salon
|
||||||
|
if (realMembersCount == 0) {
|
||||||
|
// Arrête la musique
|
||||||
|
PlayerManager.getInstance().getMusicManager(guild).audioPlayer.stopTrack();
|
||||||
|
|
||||||
|
// Déconnecte le bot
|
||||||
|
audioManager.closeAudioConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import net.dv8tion.jda.api.JDA;
|
|||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
import org.camelia.studio.kiss.shot.acerola.listeners.GuildMemberJoinListener;
|
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.SlashCommandListener;
|
||||||
|
import org.camelia.studio.kiss.shot.acerola.listeners.VoiceLeaveListener;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -13,11 +14,13 @@ import java.util.List;
|
|||||||
public class ListenerManager {
|
public class ListenerManager {
|
||||||
private final List<ListenerAdapter> listener;
|
private final List<ListenerAdapter> listener;
|
||||||
private final Logger logger = LoggerFactory.getLogger(ListenerManager.class.getName());
|
private final Logger logger = LoggerFactory.getLogger(ListenerManager.class.getName());
|
||||||
|
|
||||||
public ListenerManager() {
|
public ListenerManager() {
|
||||||
listener = new ArrayList<>();
|
listener = new ArrayList<>();
|
||||||
|
|
||||||
addListener(new SlashCommandListener());
|
addListener(new SlashCommandListener());
|
||||||
addListener(new GuildMemberJoinListener());
|
addListener(new GuildMemberJoinListener());
|
||||||
|
addListener(new VoiceLeaveListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerListeners(JDA jda) {
|
public void registerListeners(JDA jda) {
|
||||||
|
Loading…
Reference in New Issue
Block a user