add skip spotify command and show song when pausing and resuming the song
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 37s
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 37s
This commit is contained in:
parent
df44ae90b9
commit
b0949d17e6
5
pom.xml
5
pom.xml
@ -85,6 +85,11 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.sentry</groupId>
|
||||
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
|
||||
<version>7.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Libraries -->
|
||||
<dependency>
|
||||
|
@ -99,6 +99,14 @@ public class LinkSubCommand extends BatSubCommand implements EventListener {
|
||||
return;
|
||||
}
|
||||
String code = codeMapping.getAsString();
|
||||
if (!spotifyService.isValidLinkCode(code)) {
|
||||
event.replyEmbeds(EmbedUtils.errorEmbed()
|
||||
.setDescription("%s The link code you provided is invalid.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||
.build())
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
spotifyService.linkAccount(user, code);
|
||||
event.replyEmbeds(EmbedUtils.successEmbed()
|
||||
.setDescription("%s You have linked your Spotify account!".formatted(Emojis.CHECK_MARK_EMOJI.getFormatted()))
|
||||
|
@ -15,6 +15,8 @@ import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||
import se.michaelthelin.spotify.model_objects.specification.Track;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
@ -46,8 +48,14 @@ public class PauseSubCommand extends BatSubCommand {
|
||||
}
|
||||
|
||||
boolean didPause = spotifyService.pausePlayback(user);
|
||||
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||
Track track = (Track) currentlyPlaying.getItem();
|
||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
||||
.setDescription(didPause ? ":pause_button: Paused the current track."
|
||||
.setDescription(didPause ? ":pause_button: Paused the track **[%s | %s](%s)**".formatted(
|
||||
track.getName(),
|
||||
track.getArtists()[0].getName(),
|
||||
"https://open.spotify.com/track/%s".formatted(track.getId())
|
||||
)
|
||||
: "%s The current track is already paused.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||
.build())
|
||||
.queue();
|
||||
|
@ -15,6 +15,8 @@ import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||
import se.michaelthelin.spotify.model_objects.specification.Track;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
@ -46,9 +48,16 @@ public class ResumeSubCommand extends BatSubCommand {
|
||||
}
|
||||
|
||||
boolean didPause = spotifyService.resumePlayback(user);
|
||||
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||
Track track = (Track) currentlyPlaying.getItem();
|
||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
||||
.setDescription(didPause ? "%s Resumed the current track.".formatted(Emojis.CHECK_MARK_EMOJI) :
|
||||
"%s The current track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||
.setDescription(didPause ? "%s Resumed the track **[%s | %s](%s)**".formatted(
|
||||
Emojis.CHECK_MARK_EMOJI,
|
||||
track.getName(),
|
||||
track.getArtists()[0].getName(),
|
||||
"https://open.spotify.com/track/%s".formatted(track.getId())
|
||||
)
|
||||
: "%s The current track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||
.build())
|
||||
.queue();
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
package cc.fascinated.bat.features.spotify.command;
|
||||
|
||||
import cc.fascinated.bat.Emojis;
|
||||
import cc.fascinated.bat.command.BatSubCommand;
|
||||
import cc.fascinated.bat.command.CommandInfo;
|
||||
import cc.fascinated.bat.common.EmbedUtils;
|
||||
import cc.fascinated.bat.features.spotify.SpotifyFeature;
|
||||
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;
|
||||
import lombok.NonNull;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||
import se.michaelthelin.spotify.model_objects.specification.Track;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@Component
|
||||
@CommandInfo(name = "skip", description = "Skip the current Spotify track")
|
||||
public class SkipSubCommand extends BatSubCommand {
|
||||
private static final Logger log = LoggerFactory.getLogger(SkipSubCommand.class);
|
||||
private final SpotifyService spotifyService;
|
||||
|
||||
@Autowired
|
||||
public SkipSubCommand(@NonNull SpotifyService spotifyService) {
|
||||
this.spotifyService = spotifyService;
|
||||
}
|
||||
|
||||
@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(SpotifyFeature.linkAccountEmbed()).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!spotifyService.hasTrackPlaying(user)) {
|
||||
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
||||
.setDescription("%s You need to be playing a track to skip a track.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||
.build())
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||
Track track = (Track) currentlyPlaying.getItem();
|
||||
String trackName = track.getName();
|
||||
|
||||
spotifyService.skipTrack(user);
|
||||
Track newTrack = getNewTrack(user, trackName);
|
||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
||||
.setDescription("""
|
||||
:track_next: Skipped the track: **[%s | %s](%s)**
|
||||
%s New Track: **[%s | %s](%s)**
|
||||
""".formatted(
|
||||
trackName,
|
||||
track.getArtists()[0].getName(),
|
||||
"https://open.spotify.com/track/" + track.getId(),
|
||||
Emojis.CHECK_MARK_EMOJI,
|
||||
newTrack.getName(),
|
||||
newTrack.getArtists()[0].getName(),
|
||||
"https://open.spotify.com/track/" + newTrack.getId()
|
||||
)).build())
|
||||
.queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next track that is playing
|
||||
*
|
||||
* @param user The user to get the track for
|
||||
* @param oldName The name of the old track
|
||||
* @return The new track
|
||||
*/
|
||||
public Track getNewTrack(BatUser user, String oldName) {
|
||||
int checks = 0;
|
||||
|
||||
try {
|
||||
Thread.sleep(150);
|
||||
while (checks < 10) {
|
||||
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||
Track track = (Track) currentlyPlaying.getItem();
|
||||
if (track.getName().equals(oldName)) {
|
||||
Thread.sleep(250);
|
||||
checks++;
|
||||
} else {
|
||||
log.info("Found new track {} in {} checks", track.getName(), checks);
|
||||
return (Track) currentlyPlaying.getItem();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -21,5 +21,6 @@ public class SpotifyCommand extends BatCommand {
|
||||
super.addSubCommand(context.getBean(PauseSubCommand.class));
|
||||
super.addSubCommand(context.getBean(ResumeSubCommand.class));
|
||||
super.addSubCommand(context.getBean(CurrentSubCommand.class));
|
||||
super.addSubCommand(context.getBean(SkipSubCommand.class));
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import net.jodah.expiringmap.ExpiringMap;
|
||||
import org.apache.hc.core5.http.ParseException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import se.michaelthelin.spotify.SpotifyApi;
|
||||
@ -17,6 +18,7 @@ import se.michaelthelin.spotify.exceptions.SpotifyWebApiException;
|
||||
import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
|
||||
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -77,6 +79,18 @@ public class SpotifyService {
|
||||
.build().execute().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playback for the user.
|
||||
*
|
||||
* @param user the user to start playback for
|
||||
* @return if the playback was started
|
||||
*/
|
||||
@SneakyThrows
|
||||
public boolean skipTrack(BatUser user) {
|
||||
getSpotifyApi(user).skipUsersPlaybackToNextTrack().build().execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently playing track for the user.
|
||||
*
|
||||
@ -145,25 +159,6 @@ public class SpotifyService {
|
||||
""".formatted(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void linkAccount(BatUser user, String key) {
|
||||
AuthorizationCodeCredentials credentials = accessToken.get(key);
|
||||
if (credentials == null) {
|
||||
return;
|
||||
}
|
||||
// Link the user's Spotify account
|
||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
||||
profile.setAccessToken(credentials.getAccessToken());
|
||||
profile.setRefreshToken(credentials.getRefreshToken());
|
||||
profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000));
|
||||
userService.saveUser(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new Spotify API instance.
|
||||
*
|
||||
@ -197,9 +192,40 @@ public class SpotifyService {
|
||||
profile.setAccessToken(credentials.getAccessToken());
|
||||
profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000));
|
||||
userService.saveUser(user);
|
||||
log.info("Refreshed Spotify token for user {}", user.getName());
|
||||
} catch (SpotifyWebApiException ex) {
|
||||
log.error("Failed to refresh Spotify token", ex);
|
||||
throw new SpotifyTokenRefreshException("Failed to refresh Spotify token", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void linkAccount(BatUser user, String key) {
|
||||
AuthorizationCodeCredentials credentials = accessToken.get(key);
|
||||
if (credentials == null) {
|
||||
return;
|
||||
}
|
||||
// Link the user's Spotify account
|
||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
||||
profile.setAccessToken(credentials.getAccessToken());
|
||||
profile.setRefreshToken(credentials.getRefreshToken());
|
||||
profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000));
|
||||
userService.saveUser(user);
|
||||
log.info("Linked Spotify account for user {}", user.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the link code is valid.
|
||||
*
|
||||
* @param code the code to check
|
||||
* @return if the code is valid
|
||||
*/
|
||||
public boolean isValidLinkCode(String code) {
|
||||
return accessToken.containsKey(code);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,12 @@
|
||||
discord:
|
||||
token: "oh my goodnesssssssssss"
|
||||
|
||||
# Sentry Configuration
|
||||
sentry:
|
||||
dsn: "CHANGE_ME"
|
||||
tracesSampleRate: 1.0
|
||||
environment: "development"
|
||||
|
||||
# Spotify Configuration
|
||||
spotify:
|
||||
redirect-uri: "http://localhost:8080/spotify/callback"
|
||||
|
Loading…
Reference in New Issue
Block a user