Compare commits
No commits in common. 'master' and 'main' have entirely different histories.
19 changed files with 853 additions and 133 deletions
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
# MusicBot for Discord |
||||
|
||||
This is a basic Discord bot that will use the Youtube APi to find videos based on a song title and artist name. |
||||
|
||||
Note: You need to create a Token class with your own Discord Token, Youtube API Key and Spotify API client and secret. |
||||
|
||||
Sample: |
||||
|
||||
```java |
||||
|
||||
public class Token { |
||||
public static final String TOKEN = "discordToken"; |
||||
public static final String YOUTUBE_API_KEY = "YoutubeApiKey"; |
||||
} |
||||
|
||||
``` |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package musicbot; |
||||
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
||||
|
||||
/** |
||||
* Interface to build a button interaction to manage a button event |
||||
*/ |
||||
public interface ButtonInteraction { |
||||
/** |
||||
* A unique name that identifies the button interaction |
||||
* @return String the name of the button |
||||
*/ |
||||
String getName(); |
||||
|
||||
/** |
||||
* Executes the interaction logic of the button |
||||
* @param event the ButtonInteractionEvent |
||||
*/ |
||||
void execute(ButtonInteractionEvent event); |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
package musicbot; |
||||
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Extension of the Listener Adaptor that manages all the interactions. Because |
||||
* JDA doesn't have a class to specifically build button events, this is a custom way |
||||
* of doing so. |
||||
*/ |
||||
public class ButtonInteractionManager extends ListenerAdapter { |
||||
private final List<ButtonInteraction> buttonInteractions = new ArrayList<>(); |
||||
|
||||
/** |
||||
* Listener for the button interactions. |
||||
* @param event the {@code ButtonInteractionEvent} |
||||
*/ |
||||
@Override |
||||
public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { |
||||
final String id = event.getComponent().getId(); |
||||
|
||||
final String[] args = id.split("_", 0); |
||||
|
||||
for (final ButtonInteraction buttonInteraction : buttonInteractions) { |
||||
if (args[0].equals(buttonInteraction.getName())) { |
||||
buttonInteraction.execute(event); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Add a ButtonInteraction to the ButtonInteractionManager |
||||
* @param buttonInteraction the ButtonInteractiont to be added |
||||
*/ |
||||
public void add(@NotNull final ButtonInteraction buttonInteraction) { |
||||
buttonInteractions.add(buttonInteraction); |
||||
} |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
package musicbot; |
||||
|
||||
import net.dv8tion.jda.api.entities.Guild; |
||||
import net.dv8tion.jda.api.events.ReadyEvent; |
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; |
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Extension of the Listener Adaptor that manages all the commands. |
||||
*/ |
||||
public class CommandManager extends ListenerAdapter { |
||||
private final List<MCommand> commands = new ArrayList<>(); |
||||
|
||||
@Override |
||||
public void onReady(final ReadyEvent event) { |
||||
for (final Guild guild : event.getJDA().getGuilds()) { |
||||
for (MCommand command : commands) { |
||||
if (command.getOptions() == null) { |
||||
guild.upsertCommand(command.getName(), command.getDescription()).queue(); |
||||
} else { |
||||
guild.upsertCommand(command.getName(), command.getDescription()).addOptions(command.getOptions()).queue(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onSlashCommandInteraction(final @NotNull SlashCommandInteractionEvent event) { |
||||
for(final MCommand command : commands) { |
||||
if(command.getName().equals(event.getName())) { |
||||
command.execute(event); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void add(final MCommand command) { |
||||
commands.add(command); |
||||
} |
||||
} |
||||
@ -1,29 +0,0 @@
@@ -1,29 +0,0 @@
|
||||
package musicbot; |
||||
|
||||
import net.dv8tion.jda.api.entities.Guild; |
||||
import net.dv8tion.jda.api.events.ReadyEvent; |
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter; |
||||
import net.dv8tion.jda.api.interactions.commands.OptionType; |
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class Listeners extends ListenerAdapter { |
||||
|
||||
@Override |
||||
public void onReady(@NotNull final ReadyEvent event) { |
||||
event.getJDA().upsertCommand("music", "find music to play").addOptions( |
||||
new OptionData( |
||||
OptionType.STRING, |
||||
"title", |
||||
"The name of the song", |
||||
true), |
||||
new OptionData( |
||||
OptionType.STRING, |
||||
"artist", |
||||
"The artist of the song", |
||||
true) |
||||
).queue(); |
||||
} |
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
package musicbot; |
||||
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; |
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Interface to build a command interaction to manage a button event. |
||||
* This class is prefaced with an M (for MusicBot) as to not confuse with |
||||
* JDA's Command class. |
||||
*/ |
||||
public interface MCommand { |
||||
String getName(); |
||||
|
||||
String getDescription(); |
||||
|
||||
List<OptionData> getOptions(); |
||||
|
||||
void execute(SlashCommandInteractionEvent event); |
||||
} |
||||
@ -1,15 +1,35 @@
@@ -1,15 +1,35 @@
|
||||
package musicbot; |
||||
|
||||
import musicbot.buttonInteractions.MoreGenre; |
||||
import musicbot.buttonInteractions.MoreRecommendation; |
||||
import musicbot.buttonInteractions.VideoRecommendation; |
||||
import musicbot.commands.Artist; |
||||
import musicbot.commands.Genres; |
||||
import musicbot.commands.MusicVideo; |
||||
import musicbot.commands.Recommend; |
||||
import net.dv8tion.jda.api.JDA; |
||||
import net.dv8tion.jda.api.JDABuilder; |
||||
|
||||
public class Main { |
||||
public static void main(final String[] args) { |
||||
final JDA jda = JDABuilder |
||||
.createDefault(Token.TOKEN) |
||||
.createDefault(Token.DEV_TOKEN) |
||||
.build(); |
||||
jda.addEventListener(new Listeners()); |
||||
jda.addEventListener(new MusicVideo()); |
||||
final CommandManager commandManager = new CommandManager(); |
||||
final ButtonInteractionManager buttonInteractionManager = new ButtonInteractionManager(); |
||||
|
||||
// commands
|
||||
commandManager.add(new Artist()); |
||||
commandManager.add(new Genres()); |
||||
commandManager.add(new MusicVideo()); |
||||
commandManager.add(new Recommend()); |
||||
|
||||
// button interactions
|
||||
buttonInteractionManager.add(new MoreRecommendation()); |
||||
buttonInteractionManager.add(new MoreGenre()); |
||||
buttonInteractionManager.add(new VideoRecommendation()); |
||||
|
||||
// add the managers
|
||||
jda.addEventListener(commandManager); |
||||
jda.addEventListener(buttonInteractionManager); |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
package musicbot; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class Paginator { |
||||
|
||||
public static class PaginatedItems<T> { |
||||
private List<T> list = new ArrayList<T>(); |
||||
private static boolean isLastPage = true; |
||||
private static int nextPageNum; |
||||
|
||||
public void set(List<T> items){ |
||||
list = items; |
||||
} |
||||
|
||||
public void setNextPageNum(final int nextPage) { |
||||
nextPageNum = nextPage; |
||||
} |
||||
|
||||
public int getNextPageNum() { |
||||
return nextPageNum; |
||||
} |
||||
|
||||
public void setIsLastPage(final boolean isLastPageBool) { |
||||
isLastPage = isLastPageBool; |
||||
} |
||||
|
||||
public boolean isIsLastPage() { |
||||
return isLastPage; |
||||
} |
||||
|
||||
public List<T> get() { |
||||
return list; |
||||
} |
||||
} |
||||
|
||||
public <E> PaginatedItems<E> paginate( |
||||
final List<E> tracks, |
||||
final int numItemsPerPage, |
||||
final int pageNumber |
||||
) { |
||||
if (tracks.isEmpty()) { |
||||
return new PaginatedItems<>(); |
||||
} |
||||
|
||||
final int maxSize = tracks.size(); |
||||
final int maxPage = Math.round(maxSize / ((float) numItemsPerPage)); |
||||
final int maxIndex = numItemsPerPage * pageNumber; |
||||
final int baseIndex = maxIndex - numItemsPerPage; |
||||
final int nextPage = pageNumber + 1; |
||||
final PaginatedItems<E> paginatedItems = new PaginatedItems<>(); |
||||
|
||||
if (nextPage <= maxPage) { |
||||
paginatedItems.set(tracks.subList(baseIndex, maxIndex)); |
||||
paginatedItems.setIsLastPage(false); |
||||
paginatedItems.setNextPageNum(nextPage); |
||||
} else { |
||||
paginatedItems.set(tracks.subList(maxIndex, maxSize)); |
||||
paginatedItems.setIsLastPage(true); |
||||
} |
||||
|
||||
return paginatedItems; |
||||
} |
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
package musicbot; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
import se.michaelthelin.spotify.model_objects.specification.Artist; |
||||
import se.michaelthelin.spotify.model_objects.specification.Track; |
||||
|
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
public class RecommendationService { |
||||
|
||||
public List<Track> getRecommendations(final String artist, final String genre) { |
||||
|
||||
if (artist.isEmpty() && genre.isEmpty()) { |
||||
return ImmutableList.of(); |
||||
} |
||||
final SpotifyClient spotifyClient = new SpotifyClient(); |
||||
final Optional<String> maybeArtistId = findArtistId(artist); |
||||
final String artistId = maybeArtistId.orElse(""); |
||||
final List<Track> tracks = spotifyClient.findRecommendations(artistId, genre); |
||||
|
||||
return tracks; |
||||
} |
||||
|
||||
private Optional<String> findArtistId(final String artist) { |
||||
final SpotifyClient spotifyClient = new SpotifyClient(); |
||||
final Optional<Artist> maybeArtist = spotifyClient.findArtist(artist); |
||||
|
||||
return maybeArtist.map(Artist::getId); |
||||
} |
||||
} |
||||
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
package musicbot; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
import org.apache.hc.core5.http.ParseException; |
||||
import se.michaelthelin.spotify.SpotifyApi; |
||||
import se.michaelthelin.spotify.exceptions.SpotifyWebApiException; |
||||
import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials; |
||||
import se.michaelthelin.spotify.model_objects.specification.Artist; |
||||
import se.michaelthelin.spotify.model_objects.specification.Paging; |
||||
import se.michaelthelin.spotify.model_objects.specification.Recommendations; |
||||
import se.michaelthelin.spotify.model_objects.specification.Track; |
||||
import se.michaelthelin.spotify.requests.authorization.client_credentials.ClientCredentialsRequest; |
||||
import se.michaelthelin.spotify.requests.data.browse.GetRecommendationsRequest; |
||||
import se.michaelthelin.spotify.requests.data.search.simplified.SearchArtistsRequest; |
||||
import se.michaelthelin.spotify.requests.data.browse.miscellaneous.GetAvailableGenreSeedsRequest; |
||||
import se.michaelthelin.spotify.requests.data.search.simplified.SearchTracksRequest; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
public class SpotifyClient { |
||||
private static final SpotifyApi SPOTIFY_API = new SpotifyApi.Builder() |
||||
.setClientId(Token.SPOTIFY_CLIENT_ID) |
||||
.setClientSecret(Token.SPOTIFY_CLIENT_SECRET) |
||||
.build(); |
||||
|
||||
private static final ClientCredentialsRequest CLIENT_CREDENTIALS_REQUEST = SPOTIFY_API |
||||
.clientCredentials() |
||||
.build(); |
||||
|
||||
public SpotifyClient() { |
||||
clientCredentialsSync(); |
||||
} |
||||
|
||||
public Optional<Artist> findArtist(final String artist) { |
||||
try { |
||||
final SearchArtistsRequest searchArtistsRequest = SPOTIFY_API.searchArtists(artist) |
||||
.limit(1) |
||||
.build(); |
||||
|
||||
final Paging<Artist> artists = searchArtistsRequest.execute(); |
||||
|
||||
return Arrays.stream(artists.getItems()).findFirst(); |
||||
} catch (SpotifyWebApiException | ParseException | IOException exception) { |
||||
return Optional.empty(); |
||||
} |
||||
} |
||||
|
||||
public List<Track> findTracks(final String query) { |
||||
try { |
||||
final SearchTracksRequest searchTracksRequest = SPOTIFY_API.searchTracks(query).build(); |
||||
|
||||
final Paging<Track> tracks = searchTracksRequest.execute(); |
||||
|
||||
return Arrays.stream(tracks.getItems()).collect(ImmutableList.toImmutableList()); |
||||
} catch (SpotifyWebApiException | ParseException | IOException exception) { |
||||
return List.of(); |
||||
} |
||||
} |
||||
|
||||
public List<Track> findRecommendations(final String artistId, final String genres) { |
||||
final GetRecommendationsRequest.Builder recommendationsRequestBuilder = SPOTIFY_API.getRecommendations(); |
||||
|
||||
if (!artistId.isEmpty()) { |
||||
recommendationsRequestBuilder |
||||
.seed_artists(artistId); |
||||
} |
||||
|
||||
if (!genres.isEmpty()) { |
||||
recommendationsRequestBuilder |
||||
.seed_genres(genres); |
||||
} |
||||
|
||||
final GetRecommendationsRequest recommendationsRequest = recommendationsRequestBuilder.build(); |
||||
|
||||
try { |
||||
final Recommendations recommendations = recommendationsRequest.execute(); |
||||
|
||||
return Arrays.stream(recommendations.getTracks()).collect(ImmutableList.toImmutableList()); |
||||
} catch (IOException | SpotifyWebApiException | ParseException e) { |
||||
System.out.println("Error: " + e.getMessage()); |
||||
return ImmutableList.of(); |
||||
} |
||||
} |
||||
|
||||
public List<String> findGenres() { |
||||
final GetAvailableGenreSeedsRequest request = SPOTIFY_API.getAvailableGenreSeeds().build(); |
||||
|
||||
try { |
||||
final String[] genres = request.execute(); |
||||
return Arrays.stream(genres).collect(ImmutableList.toImmutableList()); |
||||
} catch (IOException | SpotifyWebApiException | ParseException e) { |
||||
System.out.println("Error: " + e.getMessage()); |
||||
return ImmutableList.of(); |
||||
} |
||||
} |
||||
|
||||
private static void clientCredentialsSync() { |
||||
try { |
||||
final ClientCredentials clientCredentials = CLIENT_CREDENTIALS_REQUEST.execute(); |
||||
|
||||
// Set access token for further "spotifyApi" object usage
|
||||
SPOTIFY_API.setAccessToken(clientCredentials.getAccessToken()); |
||||
|
||||
System.out.println("Expires in: " + clientCredentials.getExpiresIn()); |
||||
} catch (final IOException | SpotifyWebApiException | ParseException e) { |
||||
System.out.println("Error: " + e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
package musicbot.buttonInteractions; |
||||
|
||||
import musicbot.ButtonInteraction; |
||||
import musicbot.Paginator; |
||||
import musicbot.SpotifyClient; |
||||
import net.dv8tion.jda.api.EmbedBuilder; |
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class MoreGenre implements ButtonInteraction { |
||||
@Override |
||||
public String getName() { |
||||
return "genre"; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(ButtonInteractionEvent event) { |
||||
final String id = event.getComponent().getId(); |
||||
final Paginator paginator = new Paginator(); |
||||
|
||||
final String[] args = id.split("_", 0); |
||||
event.deferReply().queue(); |
||||
|
||||
final int page = Integer.parseInt(args[1]); |
||||
|
||||
final SpotifyClient spotifyClient = new SpotifyClient(); |
||||
final List<String> genres = spotifyClient.findGenres(); |
||||
final Paginator.PaginatedItems<String> paginatedGenre = paginator.paginate(genres, 10, page); |
||||
|
||||
final EmbedBuilder eb = new EmbedBuilder(); |
||||
|
||||
paginatedGenre.get().forEach(genreItem -> eb.addField(genreItem, "", false)); |
||||
if (!paginatedGenre.isIsLastPage()) { |
||||
event.getHook().sendMessageEmbeds(eb.build()).addActionRow( |
||||
Button.primary("genre_" + paginatedGenre.getNextPageNum(), "More genres") |
||||
).queue(); |
||||
} else { |
||||
event.getHook().sendMessageEmbeds(eb.build()).queue(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
package musicbot.buttonInteractions; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
import musicbot.ButtonInteraction; |
||||
import musicbot.Paginator; |
||||
import musicbot.RecommendationService; |
||||
import net.dv8tion.jda.api.EmbedBuilder; |
||||
import net.dv8tion.jda.api.entities.MessageEmbed; |
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button; |
||||
import se.michaelthelin.spotify.model_objects.specification.ArtistSimplified; |
||||
import se.michaelthelin.spotify.model_objects.specification.Track; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* This interaction happens when a user requests more recommendations |
||||
* after initial /recommend. |
||||
*/ |
||||
public class MoreRecommendation implements ButtonInteraction { |
||||
private static final String ARGS_DELIMITER = "_"; |
||||
private static final String ARTIST_GENRE_DELIMITER = "--"; |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return "more-recommend"; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(final ButtonInteractionEvent event) { |
||||
event.deferReply().queue(); |
||||
final String id = event.getComponent().getId(); |
||||
final String[] args = id.split(ARGS_DELIMITER, 0); |
||||
|
||||
final String[] artistGenre = args[1].split(ARTIST_GENRE_DELIMITER, 0); |
||||
final String artist = artistGenre[0]; |
||||
final String genre = artistGenre.length < 2 ? "" : artistGenre[1]; |
||||
|
||||
final int pageNumber = Integer.parseInt(args[2]); |
||||
final List<Track> tracks = new RecommendationService().getRecommendations(artist, genre); |
||||
|
||||
final Paginator paginator = new Paginator(); |
||||
final Paginator.PaginatedItems<Track> paginatedTracks = paginator.paginate(tracks, 5, pageNumber); |
||||
|
||||
final EmbedBuilder eb = new EmbedBuilder(); |
||||
final List<Track> trackList = paginatedTracks.get(); |
||||
if (!trackList.isEmpty()) { |
||||
eb |
||||
.setTitle("More recommendations based on \"" + artist + "\"") |
||||
.setDescription("Here are more recommendations based on your input."); |
||||
|
||||
trackList.forEach(track -> { |
||||
final List<String> trackArtists = Arrays.stream(track.getArtists()) |
||||
.map(ArtistSimplified::getName) |
||||
.collect(ImmutableList.toImmutableList()); |
||||
final String artists = String.join(", ", trackArtists); |
||||
|
||||
eb.addField(track.getName(), artists, false); |
||||
}); |
||||
|
||||
final MessageEmbed embed = eb.build(); |
||||
|
||||
final List<Button> buttons = trackList.stream().map(track -> { |
||||
final String artists = Arrays.stream(track.getArtists()).map(ArtistSimplified::getName) |
||||
.collect(Collectors.joining(", ")); |
||||
|
||||
return Button.primary("recommend_" + artists, track.getName()); |
||||
}).collect(ImmutableList.toImmutableList()); |
||||
|
||||
if (!paginatedTracks.isIsLastPage()) { |
||||
final String buttonId = "more-recommend_" + artist + "--" + genre + "_" + paginatedTracks.getNextPageNum(); |
||||
|
||||
event |
||||
.getHook() |
||||
.sendMessageEmbeds(embed) |
||||
.addActionRow(buttons) |
||||
.addActionRow(Button.secondary(buttonId, "Get more recommendations")) |
||||
.queue(); |
||||
} else { |
||||
event |
||||
.getHook() |
||||
.sendMessageEmbeds(embed) |
||||
.addActionRow(buttons) |
||||
.queue(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
package musicbot.buttonInteractions; |
||||
|
||||
import musicbot.ButtonInteraction; |
||||
import musicbot.YoutubeSearch; |
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
||||
|
||||
public class VideoRecommendation implements ButtonInteraction { |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return "recommend"; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(ButtonInteractionEvent event) { |
||||
final String id = event.getComponent().getId(); |
||||
final String label = event.getComponent().getLabel(); |
||||
final String[] args = id.split("_", 0); |
||||
|
||||
event.deferReply().queue(); |
||||
|
||||
final YoutubeSearch youtubeSearch = new YoutubeSearch(); |
||||
final String result = youtubeSearch.searchForMusic(label, args[1]); |
||||
|
||||
event.getHook().sendMessage(result).queue(); |
||||
} |
||||
} |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
package musicbot.commands; |
||||
|
||||
import musicbot.MCommand; |
||||
import musicbot.SpotifyClient; |
||||
import net.dv8tion.jda.api.EmbedBuilder; |
||||
import net.dv8tion.jda.api.entities.MessageEmbed; |
||||
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 se.michaelthelin.spotify.model_objects.specification.Track; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class Artist implements MCommand { |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return "artist"; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "Find songs by artist"; |
||||
} |
||||
|
||||
@Override |
||||
public List<OptionData> getOptions() { |
||||
final List<OptionData> options= new ArrayList<>(); |
||||
options.add(new OptionData( |
||||
OptionType.STRING, |
||||
"name", |
||||
"Find tracks by an artist.", |
||||
true |
||||
)); |
||||
|
||||
return options; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(SlashCommandInteractionEvent event) { |
||||
event.deferReply().queue(); |
||||
|
||||
final String artist = event.getOption("name").getAsString(); |
||||
|
||||
final SpotifyClient spotifyClient = new SpotifyClient(); |
||||
final List<Track> tracks = spotifyClient.findTracks("artist:" + artist); |
||||
|
||||
final EmbedBuilder eb = new EmbedBuilder(); |
||||
eb |
||||
.setTitle("Some tracks by " + artist); |
||||
|
||||
tracks.forEach(track -> |
||||
eb.addField(track.getName(), "", false) |
||||
); |
||||
|
||||
final MessageEmbed embed = eb.build(); |
||||
|
||||
event.getHook().sendMessageEmbeds(embed).queue(); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
package musicbot.commands; |
||||
|
||||
import musicbot.MCommand; |
||||
import musicbot.Paginator; |
||||
import musicbot.SpotifyClient; |
||||
import net.dv8tion.jda.api.EmbedBuilder; |
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; |
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData; |
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class Genres implements MCommand { |
||||
@Override |
||||
public String getName() { |
||||
return "genres"; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "find support genres"; |
||||
} |
||||
|
||||
@Override |
||||
public List<OptionData> getOptions() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(SlashCommandInteractionEvent event) { |
||||
event.deferReply().queue(); |
||||
final SpotifyClient spotifyClient = new SpotifyClient(); |
||||
final List<String> genres = spotifyClient.findGenres(); |
||||
final Paginator paginator = new Paginator(); |
||||
|
||||
final EmbedBuilder eb = new EmbedBuilder(); |
||||
final Paginator.PaginatedItems<String> paginatedGenre = paginator.paginate(genres, 10, 1); |
||||
|
||||
paginatedGenre.get().forEach(genre -> eb.addField(genre, "", false)); |
||||
|
||||
event.getHook().sendMessageEmbeds(eb.build()).addActionRow( |
||||
Button.primary("genre_" + paginatedGenre.getNextPageNum(), "More genres") |
||||
).queue(); |
||||
} |
||||
} |
||||
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
package musicbot.commands; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
import musicbot.MCommand; |
||||
import musicbot.Paginator; |
||||
import musicbot.RecommendationService; |
||||
import net.dv8tion.jda.api.EmbedBuilder; |
||||
import net.dv8tion.jda.api.entities.MessageEmbed; |
||||
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.interactions.components.buttons.Button; |
||||
import se.michaelthelin.spotify.model_objects.specification.ArtistSimplified; |
||||
import se.michaelthelin.spotify.model_objects.specification.Track; |
||||
|
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
|
||||
public class Recommend implements MCommand { |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return "recommend"; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "find recommendations for music"; |
||||
} |
||||
|
||||
@Override |
||||
public List<OptionData> getOptions() { |
||||
final List<OptionData> options = new ArrayList<>(); |
||||
options.add(new OptionData( |
||||
OptionType.STRING, |
||||
"artist", |
||||
"Find other songs and artists based on an artist you like" |
||||
)); |
||||
options.add(new OptionData( |
||||
OptionType.STRING, |
||||
"genre", |
||||
"Find other songs and artists based on a genre you like" |
||||
)); |
||||
return options; |
||||
} |
||||
|
||||
@Override |
||||
public void execute(SlashCommandInteractionEvent event) { |
||||
event.deferReply().queue(); |
||||
|
||||
final String artistInput = event.getOption("artist") != null ? event.getOption("artist").getAsString() : ""; |
||||
final String genreInput = event.getOption("genre") != null ? event.getOption("genre").getAsString() : ""; |
||||
final List<Track> tracks = new RecommendationService().getRecommendations(artistInput, genreInput); |
||||
final Paginator paginator = new Paginator(); |
||||
final Paginator.PaginatedItems<Track> paginatedTracks = paginator.paginate(tracks, 5, 1); |
||||
final EmbedBuilder eb = new EmbedBuilder(); |
||||
final List<Track> trackList = paginatedTracks.get(); |
||||
|
||||
if (!trackList.isEmpty()) { |
||||
eb |
||||
.setTitle("Recommendations based on \"" + artistInput + "\"") |
||||
.setDescription("Here are a list of recommendations based on your input. " + |
||||
"It will help you find new music to listen to.") |
||||
.setFooter("To find a song, select a button below."); |
||||
|
||||
trackList.forEach(track -> { |
||||
final List<String> trackArtists = Arrays.stream(track.getArtists()) |
||||
.map(ArtistSimplified::getName) |
||||
.collect(ImmutableList.toImmutableList()); |
||||
final String artists = String.join(", ", trackArtists); |
||||
|
||||
eb.addField(track.getName(), artists, false); |
||||
}); |
||||
|
||||
final MessageEmbed embed = eb.build(); |
||||
final List<Button> buttons = trackList.stream().map(track -> { |
||||
final String artists = Arrays.stream(track.getArtists()).map(ArtistSimplified::getName) |
||||
.collect(Collectors.joining(", ")); |
||||
|
||||
return Button.primary("recommend_" + artists, track.getName()); |
||||
}).collect(ImmutableList.toImmutableList()); |
||||
|
||||
final String buttonId = "more-recommend_" + artistInput + "--" + genreInput + "_" + paginatedTracks.getNextPageNum(); |
||||
|
||||
event |
||||
.getHook() |
||||
.sendMessageEmbeds(embed) |
||||
.addActionRow(buttons) |
||||
.addActionRow(Button.secondary(buttonId, "Get more recommendations")) |
||||
.queue(); |
||||
} else { |
||||
eb.setTitle("No recommendations found"); |
||||
final MessageEmbed embed = eb.build(); |
||||
|
||||
event.getHook().sendMessageEmbeds(embed).queue(); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue