From ec0ce2ae25328d8ef70416596716ae447c822b9f Mon Sep 17 00:00:00 2001 From: Mysaa Date: Thu, 27 May 2021 22:34:01 +0200 Subject: [PATCH] =?UTF-8?q?Tentative=20d'ajout=20de=20l'api=20Youtube.=20?= =?UTF-8?q?=C3=89criture=20de=20pas=20mal=20de=20code=20qui=20manquait?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + src/com/bernard/djulia/DJuLIA.java | 349 +++++++++++++++++++++++++++-- 2 files changed, 337 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index a5e5214..7ad2954 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ bin/ .settings .classpath .project + +gradle/ +gradlew +gradlew.bat diff --git a/src/com/bernard/djulia/DJuLIA.java b/src/com/bernard/djulia/DJuLIA.java index 70ee6d3..2dd710c 100644 --- a/src/com/bernard/djulia/DJuLIA.java +++ b/src/com/bernard/djulia/DJuLIA.java @@ -1,27 +1,47 @@ package com.bernard.djulia; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ShortBuffer; +import java.security.GeneralSecurityException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.bernard.djulia.DJuLIA.JuliaAudioSendHandler.JuliaAudioChannel; +import com.bernard.djulia.DJuLIA.JuliaAudioTrack.Type; import com.bernard.juliabot.api.Command; import com.bernard.juliabot.api.Discord; import com.bernard.juliabot.api.DiscordCCommande; import com.bernard.juliabot.api.JuLIAddon; import com.bernard.juliabot.api.Trukilie; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.youtube.YouTube; +import com.google.api.services.youtube.model.SearchListResponse; +import com.google.api.services.youtube.model.SearchResult; +import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; @@ -34,21 +54,27 @@ import com.sedmelluq.discord.lavaplayer.player.event.TrackExceptionEvent; import com.sedmelluq.discord.lavaplayer.player.event.TrackStartEvent; import com.sedmelluq.discord.lavaplayer.player.event.TrackStuckEvent; 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 com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame; import net.dv8tion.jda.core.EmbedBuilder; +import net.dv8tion.jda.core.Permission; import net.dv8tion.jda.core.audio.AudioSendHandler; import net.dv8tion.jda.core.entities.Guild; import net.dv8tion.jda.core.entities.Member; import net.dv8tion.jda.core.entities.Message; import net.dv8tion.jda.core.entities.MessageChannel; +import net.dv8tion.jda.core.entities.MessageEmbed; import net.dv8tion.jda.core.entities.MessageEmbed.Field; import net.dv8tion.jda.core.entities.PrivateChannel; +import net.dv8tion.jda.core.entities.Role; import net.dv8tion.jda.core.entities.TextChannel; import net.dv8tion.jda.core.entities.User; import net.dv8tion.jda.core.entities.VoiceChannel; import net.dv8tion.jda.core.events.message.react.GenericMessageReactionEvent; +import net.dv8tion.jda.core.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.core.managers.AudioManager; @@ -63,6 +89,8 @@ public class DJuLIA { AudioPlayerManager audioManager; + YouTube youtube = getService(); + public DJuLIA() { audioManager = new DefaultAudioPlayerManager(); AudioSourceManagers.registerRemoteSources(audioManager); @@ -75,9 +103,54 @@ public class DJuLIA { bMsg(commande.getChannel(), "Impossible de deviner dans quel serveur vous voulez éxecuter cette commande (y a la commande soundGuild)"); return; } + String arg = commande.getStringCommand().substring(commande.getStringCommand().split(" ")[0].length()); + if(!arg.isEmpty())arg = arg.substring(1);// On enlève l'espace en trop + + if(arg.isEmpty()) { + if(!playButton(g, commande.getUser())) + bMsg(commande.getChannel(), g.getMember(commande.getUser()).getEffectiveName()+", vous en pouvez pas lancer la lecture ..."); + return; + }else { + JuliaAudioTrack jat = understandSong(arg, commande.getChannel(), commande.getUser()); + if(jat==null) { + bMsg(commande.getChannel(),"Je ne sais pas quoi jouer quand tu me dis "+arg); + return; + } + sender(g).getChan(MUSIC).play(jat); + if(playingMessages.get(g.getIdLong()).stream().anyMatch(m->m.getGuild().getIdLong()==g.getIdLong())) + addPlayingMessage(g, commande.getChannel().sendMessage("DUMMY MESSAGE").complete()); + + } + + + } + @Command(admin = false, description = "Permet de rajouter un morcau à la file d'attente", name = "queue") + public void queueCommand(DiscordCCommande commande) { + Guild g = getGuild(commande); + if(g==null) { + bMsg(commande.getChannel(), "Impossible de deviner dans quel serveur vous voulez éxecuter cette commande (y a la commande soundGuild)"); + return; + } + String arg = commande.getStringCommand().substring(commande.getStringCommand().split(" ")[0].length()); + if(!arg.isEmpty())arg = arg.substring(1);// On enlève l'espace en trop + + + JuliaAudioTrack jat = understandSong(arg, commande.getChannel(), commande.getUser()); + if(jat==null) { + bMsg(commande.getChannel(),"Je ne sais pas quoi jouer quand tu me dis "+arg); + return; + } + sender(g).getChan(MUSIC).queue(jat); + if(playingMessages.get(g.getIdLong()).stream().anyMatch(m->m.getGuild().getIdLong()==g.getIdLong())) + addPlayingMessage(g, commande.getChannel().sendMessage("DUMMY MESSAGE").complete()); + + + + } + @Command(admin = false, description = "Définis le serveur utilisé pour les commandes de son de cet utilisateur", name = "soundGuild", synopsis = "!!soundGuild [null|long guildid|string guildName]") public void soundGuild(DiscordCCommande commande) { String arg = commande.getStringCommand().substring(commande.getStringCommand().split(" ")[0].length()); @@ -120,27 +193,138 @@ public class DJuLIA { localGuilds.put(commande.getUser().getIdLong(), guilds.get(index).getIdLong()); } + + /// Commandes internes + public boolean playButton(Guild g,User u) { + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + if(!isDJ(g, u) && chan.currentTrack().querier.getIdLong() != u.getIdLong()) + return false; + chan.play(); + return true; + } + public boolean pauseButton(Guild g,User u) { + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + if(!isDJ(g, u) && chan.currentTrack().querier.getIdLong() != u.getIdLong()) + return false; + chan.pause(); + return true; + } + public boolean nextButton(Guild g,User u) { + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + if(!isDJ(g, u) && chan.currentTrack().querier.getIdLong() != u.getIdLong()) + return false; + chan.next(); + return true; + } + public boolean stopButton(Guild g,User u) { + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + if(!isDJ(g, u) && chan.currentTrack().querier.getIdLong() != u.getIdLong()) + return false; + chan.stop(); + return true; + } + public boolean loopButton(Guild g,User u) { + if(!isDJ(g, u)) + return false; + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + chan.looping = !chan.looping; + return true; + } + public boolean emptyButton(Guild g,User u) { + if(!isDJ(g, u)) + return false; + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + chan.empty(); + return true; + } + + + + Map> playingMessages = new HashMap<>(); + public void updatePlayingMessages(Guild g) { + if(!playingMessages.containsKey(g.getIdLong())) + return; + JuliaAudioChannel chan = guilds.get(g.getIdLong()).getChan(MUSIC); + JuliaAudioTrack playing = chan.currentTrack(); + EmbedBuilder builder = new EmbedBuilder(); + builder.setAuthor("DJulia in the place"); + builder.setTitle("Je joue "+playing.displayName+" demandé par"+g.getMember(playing.querier)); + int progressP100 = (int) (chan.playProgress()*10); + String t1 = "==========",t2 = "----------"; + builder.setDescription("`<"+t1.substring(progressP100)+t2.substring(10-progressP100)+">`"); + builder.addField("Il y a "+chan.trackLeftCount()+" musiques dans la queue", null, false); + MessageEmbed embed = builder.build(); + for(Message m : playingMessages.get(g.getIdLong())) + m.editMessage(embed).queue(); + } + public void addPlayingMessage(Guild g,Message m) { + if(!playingMessages.containsKey(g.getIdLong())) + playingMessages.put(g.getIdLong(),new HashSet<>()); + playingMessages.get(g.getIdLong()).add(m); + updatePlayingMessages(g); + m.addReaction("▶").queue(); + m.addReaction("⏸").queue(); + m.addReaction("⏭").queue(); + m.addReaction("⏹").queue(); + m.addReaction("🔁").queue(); + m.addReaction("🗑").queue(); + } + @Discord(description = "Vérifie les boutons des playing messages") + public void onReact(MessageReactionAddEvent e) { + if(!playingMessages.containsKey(e.getGuild().getIdLong()))return; + if(!playingMessages.get(e.getGuild().getIdLong()).stream().map(Message::getIdLong).anyMatch(l->l==e.getMessageIdLong()))return; + if(e.getReactionEmote().isEmote())return; + if(!"▶⏸⏭⏹🔁🗑".contains(e.getReactionEmote().getName()))return; + Guild g = getGuild(e.getUser(),e.getChannel()); + User u = e.getUser(); + switch(e.getReactionEmote().getName()) { + case "▶": + playButton(g, u); + break; + case "⏸": + pauseButton(g, u); + break; + case "⏭": + nextButton(g, u); + break; + case "⏹": + stopButton(g, u); + break; + case "🔁": + loopButton(g, u); + break; + case "🗑": + emptyButton(g, u); + break; + + } + } + + public Guild getGuild(DiscordCCommande commande) { + return getGuild(commande.getUser(), commande.getChannel()); + } + public Guild getGuild(User u,MessageChannel chan) { + + if(chan instanceof TextChannel) + return ((TextChannel)chan).getGuild(); - if(commande.getChannel() instanceof TextChannel) - return ((TextChannel)commande.getChannel()).getGuild(); + if(localGuilds.containsKey(u.getIdLong())) + return Trukilie.jda().getGuildById(localGuilds.get(u.getIdLong())); - if(localGuilds.containsKey(commande.getUser().getIdLong())) - return Trukilie.jda().getGuildById(localGuilds.get(commande.getUser().getIdLong())); - - List mutualGuilds = commande.getUser().getMutualGuilds(); + List mutualGuilds = u.getMutualGuilds(); if(mutualGuilds.size() == 1) return mutualGuilds.get(0); Set vocalGuilds = mutualGuilds.stream() - .filter(gu->gu.getMember(commande.getUser()).getVoiceState().inVoiceChannel()) + .filter(gu->gu.getMember(u).getVoiceState().inVoiceChannel()) .collect(Collectors.toSet()); if(vocalGuilds.size() == 1) return vocalGuilds.iterator().next(); Set vocalGuildsWithJulia = vocalGuilds.stream() .filter(g->guilds.containsKey(g.getIdLong())) - .filter(g->guilds.get(g.getIdLong()).currentChannel.getId().equals(g.getMember(commande.getUser()).getVoiceState().getAudioChannel().getId())) + .filter(g->guilds.get(g.getIdLong()).currentChannel.getId().equals(g.getMember(u).getVoiceState().getAudioChannel().getId())) .collect(Collectors.toSet()); if(vocalGuildsWithJulia.size() == 1) return vocalGuildsWithJulia.iterator().next(); @@ -148,7 +332,74 @@ public class DJuLIA { return null; } - + public JuliaAudioTrack understandSong(String text,MessageChannel chan, User u) { + //TODO every case + Pattern youtubeURL = Pattern.compile("^(https?\\:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.?be)\\/.+$"); + String toLoad = text; + String displayName = toLoad; + Type type = Type.LOCAL; + if(!youtubeURL.matcher(text).find()) { + //Search in files + //TODO à implémenter : les fichiers locaux* + + //else make youtube search + try { + YouTube.Search.List request = youtube.search().list("snippet"); + SearchListResponse response = request.setMaxResults(10L) + .setQ(text) + .setType("video") + .execute(); + List results = response.getItems(); + int index = selector(chan, u, "Quelle vidéo Youtube voulez-vous lire ?", results.stream().map(s->s.getSnippet().getTitle()+" de "+s.getSnippet().getChannelTitle()).collect(Collectors.toList())); + toLoad = "htp://youtu.be/"+results.get(index).getId(); + displayName = results.get(index).getSnippet().getTitle(); + type = Type.YOUTUBE; + //TODO traiter le cas pas de réponce + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + AudioTrack[] t = {null}; + try { + audioManager.loadItem(toLoad, new AudioLoadResultHandler() { + + @Override + public void trackLoaded(AudioTrack track) { + t[0] = track; + } + + @Override + public void playlistLoaded(AudioPlaylist playlist) { + //TODO gérer les playlistes + } + + @Override + public void noMatches() { + System.err.println("Les vérifications n'on pas marchées DJulia"); + } + + @Override + public void loadFailed(FriendlyException exception) { + exception.printStackTrace(); + } + }).get(); + return new JuliaAudioTrack(t[0], type, toLoad, u, displayName); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + return null; + } + + public boolean isDJ(Guild g,User u) { + Optional rr = g.getRolesByName("dj", true).stream().findAny(); + if(!rr.isPresent()) + return g.getMember(u).hasPermission(Permission.ADMINISTRATOR); + else + return g.getMember(u).getRoles().contains(rr.get()); + } Map waitingEmojis = new HashMap<>(); Map everyEventLocks = new HashMap<>(); @@ -256,6 +507,13 @@ public class DJuLIA { chan.sendMessage(builder.build()).queue(); } + public JuliaAudioSendHandler sender(Guild g) { + if(guilds.containsKey(g.getIdLong())) + return guilds.get(g.getIdLong()); + guilds.put(g.getIdLong(), new JuliaAudioSendHandler(g)); + return guilds.get(g.getIdLong()); + } + public class JuliaAudioSendHandler implements AudioSendHandler{ VoiceChannel currentChannel; Guild currentGuild; @@ -296,10 +554,14 @@ public class DJuLIA { playlist = new ArrayList<>(); } + public void play() { + if(playlistIndex getPlaylist() { @@ -326,8 +621,10 @@ public class DJuLIA { if(playlist.size()-1>=playlistIndex) { if(!looping) playlistIndex++; - player.playTrack(playlist.get(playlistIndex).track); - player.setPaused(false); + if(playlistIndexsb.get(j)/n).sum())); } + updatePlayingMessages(currentGuild); return result; } @@ -398,14 +696,16 @@ public class DJuLIA { Type type; String identifier; User querier; + String displayName; - public JuliaAudioTrack(AudioTrack track, Type type, String identifier, User querier) { + public JuliaAudioTrack(AudioTrack track, Type type, String identifier, User querier,String displayName) { this.track = track; this.type = type; this.identifier = identifier; this.querier = querier; + this.displayName = displayName; } @@ -416,4 +716,21 @@ public class DJuLIA { } } + + + private static final String APPLICATION_NAME = "DJulia"; + private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + + /** + * Build and return an authorized API client service. + * + * @return an authorized API client service + * @throws GeneralSecurityException, IOException + */ + public static YouTube getService() throws GeneralSecurityException, IOException { + final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + return new YouTube.Builder(httpTransport, JSON_FACTORY, null) + .setApplicationName(APPLICATION_NAME) + .build(); + } }