diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java index d54bead..b79f091 100644 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java @@ -43,7 +43,7 @@ public class CurrentSubCommand extends BatSubCommand { } if (!spotifyService.hasTrackPlaying(user)) { - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.errorEmbed() .setDescription("You are not currently playing a track.") .build()) .queue(); @@ -55,14 +55,23 @@ public class CurrentSubCommand extends BatSubCommand { String trackUrl = "https://open.spotify.com/track/" + track.getId(); String albumUrl = "https://open.spotify.com/album/" + album.getId(); + StringBuilder artists = new StringBuilder(); + for (int i = 0; i < track.getArtists().length; i++) { + artists.append("**[%s](%s)**".formatted(track.getArtists()[i].getName(), "https://open.spotify.com/artist/" + track.getArtists()[i].getId())); + if (i != track.getArtists().length - 1) { + artists.append(", "); + } + } + String description = "➜ Song: **[%s](%s)**\n".formatted(track.getName(), trackUrl) + - "➜ Album: **[%s](%s)**\n".formatted(album.getName(), albumUrl) + - "➜ Position: %s\n".formatted(getFormattedTime(currentlyPlaying)); + "➜ Album: **[%s](%s)**\n".formatted(album.getName(), albumUrl) + + "➜ Artist%s: %s\n".formatted(track.getArtists().length > 1 ? "s" : "", artists) + + "➜ Position: %s\n".formatted(getFormattedTime(currentlyPlaying)); Image albumCover = album.getImages()[0]; interaction.replyEmbeds(EmbedUtils.genericEmbed() - .setAuthor("Listening to %s".formatted(track.getName()), trackUrl) + .setAuthor("Listening to %s | %s".formatted(track.getName(), track.getArtists()[0].getName()), trackUrl) .setThumbnail(albumCover.getUrl()) .setDescription(description) .build()).queue(); diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java index f41a9fb..95af15f 100644 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java @@ -4,6 +4,7 @@ import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.CommandInfo; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.event.EventListener; +import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.service.SpotifyService; @@ -37,6 +38,15 @@ public class LinkSubCommand extends BatSubCommand implements EventListener { @Override public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { + SpotifyProfile profile = user.getProfile(SpotifyProfile.class); + if (profile.hasLinkedAccount()) { + interaction.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("You have already linked your Spotify account!") + .build()) + .queue(); + return; + } + interaction.replyEmbeds(EmbedUtils.genericEmbed() .setDescription("You can link your Spotify account by clicking [here](%s)".formatted(spotifyService.getAuthorizationUrl())) .build()) diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java index 77ef74b..c60c015 100644 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java @@ -37,7 +37,7 @@ public class PauseSubCommand extends BatSubCommand { } if (!spotifyService.hasTrackPlaying(user)) { - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.errorEmbed() .setDescription("You need to be playing a track to pause it.") .build()) .queue(); @@ -45,7 +45,7 @@ public class PauseSubCommand extends BatSubCommand { } boolean didPause = spotifyService.pausePlayback(user); - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.successEmbed() .setDescription(didPause ? "Paused the current track." : "The current track is already paused.") .build()) .queue(); diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java index 492a407..f341d99 100644 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java @@ -37,15 +37,15 @@ public class ResumeSubCommand extends BatSubCommand { } if (!spotifyService.hasTrackPlaying(user)) { - interaction.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("You need to be playing a track to pause it.") + interaction.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("You need to be playing a track to resume it.") .build()) .queue(); return; } boolean didPause = spotifyService.resumePlayback(user); - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.successEmbed() .setDescription(didPause ? "Resumed the current track." : "The current track is already playing.") .build()) .queue(); diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java index c536331..ec902d2 100644 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java @@ -32,7 +32,7 @@ public class UnlinkSubCommand extends BatSubCommand implements EventListener { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { SpotifyProfile profile = user.getProfile(SpotifyProfile.class); if (!profile.hasLinkedAccount()) { - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.errorEmbed() .setDescription("You do not have a linked Spotify account.") .build()) .queue(); @@ -41,7 +41,7 @@ public class UnlinkSubCommand extends BatSubCommand implements EventListener { profile.reset(); userService.saveUser(user); - interaction.replyEmbeds(EmbedUtils.genericEmbed() + interaction.replyEmbeds(EmbedUtils.successEmbed() .setDescription("Successfully unlinked your Spotify account.") .build()) .queue(); diff --git a/src/main/java/cc/fascinated/bat/service/SpotifyService.java b/src/main/java/cc/fascinated/bat/service/SpotifyService.java index 6d5361e..b821210 100644 --- a/src/main/java/cc/fascinated/bat/service/SpotifyService.java +++ b/src/main/java/cc/fascinated/bat/service/SpotifyService.java @@ -6,9 +6,8 @@ import cc.fascinated.bat.model.BatUser; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; +import lombok.extern.log4j.Log4j2; import net.jodah.expiringmap.ExpiringMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import se.michaelthelin.spotify.SpotifyApi; @@ -25,8 +24,8 @@ import java.util.concurrent.TimeUnit; */ @Service @Getter +@Log4j2 public class SpotifyService { - private static final Logger log = LoggerFactory.getLogger(SpotifyService.class); /** * The access token map. */ @@ -108,12 +107,8 @@ public class SpotifyService { */ @SneakyThrows public boolean pausePlayback(BatUser user) { - try { - getSpotifyApi(user).pauseUsersPlayback().build().execute(); - return true; - } catch (Exception e) { - return false; - } + getSpotifyApi(user).pauseUsersPlayback().build().execute(); + return true; } /** @@ -124,12 +119,8 @@ public class SpotifyService { */ @SneakyThrows public boolean resumePlayback(BatUser user) { - try { - getSpotifyApi(user).startResumeUsersPlayback().build().execute(); - return true; - } catch (Exception e) { - return false; - } + getSpotifyApi(user).startResumeUsersPlayback().build().execute(); + return true; } /** @@ -154,7 +145,7 @@ public class SpotifyService { * Links the user's Spotify account with their Discord account. * * @param user the user to link the account with - * @param key the key to link the account with + * @param key the key to link the account with */ public void linkAccount(BatUser user, String key) { AuthorizationCodeCredentials credentials = accessToken.get(key); @@ -165,6 +156,7 @@ public class SpotifyService { SpotifyProfile profile = user.getProfile(SpotifyProfile.class); profile.setAccessToken(credentials.getAccessToken()); profile.setRefreshToken(credentials.getRefreshToken()); + profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000)); userService.saveUser(user); } @@ -176,24 +168,27 @@ public class SpotifyService { @SneakyThrows public SpotifyApi getSpotifyApi(BatUser user) { SpotifyProfile profile = user.getProfile(SpotifyProfile.class); - SpotifyApi api = new SpotifyApi.Builder() - .setClientId(clientId) - .setClientSecret(clientSecret) + ensureValidToken(profile, user); + return new SpotifyApi.Builder() .setAccessToken(profile.getAccessToken()) .setRefreshToken(profile.getRefreshToken()) .build(); + } - // Refresh the access token if it's expired - if (profile.getExpiresAt() == null || profile.getExpiresAt() < System.currentTimeMillis()) { - AuthorizationCodeCredentials credentials = api.authorizationCodeRefresh().build().execute(); - profile.setAccessToken(credentials.getAccessToken()); - profile.setRefreshToken(credentials.getRefreshToken()); - profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000)); - api.setAccessToken(credentials.getAccessToken()); - api.setRefreshToken(credentials.getRefreshToken()); - userService.saveUser(user); - log.info("Refreshed spotify access token for user {}", user.getName()); + /** + * Ensures the user has a valid Spotify access token. + * + * @param user the user to get the token for + */ + @SneakyThrows + public void ensureValidToken(SpotifyProfile profile, BatUser user) { + if (profile.getExpiresAt() == null || profile.getExpiresAt() > System.currentTimeMillis()) { + return; } - return api; + AuthorizationCodeCredentials credentials = spotifyApi.authorizationCodeRefresh().build().execute(); + profile.setAccessToken(credentials.getAccessToken()); + profile.setRefreshToken(credentials.getRefreshToken()); + profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000)); + userService.saveUser(user); } }