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>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.sentry</groupId>
|
||||||
|
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
|
||||||
|
<version>7.10.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Libraries -->
|
<!-- Libraries -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -99,6 +99,14 @@ public class LinkSubCommand extends BatSubCommand implements EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String code = codeMapping.getAsString();
|
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);
|
spotifyService.linkAccount(user, code);
|
||||||
event.replyEmbeds(EmbedUtils.successEmbed()
|
event.replyEmbeds(EmbedUtils.successEmbed()
|
||||||
.setDescription("%s You have linked your Spotify account!".formatted(Emojis.CHECK_MARK_EMOJI.getFormatted()))
|
.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 net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||||
|
import se.michaelthelin.spotify.model_objects.specification.Track;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
@ -46,8 +48,14 @@ public class PauseSubCommand extends BatSubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean didPause = spotifyService.pausePlayback(user);
|
boolean didPause = spotifyService.pausePlayback(user);
|
||||||
|
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||||
|
Track track = (Track) currentlyPlaying.getItem();
|
||||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
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))
|
: "%s The current track is already paused.".formatted(Emojis.CROSS_MARK_EMOJI))
|
||||||
.build())
|
.build())
|
||||||
.queue();
|
.queue();
|
||||||
|
@ -15,6 +15,8 @@ import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|||||||
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||||
|
import se.michaelthelin.spotify.model_objects.specification.Track;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
@ -46,9 +48,16 @@ public class ResumeSubCommand extends BatSubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean didPause = spotifyService.resumePlayback(user);
|
boolean didPause = spotifyService.resumePlayback(user);
|
||||||
|
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
||||||
|
Track track = (Track) currentlyPlaying.getItem();
|
||||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
||||||
.setDescription(didPause ? "%s Resumed the current track.".formatted(Emojis.CHECK_MARK_EMOJI) :
|
.setDescription(didPause ? "%s Resumed the track **[%s | %s](%s)**".formatted(
|
||||||
"%s The current track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI))
|
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())
|
.build())
|
||||||
.queue();
|
.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(PauseSubCommand.class));
|
||||||
super.addSubCommand(context.getBean(ResumeSubCommand.class));
|
super.addSubCommand(context.getBean(ResumeSubCommand.class));
|
||||||
super.addSubCommand(context.getBean(CurrentSubCommand.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.SneakyThrows;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import net.jodah.expiringmap.ExpiringMap;
|
import net.jodah.expiringmap.ExpiringMap;
|
||||||
|
import org.apache.hc.core5.http.ParseException;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import se.michaelthelin.spotify.SpotifyApi;
|
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.credentials.AuthorizationCodeCredentials;
|
||||||
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -77,6 +79,18 @@ public class SpotifyService {
|
|||||||
.build().execute().toString();
|
.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.
|
* Gets the currently playing track for the user.
|
||||||
*
|
*
|
||||||
@ -145,25 +159,6 @@ public class SpotifyService {
|
|||||||
""".formatted(key);
|
""".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.
|
* Gets a new Spotify API instance.
|
||||||
*
|
*
|
||||||
@ -197,9 +192,40 @@ public class SpotifyService {
|
|||||||
profile.setAccessToken(credentials.getAccessToken());
|
profile.setAccessToken(credentials.getAccessToken());
|
||||||
profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000));
|
profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000));
|
||||||
userService.saveUser(user);
|
userService.saveUser(user);
|
||||||
|
log.info("Refreshed Spotify token for user {}", user.getName());
|
||||||
} catch (SpotifyWebApiException ex) {
|
} catch (SpotifyWebApiException ex) {
|
||||||
log.error("Failed to refresh Spotify token", ex);
|
log.error("Failed to refresh Spotify token", ex);
|
||||||
throw new SpotifyTokenRefreshException("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:
|
discord:
|
||||||
token: "oh my goodnesssssssssss"
|
token: "oh my goodnesssssssssss"
|
||||||
|
|
||||||
|
# Sentry Configuration
|
||||||
|
sentry:
|
||||||
|
dsn: "CHANGE_ME"
|
||||||
|
tracesSampleRate: 1.0
|
||||||
|
environment: "development"
|
||||||
|
|
||||||
# Spotify Configuration
|
# Spotify Configuration
|
||||||
spotify:
|
spotify:
|
||||||
redirect-uri: "http://localhost:8080/spotify/callback"
|
redirect-uri: "http://localhost:8080/spotify/callback"
|
||||||
|
Loading…
Reference in New Issue
Block a user