From 4749216320da6d8adac202f468c759db18a8b735 Mon Sep 17 00:00:00 2001 From: "Yukai (Donald) Shan" Date: Wed, 7 Dec 2022 10:03:23 -0800 Subject: Add files via upload --- Main.java | 86 +++++++ audio/AudioPlayerSendHandler.java | 38 +++ audio/GuildMusicManager.java | 18 ++ audio/Music.java | 507 ++++++++++++++++++++++++++++++++++++++ audio/TrackScheduler.java | 59 +++++ commands/CommandManager.java | 82 ++++++ utility/SpotifyAPI.java | 111 +++++++++ utility/URLChecker.java | 31 +++ 8 files changed, 932 insertions(+) create mode 100644 Main.java create mode 100644 audio/AudioPlayerSendHandler.java create mode 100644 audio/GuildMusicManager.java create mode 100644 audio/Music.java create mode 100644 audio/TrackScheduler.java create mode 100644 commands/CommandManager.java create mode 100644 utility/SpotifyAPI.java create mode 100644 utility/URLChecker.java 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 hololiveMusicURL = new ArrayList(); + String ytapiKey = ""; + String spotifyapiKey = ""; + static String append = "!"; + private URLChecker urlCheck = new URLChecker(); + private final AudioPlayerManager playerManager; + private final Map 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 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 queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue; + List trackMenuOptions = new ArrayList(); + 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 queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue; + BlockingQueue 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 queue = getGuildAudioPlayer(event.getGuild()).scheduler.queue; + BlockingQueue 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 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 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 = 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/",""); + + } +} -- cgit v1.2.3