diff options
| author | Yukai (Donald) Shan <xxpinapelzxx@gmail.com> | 2022-12-07 10:03:23 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-07 10:03:23 -0800 |
| commit | 4749216320da6d8adac202f468c759db18a8b735 (patch) | |
| tree | 7ffb866ec622f6abfd88cf53f063c3a4cce499db | |
| parent | a1e8ba6d27f5d9496c3d389861c963d0a5e51295 (diff) | |
Add files via upload
| -rw-r--r-- | Main.java | 86 | ||||
| -rw-r--r-- | audio/AudioPlayerSendHandler.java | 38 | ||||
| -rw-r--r-- | audio/GuildMusicManager.java | 18 | ||||
| -rw-r--r-- | audio/Music.java | 507 | ||||
| -rw-r--r-- | audio/TrackScheduler.java | 59 | ||||
| -rw-r--r-- | commands/CommandManager.java | 82 | ||||
| -rw-r--r-- | utility/SpotifyAPI.java | 111 | ||||
| -rw-r--r-- | utility/URLChecker.java | 31 |
8 files changed, 932 insertions, 0 deletions
diff --git a/Main.java b/Main.java new file mode 100644 index 0000000..85b7e16 --- /dev/null +++ b/Main.java @@ -0,0 +1,86 @@ +import audio.Music;
+import commands.CommandManager;
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.JDABuilder;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.events.ReadyEvent;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import javax.security.auth.login.LoginException;
+import java.io.FileReader;
+
+
+
+public class Main extends ListenerAdapter {
+ public static JDABuilder jdabuilder = JDABuilder.createDefault(readSetting("discordToken")).addEventListeners(new Main());
+ public static JDA jda;
+
+ public static Music musicPlayer = new Music("$",readSetting("youtubeApi"));
+ public static void main( String[] args)
+ {
+ try {
+ jdabuilder.addEventListeners(musicPlayer);
+ jdabuilder.addEventListeners(new CommandManager(musicPlayer));
+ jda = jdabuilder.build();
+ setSlashCommands();
+ System.out.println("Bot Started");
+ } catch (LoginException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ public static void setSlashCommands(){
+ // jda.updateCommands().queue();
+ jda.upsertCommand(new CommandData("play","Adds a song to the queue with a URL or search terms").
+ addOption(OptionType.STRING,"term","The link or search terms of the music to queue")
+ ).queue();
+ jda.upsertCommand(new CommandData("leave","Clears the queue and disconnects the bot from voice channel")).queue();
+ jda.upsertCommand(new CommandData("showqueue","Shows the current queue")).queue();
+ jda.upsertCommand(new CommandData("pause","Pauses the player")).queue();
+ jda.upsertCommand(new CommandData("skip","Skips the current song")).queue();
+ jda.upsertCommand(new CommandData("nowplaying","Shows a detailed view of the current song playing")).queue();
+ jda.upsertCommand(new CommandData("stop","Stops the player and clears the queue")).queue();
+ jda.upsertCommand(new CommandData("remove","Remove a track in queue")).queue();
+ jda.upsertCommand(new CommandData("shuffle","Shuffle the current queue")).queue();
+ //jda.upsertCommand(new CommandData("inspect","Get more details on a song currently in queue")).queue();
+ jda.upsertCommand(new CommandData("vtmusic","Queues a set number of random VTuber songs and covers").
+ addOption(OptionType.INTEGER,"number","Number of songs to queue")
+ ).queue();
+ jda.upsertCommand(new CommandData("volume","Set the volume or leave blank to check current volume").
+ addOption(OptionType.INTEGER,"volume","Volume from 0-100")
+ ).queue();
+
+ }
+ public static String readSetting(String parameter){
+ Object obj = null;
+ try {
+ obj = new JSONParser().parse(new FileReader("settings//config.json"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ JSONObject jo = (JSONObject) obj;
+ return (String) jo.get(parameter);
+ }
+
+ @Override
+ public void onMessageReceived(MessageReceivedEvent e) {
+ JDA jda = e.getJDA();
+ Message message = e.getMessage();
+ String msg = message.getContentDisplay();
+ if(msg.startsWith("!maintenance") && e.getAuthor().getId().equals("246787839570739211")){
+ jda.getPresence().setActivity(net.dv8tion.jda.api.entities.Activity.watching("Maintenance Mode!"));
+ }
+
+ }
+
+ @Override
+ public void onReady(ReadyEvent event) {
+ System.out.println("Loading Complete");
+ }
+}
diff --git a/audio/AudioPlayerSendHandler.java b/audio/AudioPlayerSendHandler.java new file mode 100644 index 0000000..fb79ea7 --- /dev/null +++ b/audio/AudioPlayerSendHandler.java @@ -0,0 +1,38 @@ +package audio;
+
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
+import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame;
+import java.nio.Buffer;
+import net.dv8tion.jda.api.audio.AudioSendHandler;
+
+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() {
+ // returns true if audio was provided
+ return audioPlayer.provide(frame);
+ }
+
+ @Override
+ public ByteBuffer provide20MsAudio() {
+ // flip to make it a read buffer
+ ((Buffer) buffer).flip();
+ return buffer;
+ }
+
+ @Override
+ public boolean isOpus() {
+ return true;
+ }
+}
\ No newline at end of file diff --git a/audio/GuildMusicManager.java b/audio/GuildMusicManager.java new file mode 100644 index 0000000..0757422 --- /dev/null +++ b/audio/GuildMusicManager.java @@ -0,0 +1,18 @@ +package audio;
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
+public class GuildMusicManager {
+
+ public final AudioPlayer player;
+
+ public final TrackScheduler scheduler;
+ public GuildMusicManager(AudioPlayerManager manager) {
+ player = manager.createPlayer();
+ scheduler = new TrackScheduler(player);
+ player.addListener(scheduler);
+ }
+
+ public AudioPlayerSendHandler getSendHandler() {
+ return new AudioPlayerSendHandler(player);
+ }
+}
\ No newline at end of file diff --git a/audio/Music.java b/audio/Music.java new file mode 100644 index 0000000..45f0d3d --- /dev/null +++ b/audio/Music.java @@ -0,0 +1,507 @@ +package audio;
+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;
+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.EmbedBuilder;
+import net.dv8tion.jda.api.MessageBuilder;
+import net.dv8tion.jda.api.entities.*;
+import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
+import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu;
+import net.dv8tion.jda.api.managers.AudioManager;
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.jsoup.Jsoup;
+import se.michaelthelin.spotify.model_objects.specification.PlaylistTrack;
+import utility.SpotifyAPI;
+import utility.URLChecker;
+
+import java.awt.*;
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+
+public class Music extends ListenerAdapter {
+ ArrayList<String> hololiveMusicURL = new ArrayList<String>();
+ String ytapiKey = "";
+ String spotifyapiKey = "";
+ static String append = "!";
+ private URLChecker urlCheck = new URLChecker();
+ private final AudioPlayerManager playerManager;
+ private final Map<Long, GuildMusicManager> musicManagers;
+ private SpotifyAPI spotifyAPI = new SpotifyAPI();
+ public Music(String append, String ytapiKey) {
+ this.musicManagers = new HashMap<>();
+ this.ytapiKey = ytapiKey;
+ this.spotifyapiKey = spotifyapiKey;
+ this.append = append;
+ this.playerManager = new DefaultAudioPlayerManager();
+ AudioSourceManagers.registerRemoteSources(playerManager);
+ AudioSourceManagers.registerLocalSource(playerManager);
+ System.out.println("Filling Music List");
+ }
+ private synchronized GuildMusicManager getGuildAudioPlayer(Guild guild) {
+ long guildId = Long.parseLong(guild.getId());
+ GuildMusicManager musicManager = musicManagers.get(guildId);
+
+ if (musicManager == null) {
+ musicManager = new GuildMusicManager(playerManager);
+ musicManagers.put(guildId, musicManager);
+ }
+ guild.getAudioManager().setSendingHandler(musicManager.getSendHandler());
+
+ return musicManager;
+ }
+ private void populateVTuberMusic(){
+ try {
+ URL url = new URL("https://pinapelz.github.io/vTuberDiscordBot/hololiveMusic.txt");
+ BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
+ String line;
+ FileWriter writer = new FileWriter("data//hololiveMusic.txt");
+ while ((line = in.readLine()) != null) {
+ writer.write(line+"\n");
+ }
+ writer.close();
+ in.close();
+ }
+ catch (MalformedURLException e) {
+ System.out.println("Malformed URL: " + e.getMessage());
+ }
+ catch (IOException e) {
+ System.out.println("I/O Error: " + e.getMessage());
+ }
+ }
+ private void fillVTuberMusic(){
+ populateVTuberMusic();
+ Scanner s = null;
+ try {
+ s = new Scanner(new File("data//hololiveMusic.txt"));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ while (s.hasNext()){
+ hololiveMusicURL.add(s.nextLine());
+ }
+ s.close();
+ }
+ @Override
+ public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
+ Guild guild = event.getGuild();
+ GuildMusicManager mng = getGuildAudioPlayer(guild);
+ TrackScheduler scheduler = mng.scheduler;
+ String[] command = event.getMessage().getContentRaw().split(" ", 2);
+
+ if ((append+"play").equals(command[0]) && command.length == 2) {
+ loadAndPlay(event.getChannel(), command[1],true);
+ }
+ else if((append+"refreshlist").equals(command[0])){
+ event.getChannel().sendMessage("Refreshing songs database").queue();
+ fillVTuberMusic();
+ event.getChannel().sendMessage("Refresh Complete!").queue();
+ }
+ else if ((append+"shuffle").equals(command[0]))
+ {
+ if (scheduler.queue.isEmpty())
+ {
+ event.getChannel().sendMessage("The queue is currently empty!").queue();
+ return;
+ }
+
+ scheduler.shuffle();
+ event.getChannel().sendMessage("The queue has been shuffled!").queue();
+ }
+
+ else if("!holoadd".equals(command[0])){
+ event.getChannel().sendMessage("The url has been successfully added to the database").queue();
+ }
+ else if("!dev".equals(command[0])){
+ try {
+ spotifyAPI.clientCredentials_Sync();
+ }
+ catch (Exception e){
+
+ }
+ }
+
+ super.onGuildMessageReceived(event);
+ }
+ public void playMusic(SlashCommandEvent event){
+ try {
+ String userQuery = event.getOption("term").getAsString();
+ if (urlCheck.isURL(userQuery) && !urlCheck.getURLType(userQuery).equals("spotify")&&!urlCheck.getURLType(userQuery).equals("spotify-playlist")) { //The term is a URL
+ event.reply("Found Video: " + userQuery).queue();
+ loadAndPlay((TextChannel) event.getChannel(), userQuery, false);
+ } else {
+ try {
+ if (urlCheck.getURLType(userQuery).equals("spotify")){
+
+ event.deferReply().queue();
+ event.getHook().sendMessage("Matched Video From Spotify: " + returnTopVideoURL(spotifyAPI.getSearchTerm_sync(urlCheck.getSpotifyTrackID(userQuery)))).queue();
+ loadAndPlay((TextChannel) event.getChannel(), returnTopVideoURL(spotifyAPI.getSearchTerm_sync(urlCheck.getSpotifyTrackID(userQuery))), true);
+ }
+ else if(urlCheck.getURLType(userQuery).equals("spotify-playlist")){
+ event.deferReply().queue();
+ String randomSong = spotifyAPI.getRandomPlaylistTrack_Sync(urlCheck.getSpotifyPlaylistID(userQuery));
+ event.getHook().sendMessage("Matched Video From Spotify Playlist: " + returnTopVideoURL(spotifyAPI.getSearchTerm_sync(randomSong))).queue();
+ loadAndPlay((TextChannel) event.getChannel(), returnTopVideoURL(spotifyAPI.getSearchTerm_sync(randomSong)), true);
+ }
+ else {
+ event.reply("Found Video: " + returnTopVideoURL(userQuery)).queue();
+ loadAndPlay((TextChannel) event.getChannel(), returnTopVideoURL(userQuery), true);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+ catch(Exception e){
+ event.reply("Error! Hazukashii! " + e.toString());
+ }
+ }
+
+
+ public void setVolume( SlashCommandEvent event, String command){
+ Guild guild = event.getGuild();
+ GuildMusicManager mng = getGuildAudioPlayer(guild);
+ AudioPlayer player = mng.player;
+ if (command.equals("CHECK"))
+ {
+ event.reply("Current player volume: **" + player.getVolume() + "**").queue();
+ }
+ else
+ {
+ try
+ {
+ int newVolume = Math.max(10, Math.min(100, Integer.parseInt(command)));
+ int oldVolume = player.getVolume();
+ player.setVolume(newVolume);
+ event.reply("Player volume changed from `" + oldVolume + "` to `" + newVolume + "`").queue();
+ }
+ catch (NumberFormatException e)
+ {
+ event.reply("`" + command + "` is not a valid integer. (10 - 100)").queue();
+ }
+ }
+ }
+ public void stopPlayer(SlashCommandEvent event){
+ Guild guild = event.getGuild();
+ GuildMusicManager mng = getGuildAudioPlayer(guild);
+ AudioPlayer player = mng.player;
+ TrackScheduler scheduler = mng.scheduler;
+ scheduler.queue.clear();
+ player.stopTrack();
+ player.setPaused(false);
+ event.reply("Playback has been completely stopped and the queue has been cleared.").queue();
+ }
+ public void pausePlayer(final TextChannel channel, SlashCommandEvent event){
+ Guild guild = event.getGuild();
+ GuildMusicManager mng = getGuildAudioPlayer(guild);
+ AudioPlayer player = mng.player;
+ if (player.getPlayingTrack() == null)
+ {
+ event.reply("Cannot pause or resume player because no track is loaded for playing.").queue();
+ return;
+ }
+ player.setPaused(!player.isPaused());
+ if (player.isPaused())
+ event.reply("The player has been paused.").queue();
+ else
+ event.reply("The player has resumed playing.").queue();
+ }
+ public void queueVTMusic(final TextChannel channel, int songsToQueue){
+ fillVTuberMusic();
+ Collections.shuffle(hololiveMusicURL);
+ System.out.println("Requesting to queue " + songsToQueue + " songs");
+ System.out.println("Queueing all Hololive Music");
+ for (int i = 0;i<songsToQueue;i++){
+ loadAndPlay(channel, hololiveMusicURL.get(i),false);
+ }
+ }
+ public void showNowPlaying(SlashCommandEvent event){
+ Guild guild = event.getGuild();
+ GuildMusicManager mng = getGuildAudioPlayer(guild);
+ AudioPlayer player = mng.player;
+ AudioTrack currentTrack = player.getPlayingTrack();
+ if (currentTrack != null)
+ {
+ String currentTrackUrl = currentTrack.getInfo().uri;
+ String currentTrackUrlType = urlCheck.getURLType(currentTrackUrl);
+ String title = currentTrack.getInfo().title;
+ System.out.println(currentTrack.getInfo().uri);
+ String position = getTimestamp(currentTrack.getPosition());
+ String duration = getTimestamp(currentTrack.getDuration());
+ if(currentTrackUrlType=="yt") { //YOUTUBE EMBED
+ EmbedBuilder embed = new EmbedBuilder()
+ .setColor(new Color(0xFD0001))
+ .setTitle("Now Playing: " + title)
+ .setDescription(currentTrack.getInfo().author)
+ .setImage("https://img.youtube.com/vi/" + currentTrack.getIdentifier() + "/hqdefault.jpg");
+ embed.addField("Timestamp: ", "**[" + position + "/" + duration + "]**", false);
+ embed.addField("", "https://www.youtube.com/watch?v=" + currentTrack.getIdentifier(), false);
+ MessageBuilder messageBuilder = (MessageBuilder) new MessageBuilder().setEmbeds(embed.build());
+ event.reply(messageBuilder.build()).queue();
+ }
+ else if(currentTrackUrlType=="snd") { //SOUNDCLOUD EMBED
+ EmbedBuilder embed = new EmbedBuilder()
+ .setColor(new Color(0xFD5401))
+ .setTitle("Now Playing: " + title)
+ .setDescription(currentTrack.getInfo().author);
+ embed.addField("Timestamp: ", "**[" + position + "/" + duration + "]**", false);
+ embed.addField("", currentTrack.getInfo().uri, false);
+ MessageBuilder messageBuilder = (MessageBuilder) new MessageBuilder().setEmbeds(embed.build());
+ event.reply(messageBuilder.build()).queue();
+ }
+ else if(currentTrackUrlType=="twitch"){ //TWITCH EMBED
+ System.out.println("https://static-cdn.jtvnw.net/previews-ttv/live_user_" + currentTrack.getIdentifier() + "-440x248.jpg");
+ EmbedBuilder embed = new EmbedBuilder()
+ .setColor(new Color(0xA86FFE))
+ .setTitle("Now Playing: " + title)
+ .setDescription(currentTrack.getInfo().author)//https://static-cdn.jtvnw.net/previews-ttv/live_user_cdawgva-440x248.jpg
+ .setImage("https://static-cdn.jtvnw.net/previews-ttv/live_user_" + currentTrack.getIdentifier().replaceAll("https://www.twitch.tv/","") + "-440x248.jpg");
+ embed.addField("Timestamp: ", "Currently Live!", false);
+ embed.addField("", currentTrack.getIdentifier(), false);
+ MessageBuilder messageBuilder = (MessageBuilder) new MessageBuilder().setEmbeds(embed.build());
+ event.reply(messageBuilder.build()).queue();
+ }
+ }
+ else {
+ event.reply("The player is not currently playing anything!").queue();
+ }
+ }
+ public void showQueue(final TextChannel channel, @NotNull SlashCommandEvent event){
+ Queue<AudioTrack> queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue;
+ synchronized (queue)
+ {
+ if (queue.isEmpty())
+ {
+ event.reply("The queue is currently empty!").queue();
+ }
+ else
+ {
+ int trackCount = 0;
+ long queueLength = 0;
+ StringBuilder sb = new StringBuilder();
+ sb.append("```Current Queue: Entries: ").append(queue.size()).append("\n");
+ for (AudioTrack track : queue)
+ {
+ queueLength += track.getDuration();
+ if (trackCount < 10)
+ {
+ sb.append(trackCount+1 +". [").append(getTimestamp(track.getDuration())).append("] ");
+ sb.append(track.getInfo().title).append("\n");
+ trackCount++;
+ }
+ }
+ sb.append("\n").append("Total Queue Time Length: ").append(getTimestamp(queueLength)+"```");
+ event.reply(sb.toString()).queue();
+
+ }
+ }
+ }
+ public void showQueueMenu(final TextChannel channel, @NotNull SlashCommandEvent event, String param, String instruction){
+ Queue<AudioTrack> queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue;
+ List<SelectOption> trackMenuOptions = new ArrayList<SelectOption>();
+ synchronized (queue)
+ {
+ if (queue.isEmpty())
+ {
+ channel.sendMessage("The queue is currently empty!").queue();
+ }
+ else
+ {
+ int trackCount = 0;
+ for (AudioTrack track : queue)
+ {
+ if (trackCount != 25)
+ {
+ SelectOption option = SelectOption.of(track.getInfo().title,param+" "+track.getInfo().title);
+ trackMenuOptions.add(option);
+ trackCount++;
+ }
+ }
+ SelectionMenu menu = SelectionMenu.create("menu:class")
+ .setPlaceholder("-Select a track-") // shows the placeholder indicating what this menu is for
+ .setRequiredRange(1,1)// only one can be selected
+ .addOptions(trackMenuOptions)
+ .build();
+ event.reply(instruction)
+ .setEphemeral(true)
+ .addActionRow(menu)
+ .queue();
+
+ }
+ }
+ }
+ @Override
+ public void onSelectionMenu(SelectionMenuEvent event){
+ if(event.getValues().get(0).contains("remove-queue")) {
+ boolean deletedSong = false;
+ Queue<AudioTrack> queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue;
+ BlockingQueue<AudioTrack> newQueue = new LinkedBlockingQueue<>();
+ String trackName = event.getValues().get(0).replaceAll("remove-queue ", "");
+ synchronized (queue) {
+ if (queue.isEmpty())
+ {
+ event.reply("The queue is currently empty!").queue();
+ }
+ else {
+ for (AudioTrack track : queue) {
+ if (!track.getInfo().title.equals(trackName)) {
+ newQueue.add(track);
+ }
+ else{
+ deletedSong = true;
+ }
+ }
+ getGuildAudioPlayer(event.getGuild()).scheduler.queue = newQueue;
+ if(deletedSong){
+ event.reply("Removed " + trackName + " from the queue!").queue();
+ }
+ else{
+ event.reply("Could not find " + trackName + " in the queue!").queue();
+ }
+
+ }
+
+ }
+ }
+
+ else if(event.getValues().get(0).contains("inspect-queue")) { //THIS FEAUTURE IS NOT FINISHED
+ Queue<AudioTrack> queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue;
+ BlockingQueue<AudioTrack> newQueue = new LinkedBlockingQueue<>();
+ String trackName = event.getValues().get(0).replaceAll("inspect-queue ", "");
+ synchronized (queue) {
+ if (queue.isEmpty())
+ {
+ event.reply("The queue is currently empty!").queue();
+ }
+ else {
+ for (AudioTrack track : queue) {
+ if (!track.getInfo().title.equals(trackName)) {
+ newQueue.add(track);
+ }
+ else{
+
+ }
+ }
+ getGuildAudioPlayer(event.getGuild()).scheduler.queue = newQueue;
+ }
+
+ }
+ }
+ }
+ public void loadAndPlay(final @NotNull TextChannel channel, final String trackUrl, boolean returnMessage) {
+ GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild());
+ playerManager.loadItemOrdered(musicManager, trackUrl, new AudioLoadResultHandler() {
+ @Override
+ public void trackLoaded(AudioTrack track) {
+ if(returnMessage) {
+ channel.sendMessage("Adding to queue " + track.getInfo().title).queue();
+
+ }
+
+ play(channel.getGuild(), musicManager, track);
+ }
+
+ @Override
+ public void playlistLoaded(AudioPlaylist playlist) {
+ AudioTrack firstTrack = playlist.getSelectedTrack();
+
+ if (firstTrack == null) {
+ firstTrack = playlist.getTracks().get(0);
+ }
+; if(returnMessage){
+ channel.sendMessage("Adding to queue " + firstTrack.getInfo().title + " (first track of playlist " + playlist.getName() + ")").queue();
+ }
+
+
+ play(channel.getGuild(), musicManager, firstTrack);
+ }
+
+ @Override
+ public void noMatches() {
+ channel.sendMessage("Nothing found by " + trackUrl).queue();
+ }
+
+ @Override
+ public void loadFailed(FriendlyException exception) {
+ if(returnMessage) {
+ channel.sendMessage("Could not play: " + exception.getMessage()).queue();
+ System.out.println(exception);
+ }
+ }
+ });
+
+ }
+
+
+
+ private void play(Guild guild, GuildMusicManager musicManager, AudioTrack track) {
+ connectToFirstVoiceChannel(guild.getAudioManager());
+ musicManager.scheduler.queue(track);
+ BlockingQueue<AudioTrack> s = musicManager.scheduler.queue;
+
+ }
+
+ public void skipTrack(TextChannel channel,SlashCommandEvent event) {
+ GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild());
+ musicManager.scheduler.nextTrack();
+
+ event.reply("Skipped to next track.").queue();
+ }
+
+ private static void connectToFirstVoiceChannel(AudioManager audioManager) {
+ if (!audioManager.isConnected() && !audioManager.isAttemptingToConnect()) {
+ for (VoiceChannel voiceChannel : audioManager.getGuild().getVoiceChannels()) {
+ audioManager.openAudioConnection(voiceChannel);
+ break;
+ }
+ }
+ }
+ private static String getTimestamp(long milliseconds)
+ {
+ int seconds = (int) (milliseconds / 1000) % 60 ;
+ int minutes = (int) ((milliseconds / (1000 * 60)) % 60);
+ int hours = (int) ((milliseconds / (1000 * 60 * 60)) % 24);
+
+ if (hours > 0)
+ return String.format("%02d:%02d:%02d", hours, minutes, seconds);
+ else
+ return String.format("%02d:%02d", minutes, seconds);
+ }
+
+ public String returnTopVideoURL(String keyword) throws IOException {
+ String url = "https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q="+keyword+"&type=video&key="+ytapiKey;
+ url = url.replaceAll(" ", "%20");
+ String data = Jsoup.connect(url).ignoreContentType(true).execute().body();
+ JSONObject obj = new JSONObject(data);
+ JSONArray arr = obj.getJSONArray("items");
+ String videoID = "";
+ for (int i = 0; i < arr.length(); i++)
+ {
+ videoID = arr.getJSONObject(i).getJSONObject("id").getString("videoId");
+ System.out.println("Parsed ID "+ videoID);
+ }
+ return "https://www.youtube.com/watch?v="+videoID;
+ }
+
+
+
+
+
+}
diff --git a/audio/TrackScheduler.java b/audio/TrackScheduler.java new file mode 100644 index 0000000..8377e7a --- /dev/null +++ b/audio/TrackScheduler.java @@ -0,0 +1,59 @@ +package 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;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * This class schedules tracks for the audio player. It contains the queue of tracks.
+ */
+public class TrackScheduler extends AudioEventAdapter {
+ public final AudioPlayer player;
+ public BlockingQueue<AudioTrack> queue;
+
+ /**
+ * @param player The audio player this scheduler uses
+ */
+ public TrackScheduler(AudioPlayer player) {
+ this.player = player;
+ this.queue = new LinkedBlockingQueue<>();
+ }
+
+ /**
+ * Add the next track to queue or play right away if nothing is in the queue.
+ *
+ * @param track The track to play or add to queue.
+ */
+ public void queue(AudioTrack track) {
+ if (!player.startTrack(track, true)) {
+ queue.offer(track);
+ }
+ }
+
+ /**
+ * Start the next track, stopping the current one if it is playing.
+ */
+ public void nextTrack() {
+
+ player.startTrack(queue.poll(), false);
+
+ }
+
+ @Override
+ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
+ // Only start the next track if the end reason is suitable for it (FINISHED or LOAD_FAILED)
+ if (endReason.mayStartNext) {
+ nextTrack();
+ }
+ }
+ public void shuffle()
+ {
+ Collections.shuffle((List<?>) queue);
+ }
+}
\ No newline at end of file diff --git a/commands/CommandManager.java b/commands/CommandManager.java new file mode 100644 index 0000000..615f52c --- /dev/null +++ b/commands/CommandManager.java @@ -0,0 +1,82 @@ +package commands;
+
+import audio.Music;
+import net.dv8tion.jda.api.entities.TextChannel;
+import net.dv8tion.jda.api.events.guild.GuildReadyEvent;
+import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
+import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu;
+import utility.URLChecker;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class CommandManager extends ListenerAdapter {
+ Music music;
+ public CommandManager(Music music){
+ this.music = music;
+ }
+ @Override
+ public void onSlashCommand(SlashCommandEvent event) {
+ String command = event.getName(); //test
+ if (command.equals("play")) {
+ music.playMusic(event);
+ }
+ else if(command.equals("leave")){
+ event.getGuild().getAudioManager().setSendingHandler(null);
+ event.getGuild().getAudioManager().closeAudioConnection();
+ event.reply("OtsuRose! See you later!").queue();
+ }
+ else if(command.equals("vtmusic")){
+ event.deferReply().queue();
+ music.queueVTMusic((TextChannel) event.getChannel(),Integer.parseInt(event.getOption("number").getAsString()));
+ event.getHook().sendMessage("Queued up " + Integer.parseInt(event.getOption("number").getAsString())+" songs!").queue();
+ }
+ else if(command.equals("showqueue")){
+ music.showQueue((TextChannel) event.getChannel(), event);
+ }
+ else if(command.equals("skip")){
+ music.skipTrack((TextChannel) event.getChannel(),event);
+
+ }
+ else if(command.equals("pause")){
+ music.pausePlayer((TextChannel) event.getChannel(),event);
+
+ }
+ else if(command.equals("nowplaying")){
+ music.showNowPlaying(event);
+
+ }
+ else if(command.equals("stop")){
+ music.stopPlayer(event);
+
+ }
+ else if(command.equals("volume")){
+ music.setVolume(event,event.getOption("volume").getAsString());
+ }
+
+ else if(command.equals("remove")){
+ music.showQueueMenu((TextChannel) event.getChannel(), event,"remove-queue","Select a track to remove below");
+
+ }
+ else if(command.equals("inspect")){
+ music.showQueueMenu((TextChannel) event.getChannel(), event,"inspect-queue","Select a track to inspect below");
+
+ }
+ super.onSlashCommand(event);
+ }
+ @Override
+ public void onGuildReady(GuildReadyEvent event){
+ List<CommandData> commandData = new ArrayList<>();
+ }
+
+
+}
diff --git a/utility/SpotifyAPI.java b/utility/SpotifyAPI.java new file mode 100644 index 0000000..78738d8 --- /dev/null +++ b/utility/SpotifyAPI.java @@ -0,0 +1,111 @@ +package utility;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import se.michaelthelin.spotify.SpotifyApi;
+import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials;
+import se.michaelthelin.spotify.model_objects.specification.ArtistSimplified;
+import se.michaelthelin.spotify.model_objects.specification.Playlist;
+import se.michaelthelin.spotify.model_objects.specification.PlaylistTrack;
+import se.michaelthelin.spotify.model_objects.specification.Track;
+import se.michaelthelin.spotify.requests.authorization.client_credentials.ClientCredentialsRequest;
+import se.michaelthelin.spotify.requests.data.playlists.GetPlaylistRequest;
+import se.michaelthelin.spotify.requests.data.tracks.GetTrackRequest;
+import java.io.FileReader;
+import java.time.Instant;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+public class SpotifyAPI {
+ private static final String clientId = readSetting("spotifyClientID");
+ private static final String clientSecret = readSetting("spotifyClientSecret");
+
+ public static String spotifyapiKey = "";
+ public static long lastRefresh = 0;
+ private static final SpotifyApi spotifyApi = new SpotifyApi.Builder()
+ .setClientId(clientId)
+ .setClientSecret(clientSecret)
+ .build();
+ private static final ClientCredentialsRequest clientCredentialsRequest = spotifyApi.clientCredentials()
+ .build();
+
+
+ public SpotifyAPI(){
+ this.spotifyapiKey = readSetting("spotifyApi");
+ }
+ public static String getSearchTerm_sync(String trackid) {
+ checkRefreshToken();
+ String searchQuery = "";
+ try {
+ GetTrackRequest getTrackRequest = spotifyApi.getTrack(trackid)
+// .market(CountryCode.SE)
+ .build();
+ final Track track = getTrackRequest.execute();
+ searchQuery = track.getName();
+ ArtistSimplified[] artists = track.getArtists();
+ for (int i = 0;i< artists.length;i++){
+ searchQuery = searchQuery + " "+artists[i].getName();
+ }
+ System.out.println(searchQuery);
+ } catch (Exception e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ return searchQuery;
+
+ }
+ public static String getRandomPlaylistTrack_Sync(String playlistId) {
+ checkRefreshToken();
+ GetPlaylistRequest getPlaylistRequest = spotifyApi.getPlaylist(playlistId).build();
+ try {
+ Playlist playlist = getPlaylistRequest.execute();
+ PlaylistTrack[] tracks = playlist.getTracks().getItems();
+ //pick a random track and return it
+ int randomTrack = (int) (Math.random() * tracks.length);
+ System.out.println(tracks[randomTrack].getTrack().getId());
+ return tracks[randomTrack].getTrack().getId();
+ } catch (Exception e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ return "";
+ }
+ public static PlaylistTrack[] getPlaylist_Sync(String playlistId) {
+ checkRefreshToken();
+ GetPlaylistRequest getPlaylistRequest = spotifyApi.getPlaylist(playlistId).build();
+ try {
+ Playlist playlist = getPlaylistRequest.execute();
+ PlaylistTrack[] tracks = playlist.getTracks().getItems();
+ return tracks;
+ } catch (Exception e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ return null;
+ }
+ public static void clientCredentials_Sync() {
+ try {
+ final ClientCredentials clientCredentials = clientCredentialsRequest.execute();
+ spotifyApi.setAccessToken(clientCredentials.getAccessToken());
+ System.out.println("Expires in: " + clientCredentials.getExpiresIn());
+ } catch (Exception e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ }
+ public static String readSetting(String parameter){
+ Object obj = null;
+ try {
+ obj = new JSONParser().parse(new FileReader("settings//config.json"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ JSONObject jo = (JSONObject) obj;
+ return (String) jo.get(parameter);
+ }
+ public static void checkRefreshToken(){
+ long unixTime = Instant.now().getEpochSecond();
+ if(lastRefresh + 3600 <= unixTime){
+ System.out.println("Spotify Token is expired, refreshing");
+ clientCredentials_Sync();
+ lastRefresh = Instant.now().getEpochSecond();
+ }
+ }
+
+}
diff --git a/utility/URLChecker.java b/utility/URLChecker.java new file mode 100644 index 0000000..ffea53b --- /dev/null +++ b/utility/URLChecker.java @@ -0,0 +1,31 @@ +package utility;
+
+public class URLChecker {
+ public boolean isURL(String term){
+ return term.matches("^(http|https)://.*");
+ }
+ public String getURLType(String url) {
+ if (url.matches("^((?:https?:)?\\/\\/)?((?:www|m)\\.)?((?:youtube(-nocookie)?\\.com|youtu.be))(\\/(?:[\\w\\-]+\\?v=|embed\\/|v\\/)?)([\\w\\-]+)(\\S+)?$")) {
+ return "yt"; //Youtube
+ } else if (url.matches("^(https?:\\/\\/)?(www.)?(m\\.)?soundcloud\\.com\\/[\\w\\-\\.]+(\\/)+[\\w\\-\\.]+/?$")) {
+ return "snd";
+ } else if (url.matches("^(?:https?:\\/\\/)?(?:www\\.|go\\.)?twitch\\.tv\\/([a-z0-9_]+)($|\\?)")) {
+ return "twitch";
+ } else if (url.split("\\?si=")[0].matches("^(https?://)?(www.)?(open.)?spotify.com/(user/[a-zA-Z0-9]+|artist/[a-zA-Z0-9]+|album/[a-zA-Z0-9]+|track/[a-zA-Z0-9]+|playlist/[a-zA-Z0-9]+)$")) {
+ return url.split("\\?si=")[0].matches("^(https?://)?(www.)?(open.)?spotify.com/playlist/[a-zA-Z0-9]+$") ? "spotify-playlist" : "spotify";
+ }
+ return "unknown";
+
+ }
+
+ public String getSpotifyTrackID(String uri){
+ String[] uriParts = uri.split("\\?si=");
+ return uriParts[0].replaceAll("https://open.spotify.com/track/","");
+
+ }
+ public String getSpotifyPlaylistID(String url){
+ String[] uriParts = url.split("\\?si=");
+ return uriParts[0].replaceAll("https://open.spotify.com/playlist/","");
+
+ }
+}
|
